blob: c42eb54f379a13c7202afc628a6e7e7ee9219044 [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{
Zhu Yi397ae122006-01-24 16:37:22 +08002775 u32 current_index = 0, previous_index;
James Ketrenos43f66a62005-03-25 12:31:53 -06002776 u32 watchdog = 0;
2777
2778 IPW_DEBUG_FW(">> : \n");
2779
2780 current_index = ipw_fw_dma_command_block_index(priv);
Zhu Yi397ae122006-01-24 16:37:22 +08002781 IPW_DEBUG_FW_INFO("sram_desc.last_cb_index:0x%08X\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);
Zhu Yi397ae122006-01-24 16:37:22 +08002786 previous_index = current_index;
James Ketrenos43f66a62005-03-25 12:31:53 -06002787 current_index = ipw_fw_dma_command_block_index(priv);
2788
Zhu Yi397ae122006-01-24 16:37:22 +08002789 if (previous_index < current_index) {
2790 watchdog = 0;
2791 continue;
2792 }
2793 if (++watchdog > 400) {
James Ketrenos43f66a62005-03-25 12:31:53 -06002794 IPW_DEBUG_FW_INFO("Timeout\n");
2795 ipw_fw_dma_dump_command_block(priv);
2796 ipw_fw_dma_abort(priv);
2797 return -1;
2798 }
2799 }
2800
2801 ipw_fw_dma_abort(priv);
2802
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002803 /*Disable the DMA in the CSR register */
James Ketrenosb095c382005-08-24 22:04:42 -05002804 ipw_set_bit(priv, IPW_RESET_REG,
2805 IPW_RESET_REG_MASTER_DISABLED | IPW_RESET_REG_STOP_MASTER);
James Ketrenos43f66a62005-03-25 12:31:53 -06002806
2807 IPW_DEBUG_FW("<< dmaWaitSync \n");
2808 return 0;
2809}
2810
Jeff Garzikbf794512005-07-31 13:07:26 -04002811static void ipw_remove_current_network(struct ipw_priv *priv)
James Ketrenos43f66a62005-03-25 12:31:53 -06002812{
2813 struct list_head *element, *safe;
Jeff Garzikbf794512005-07-31 13:07:26 -04002814 struct ieee80211_network *network = NULL;
James Ketrenosa613bff2005-08-24 21:43:11 -05002815 unsigned long flags;
2816
2817 spin_lock_irqsave(&priv->ieee->lock, flags);
James Ketrenos43f66a62005-03-25 12:31:53 -06002818 list_for_each_safe(element, safe, &priv->ieee->network_list) {
2819 network = list_entry(element, struct ieee80211_network, list);
2820 if (!memcmp(network->bssid, priv->bssid, ETH_ALEN)) {
2821 list_del(element);
Jeff Garzikbf794512005-07-31 13:07:26 -04002822 list_add_tail(&network->list,
James Ketrenos43f66a62005-03-25 12:31:53 -06002823 &priv->ieee->network_free_list);
2824 }
2825 }
James Ketrenosa613bff2005-08-24 21:43:11 -05002826 spin_unlock_irqrestore(&priv->ieee->lock, flags);
James Ketrenos43f66a62005-03-25 12:31:53 -06002827}
2828
2829/**
Jeff Garzikbf794512005-07-31 13:07:26 -04002830 * Check that card is still alive.
James Ketrenos43f66a62005-03-25 12:31:53 -06002831 * Reads debug register from domain0.
2832 * If card is present, pre-defined value should
2833 * be found there.
Jeff Garzikbf794512005-07-31 13:07:26 -04002834 *
James Ketrenos43f66a62005-03-25 12:31:53 -06002835 * @param priv
2836 * @return 1 if card is present, 0 otherwise
2837 */
2838static inline int ipw_alive(struct ipw_priv *priv)
2839{
2840 return ipw_read32(priv, 0x90) == 0xd55555d5;
2841}
2842
Zhu Yic7b6a672006-01-24 16:37:05 +08002843/* timeout in msec, attempted in 10-msec quanta */
Arjan van de Ven858119e2006-01-14 13:20:43 -08002844static int ipw_poll_bit(struct ipw_priv *priv, u32 addr, u32 mask,
James Ketrenos43f66a62005-03-25 12:31:53 -06002845 int timeout)
2846{
2847 int i = 0;
2848
2849 do {
Jeff Garzikbf794512005-07-31 13:07:26 -04002850 if ((ipw_read32(priv, addr) & mask) == mask)
James Ketrenos43f66a62005-03-25 12:31:53 -06002851 return i;
2852 mdelay(10);
2853 i += 10;
2854 } while (i < timeout);
Jeff Garzikbf794512005-07-31 13:07:26 -04002855
James Ketrenos43f66a62005-03-25 12:31:53 -06002856 return -ETIME;
2857}
2858
Jeff Garzikbf794512005-07-31 13:07:26 -04002859/* These functions load the firmware and micro code for the operation of
James Ketrenos43f66a62005-03-25 12:31:53 -06002860 * the ipw hardware. It assumes the buffer has all the bits for the
2861 * image and the caller is handling the memory allocation and clean up.
2862 */
2863
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002864static int ipw_stop_master(struct ipw_priv *priv)
James Ketrenos43f66a62005-03-25 12:31:53 -06002865{
2866 int rc;
Jeff Garzikbf794512005-07-31 13:07:26 -04002867
James Ketrenos43f66a62005-03-25 12:31:53 -06002868 IPW_DEBUG_TRACE(">> \n");
2869 /* stop master. typical delay - 0 */
James Ketrenosb095c382005-08-24 22:04:42 -05002870 ipw_set_bit(priv, IPW_RESET_REG, IPW_RESET_REG_STOP_MASTER);
James Ketrenos43f66a62005-03-25 12:31:53 -06002871
Zhu Yic7b6a672006-01-24 16:37:05 +08002872 /* timeout is in msec, polled in 10-msec quanta */
James Ketrenosb095c382005-08-24 22:04:42 -05002873 rc = ipw_poll_bit(priv, IPW_RESET_REG,
2874 IPW_RESET_REG_MASTER_DISABLED, 100);
James Ketrenos43f66a62005-03-25 12:31:53 -06002875 if (rc < 0) {
Zhu Yic7b6a672006-01-24 16:37:05 +08002876 IPW_ERROR("wait for stop master failed after 100ms\n");
James Ketrenos43f66a62005-03-25 12:31:53 -06002877 return -1;
2878 }
2879
2880 IPW_DEBUG_INFO("stop master %dms\n", rc);
2881
2882 return rc;
2883}
2884
2885static void ipw_arc_release(struct ipw_priv *priv)
2886{
2887 IPW_DEBUG_TRACE(">> \n");
2888 mdelay(5);
2889
James Ketrenosb095c382005-08-24 22:04:42 -05002890 ipw_clear_bit(priv, IPW_RESET_REG, CBD_RESET_REG_PRINCETON_RESET);
James Ketrenos43f66a62005-03-25 12:31:53 -06002891
2892 /* no one knows timing, for safety add some delay */
2893 mdelay(5);
2894}
2895
2896struct fw_header {
2897 u32 version;
2898 u32 mode;
2899};
2900
2901struct fw_chunk {
2902 u32 address;
2903 u32 length;
2904};
2905
2906#define IPW_FW_MAJOR_VERSION 2
James Ketrenos817153762005-08-25 01:37:28 -05002907#define IPW_FW_MINOR_VERSION 4
James Ketrenos43f66a62005-03-25 12:31:53 -06002908
2909#define IPW_FW_MINOR(x) ((x & 0xff) >> 8)
2910#define IPW_FW_MAJOR(x) (x & 0xff)
2911
James Ketrenosafbf30a2005-08-25 00:05:33 -05002912#define IPW_FW_VERSION ((IPW_FW_MINOR_VERSION << 8) | IPW_FW_MAJOR_VERSION)
James Ketrenos43f66a62005-03-25 12:31:53 -06002913
2914#define IPW_FW_PREFIX "ipw-" __stringify(IPW_FW_MAJOR_VERSION) \
2915"." __stringify(IPW_FW_MINOR_VERSION) "-"
2916
2917#if IPW_FW_MAJOR_VERSION >= 2 && IPW_FW_MINOR_VERSION > 0
2918#define IPW_FW_NAME(x) IPW_FW_PREFIX "" x ".fw"
2919#else
2920#define IPW_FW_NAME(x) "ipw2200_" x ".fw"
2921#endif
2922
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002923static int ipw_load_ucode(struct ipw_priv *priv, u8 * data, size_t len)
James Ketrenos43f66a62005-03-25 12:31:53 -06002924{
2925 int rc = 0, i, addr;
2926 u8 cr = 0;
2927 u16 *image;
2928
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002929 image = (u16 *) data;
Jeff Garzikbf794512005-07-31 13:07:26 -04002930
James Ketrenos43f66a62005-03-25 12:31:53 -06002931 IPW_DEBUG_TRACE(">> \n");
2932
2933 rc = ipw_stop_master(priv);
2934
2935 if (rc < 0)
2936 return rc;
Jeff Garzikbf794512005-07-31 13:07:26 -04002937
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002938// spin_lock_irqsave(&priv->lock, flags);
Jeff Garzikbf794512005-07-31 13:07:26 -04002939
James Ketrenosb095c382005-08-24 22:04:42 -05002940 for (addr = IPW_SHARED_LOWER_BOUND;
2941 addr < IPW_REGISTER_DOMAIN1_END; addr += 4) {
James Ketrenos43f66a62005-03-25 12:31:53 -06002942 ipw_write32(priv, addr, 0);
2943 }
2944
2945 /* no ucode (yet) */
2946 memset(&priv->dino_alive, 0, sizeof(priv->dino_alive));
2947 /* destroy DMA queues */
2948 /* reset sequence */
2949
James Ketrenosb095c382005-08-24 22:04:42 -05002950 ipw_write_reg32(priv, IPW_MEM_HALT_AND_RESET, IPW_BIT_HALT_RESET_ON);
James Ketrenos43f66a62005-03-25 12:31:53 -06002951 ipw_arc_release(priv);
James Ketrenosb095c382005-08-24 22:04:42 -05002952 ipw_write_reg32(priv, IPW_MEM_HALT_AND_RESET, IPW_BIT_HALT_RESET_OFF);
James Ketrenos43f66a62005-03-25 12:31:53 -06002953 mdelay(1);
2954
2955 /* reset PHY */
James Ketrenosb095c382005-08-24 22:04:42 -05002956 ipw_write_reg32(priv, IPW_INTERNAL_CMD_EVENT, IPW_BASEBAND_POWER_DOWN);
James Ketrenos43f66a62005-03-25 12:31:53 -06002957 mdelay(1);
Jeff Garzikbf794512005-07-31 13:07:26 -04002958
James Ketrenosb095c382005-08-24 22:04:42 -05002959 ipw_write_reg32(priv, IPW_INTERNAL_CMD_EVENT, 0);
James Ketrenos43f66a62005-03-25 12:31:53 -06002960 mdelay(1);
Jeff Garzikbf794512005-07-31 13:07:26 -04002961
James Ketrenos43f66a62005-03-25 12:31:53 -06002962 /* enable ucode store */
Zhu Yic8fe6672006-01-24 16:36:36 +08002963 ipw_write_reg8(priv, IPW_BASEBAND_CONTROL_STATUS, 0x0);
2964 ipw_write_reg8(priv, IPW_BASEBAND_CONTROL_STATUS, DINO_ENABLE_CS);
James Ketrenos43f66a62005-03-25 12:31:53 -06002965 mdelay(1);
2966
2967 /* write ucode */
2968 /**
2969 * @bug
2970 * Do NOT set indirect address register once and then
2971 * store data to indirect data register in the loop.
2972 * It seems very reasonable, but in this case DINO do not
2973 * accept ucode. It is essential to set address each time.
2974 */
2975 /* load new ipw uCode */
2976 for (i = 0; i < len / 2; i++)
James Ketrenosb095c382005-08-24 22:04:42 -05002977 ipw_write_reg16(priv, IPW_BASEBAND_CONTROL_STORE,
James Ketrenosa613bff2005-08-24 21:43:11 -05002978 cpu_to_le16(image[i]));
James Ketrenos43f66a62005-03-25 12:31:53 -06002979
James Ketrenos43f66a62005-03-25 12:31:53 -06002980 /* enable DINO */
James Ketrenosb095c382005-08-24 22:04:42 -05002981 ipw_write_reg8(priv, IPW_BASEBAND_CONTROL_STATUS, 0);
2982 ipw_write_reg8(priv, IPW_BASEBAND_CONTROL_STATUS, DINO_ENABLE_SYSTEM);
James Ketrenos43f66a62005-03-25 12:31:53 -06002983
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002984 /* this is where the igx / win driver deveates from the VAP driver. */
James Ketrenos43f66a62005-03-25 12:31:53 -06002985
2986 /* wait for alive response */
2987 for (i = 0; i < 100; i++) {
2988 /* poll for incoming data */
James Ketrenosb095c382005-08-24 22:04:42 -05002989 cr = ipw_read_reg8(priv, IPW_BASEBAND_CONTROL_STATUS);
James Ketrenos43f66a62005-03-25 12:31:53 -06002990 if (cr & DINO_RXFIFO_DATA)
2991 break;
2992 mdelay(1);
2993 }
2994
2995 if (cr & DINO_RXFIFO_DATA) {
2996 /* alive_command_responce size is NOT multiple of 4 */
2997 u32 response_buffer[(sizeof(priv->dino_alive) + 3) / 4];
Jeff Garzikbf794512005-07-31 13:07:26 -04002998
2999 for (i = 0; i < ARRAY_SIZE(response_buffer); i++)
James Ketrenos43f66a62005-03-25 12:31:53 -06003000 response_buffer[i] =
James Ketrenosa613bff2005-08-24 21:43:11 -05003001 le32_to_cpu(ipw_read_reg32(priv,
James Ketrenosb095c382005-08-24 22:04:42 -05003002 IPW_BASEBAND_RX_FIFO_READ));
James Ketrenos43f66a62005-03-25 12:31:53 -06003003 memcpy(&priv->dino_alive, response_buffer,
3004 sizeof(priv->dino_alive));
3005 if (priv->dino_alive.alive_command == 1
3006 && priv->dino_alive.ucode_valid == 1) {
3007 rc = 0;
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003008 IPW_DEBUG_INFO
3009 ("Microcode OK, rev. %d (0x%x) dev. %d (0x%x) "
3010 "of %02d/%02d/%02d %02d:%02d\n",
3011 priv->dino_alive.software_revision,
3012 priv->dino_alive.software_revision,
3013 priv->dino_alive.device_identifier,
3014 priv->dino_alive.device_identifier,
3015 priv->dino_alive.time_stamp[0],
3016 priv->dino_alive.time_stamp[1],
3017 priv->dino_alive.time_stamp[2],
3018 priv->dino_alive.time_stamp[3],
3019 priv->dino_alive.time_stamp[4]);
James Ketrenos43f66a62005-03-25 12:31:53 -06003020 } else {
3021 IPW_DEBUG_INFO("Microcode is not alive\n");
3022 rc = -EINVAL;
3023 }
3024 } else {
3025 IPW_DEBUG_INFO("No alive response from DINO\n");
3026 rc = -ETIME;
3027 }
3028
3029 /* disable DINO, otherwise for some reason
3030 firmware have problem getting alive resp. */
James Ketrenosb095c382005-08-24 22:04:42 -05003031 ipw_write_reg8(priv, IPW_BASEBAND_CONTROL_STATUS, 0);
James Ketrenos43f66a62005-03-25 12:31:53 -06003032
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003033// spin_unlock_irqrestore(&priv->lock, flags);
James Ketrenos43f66a62005-03-25 12:31:53 -06003034
3035 return rc;
3036}
3037
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003038static int ipw_load_firmware(struct ipw_priv *priv, u8 * data, size_t len)
James Ketrenos43f66a62005-03-25 12:31:53 -06003039{
3040 int rc = -1;
3041 int offset = 0;
3042 struct fw_chunk *chunk;
3043 dma_addr_t shared_phys;
3044 u8 *shared_virt;
3045
3046 IPW_DEBUG_TRACE("<< : \n");
3047 shared_virt = pci_alloc_consistent(priv->pci_dev, len, &shared_phys);
3048
3049 if (!shared_virt)
3050 return -ENOMEM;
3051
3052 memmove(shared_virt, data, len);
3053
3054 /* Start the Dma */
3055 rc = ipw_fw_dma_enable(priv);
3056
3057 if (priv->sram_desc.last_cb_index > 0) {
3058 /* the DMA is already ready this would be a bug. */
3059 BUG();
3060 goto out;
3061 }
3062
3063 do {
3064 chunk = (struct fw_chunk *)(data + offset);
3065 offset += sizeof(struct fw_chunk);
3066 /* build DMA packet and queue up for sending */
Jeff Garzikbf794512005-07-31 13:07:26 -04003067 /* dma to chunk->address, the chunk->length bytes from data +
James Ketrenos43f66a62005-03-25 12:31:53 -06003068 * offeset*/
3069 /* Dma loading */
3070 rc = ipw_fw_dma_add_buffer(priv, shared_phys + offset,
James Ketrenosa613bff2005-08-24 21:43:11 -05003071 le32_to_cpu(chunk->address),
3072 le32_to_cpu(chunk->length));
James Ketrenos43f66a62005-03-25 12:31:53 -06003073 if (rc) {
3074 IPW_DEBUG_INFO("dmaAddBuffer Failed\n");
3075 goto out;
3076 }
Jeff Garzikbf794512005-07-31 13:07:26 -04003077
James Ketrenosa613bff2005-08-24 21:43:11 -05003078 offset += le32_to_cpu(chunk->length);
James Ketrenos43f66a62005-03-25 12:31:53 -06003079 } while (offset < len);
3080
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003081 /* Run the DMA and wait for the answer */
James Ketrenos43f66a62005-03-25 12:31:53 -06003082 rc = ipw_fw_dma_kick(priv);
3083 if (rc) {
3084 IPW_ERROR("dmaKick Failed\n");
3085 goto out;
3086 }
3087
3088 rc = ipw_fw_dma_wait(priv);
3089 if (rc) {
3090 IPW_ERROR("dmaWaitSync Failed\n");
3091 goto out;
3092 }
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003093 out:
3094 pci_free_consistent(priv->pci_dev, len, shared_virt, shared_phys);
James Ketrenos43f66a62005-03-25 12:31:53 -06003095 return rc;
3096}
3097
3098/* stop nic */
3099static int ipw_stop_nic(struct ipw_priv *priv)
3100{
3101 int rc = 0;
3102
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003103 /* stop */
James Ketrenosb095c382005-08-24 22:04:42 -05003104 ipw_write32(priv, IPW_RESET_REG, IPW_RESET_REG_STOP_MASTER);
Jeff Garzikbf794512005-07-31 13:07:26 -04003105
James Ketrenosb095c382005-08-24 22:04:42 -05003106 rc = ipw_poll_bit(priv, IPW_RESET_REG,
3107 IPW_RESET_REG_MASTER_DISABLED, 500);
James Ketrenos43f66a62005-03-25 12:31:53 -06003108 if (rc < 0) {
Zhu Yic7b6a672006-01-24 16:37:05 +08003109 IPW_ERROR("wait for reg master disabled failed after 500ms\n");
James Ketrenos43f66a62005-03-25 12:31:53 -06003110 return rc;
Jeff Garzikbf794512005-07-31 13:07:26 -04003111 }
James Ketrenos43f66a62005-03-25 12:31:53 -06003112
James Ketrenosb095c382005-08-24 22:04:42 -05003113 ipw_set_bit(priv, IPW_RESET_REG, CBD_RESET_REG_PRINCETON_RESET);
Jeff Garzikbf794512005-07-31 13:07:26 -04003114
James Ketrenos43f66a62005-03-25 12:31:53 -06003115 return rc;
3116}
3117
3118static void ipw_start_nic(struct ipw_priv *priv)
3119{
3120 IPW_DEBUG_TRACE(">>\n");
3121
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003122 /* prvHwStartNic release ARC */
James Ketrenosb095c382005-08-24 22:04:42 -05003123 ipw_clear_bit(priv, IPW_RESET_REG,
3124 IPW_RESET_REG_MASTER_DISABLED |
3125 IPW_RESET_REG_STOP_MASTER |
James Ketrenos43f66a62005-03-25 12:31:53 -06003126 CBD_RESET_REG_PRINCETON_RESET);
Jeff Garzikbf794512005-07-31 13:07:26 -04003127
James Ketrenos43f66a62005-03-25 12:31:53 -06003128 /* enable power management */
James Ketrenosb095c382005-08-24 22:04:42 -05003129 ipw_set_bit(priv, IPW_GP_CNTRL_RW,
3130 IPW_GP_CNTRL_BIT_HOST_ALLOWS_STANDBY);
James Ketrenos43f66a62005-03-25 12:31:53 -06003131
3132 IPW_DEBUG_TRACE("<<\n");
3133}
Jeff Garzikbf794512005-07-31 13:07:26 -04003134
James Ketrenos43f66a62005-03-25 12:31:53 -06003135static int ipw_init_nic(struct ipw_priv *priv)
3136{
3137 int rc;
3138
3139 IPW_DEBUG_TRACE(">>\n");
Jeff Garzikbf794512005-07-31 13:07:26 -04003140 /* reset */
James Ketrenos43f66a62005-03-25 12:31:53 -06003141 /*prvHwInitNic */
3142 /* set "initialization complete" bit to move adapter to D0 state */
James Ketrenosb095c382005-08-24 22:04:42 -05003143 ipw_set_bit(priv, IPW_GP_CNTRL_RW, IPW_GP_CNTRL_BIT_INIT_DONE);
James Ketrenos43f66a62005-03-25 12:31:53 -06003144
3145 /* low-level PLL activation */
James Ketrenosb095c382005-08-24 22:04:42 -05003146 ipw_write32(priv, IPW_READ_INT_REGISTER,
3147 IPW_BIT_INT_HOST_SRAM_READ_INT_REGISTER);
James Ketrenos43f66a62005-03-25 12:31:53 -06003148
3149 /* wait for clock stabilization */
James Ketrenosb095c382005-08-24 22:04:42 -05003150 rc = ipw_poll_bit(priv, IPW_GP_CNTRL_RW,
3151 IPW_GP_CNTRL_BIT_CLOCK_READY, 250);
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003152 if (rc < 0)
James Ketrenos43f66a62005-03-25 12:31:53 -06003153 IPW_DEBUG_INFO("FAILED wait for clock stablization\n");
3154
3155 /* assert SW reset */
James Ketrenosb095c382005-08-24 22:04:42 -05003156 ipw_set_bit(priv, IPW_RESET_REG, IPW_RESET_REG_SW_RESET);
James Ketrenos43f66a62005-03-25 12:31:53 -06003157
3158 udelay(10);
3159
3160 /* set "initialization complete" bit to move adapter to D0 state */
James Ketrenosb095c382005-08-24 22:04:42 -05003161 ipw_set_bit(priv, IPW_GP_CNTRL_RW, IPW_GP_CNTRL_BIT_INIT_DONE);
James Ketrenos43f66a62005-03-25 12:31:53 -06003162
3163 IPW_DEBUG_TRACE(">>\n");
3164 return 0;
3165}
3166
Jeff Garzikbf794512005-07-31 13:07:26 -04003167/* Call this function from process context, it will sleep in request_firmware.
James Ketrenos43f66a62005-03-25 12:31:53 -06003168 * Probe is an ok place to call this from.
3169 */
3170static int ipw_reset_nic(struct ipw_priv *priv)
3171{
3172 int rc = 0;
James Ketrenosa613bff2005-08-24 21:43:11 -05003173 unsigned long flags;
James Ketrenos43f66a62005-03-25 12:31:53 -06003174
3175 IPW_DEBUG_TRACE(">>\n");
Jeff Garzikbf794512005-07-31 13:07:26 -04003176
James Ketrenos43f66a62005-03-25 12:31:53 -06003177 rc = ipw_init_nic(priv);
Jeff Garzikbf794512005-07-31 13:07:26 -04003178
James Ketrenosa613bff2005-08-24 21:43:11 -05003179 spin_lock_irqsave(&priv->lock, flags);
James Ketrenos43f66a62005-03-25 12:31:53 -06003180 /* Clear the 'host command active' bit... */
3181 priv->status &= ~STATUS_HCMD_ACTIVE;
3182 wake_up_interruptible(&priv->wait_command_queue);
James Ketrenosafbf30a2005-08-25 00:05:33 -05003183 priv->status &= ~(STATUS_SCANNING | STATUS_SCAN_ABORTING);
3184 wake_up_interruptible(&priv->wait_state);
James Ketrenosa613bff2005-08-24 21:43:11 -05003185 spin_unlock_irqrestore(&priv->lock, flags);
James Ketrenos43f66a62005-03-25 12:31:53 -06003186
3187 IPW_DEBUG_TRACE("<<\n");
3188 return rc;
Jeff Garzikbf794512005-07-31 13:07:26 -04003189}
James Ketrenos43f66a62005-03-25 12:31:53 -06003190
Jeff Garzikbf794512005-07-31 13:07:26 -04003191static int ipw_get_fw(struct ipw_priv *priv,
James Ketrenos43f66a62005-03-25 12:31:53 -06003192 const struct firmware **fw, const char *name)
3193{
3194 struct fw_header *header;
3195 int rc;
3196
3197 /* ask firmware_class module to get the boot firmware off disk */
3198 rc = request_firmware(fw, name, &priv->pci_dev->dev);
3199 if (rc < 0) {
3200 IPW_ERROR("%s load failed: Reason %d\n", name, rc);
3201 return rc;
Jeff Garzikbf794512005-07-31 13:07:26 -04003202 }
James Ketrenos43f66a62005-03-25 12:31:53 -06003203
3204 header = (struct fw_header *)(*fw)->data;
James Ketrenosa613bff2005-08-24 21:43:11 -05003205 if (IPW_FW_MAJOR(le32_to_cpu(header->version)) != IPW_FW_MAJOR_VERSION) {
James Ketrenos43f66a62005-03-25 12:31:53 -06003206 IPW_ERROR("'%s' firmware version not compatible (%d != %d)\n",
3207 name,
James Ketrenosa613bff2005-08-24 21:43:11 -05003208 IPW_FW_MAJOR(le32_to_cpu(header->version)),
3209 IPW_FW_MAJOR_VERSION);
James Ketrenos43f66a62005-03-25 12:31:53 -06003210 return -EINVAL;
3211 }
3212
Jiri Bencaaa4d302005-06-07 14:58:41 +02003213 IPW_DEBUG_INFO("Loading firmware '%s' file v%d.%d (%zd bytes)\n",
James Ketrenos43f66a62005-03-25 12:31:53 -06003214 name,
James Ketrenosa613bff2005-08-24 21:43:11 -05003215 IPW_FW_MAJOR(le32_to_cpu(header->version)),
3216 IPW_FW_MINOR(le32_to_cpu(header->version)),
James Ketrenos43f66a62005-03-25 12:31:53 -06003217 (*fw)->size - sizeof(struct fw_header));
3218 return 0;
3219}
3220
James Ketrenosb095c382005-08-24 22:04:42 -05003221#define IPW_RX_BUF_SIZE (3000)
James Ketrenos43f66a62005-03-25 12:31:53 -06003222
Arjan van de Ven858119e2006-01-14 13:20:43 -08003223static void ipw_rx_queue_reset(struct ipw_priv *priv,
James Ketrenos43f66a62005-03-25 12:31:53 -06003224 struct ipw_rx_queue *rxq)
3225{
3226 unsigned long flags;
3227 int i;
3228
3229 spin_lock_irqsave(&rxq->lock, flags);
3230
3231 INIT_LIST_HEAD(&rxq->rx_free);
3232 INIT_LIST_HEAD(&rxq->rx_used);
3233
3234 /* Fill the rx_used queue with _all_ of the Rx buffers */
3235 for (i = 0; i < RX_FREE_BUFFERS + RX_QUEUE_SIZE; i++) {
3236 /* In the reset function, these buffers may have been allocated
3237 * to an SKB, so we need to unmap and free potential storage */
3238 if (rxq->pool[i].skb != NULL) {
3239 pci_unmap_single(priv->pci_dev, rxq->pool[i].dma_addr,
James Ketrenosb095c382005-08-24 22:04:42 -05003240 IPW_RX_BUF_SIZE, PCI_DMA_FROMDEVICE);
James Ketrenos43f66a62005-03-25 12:31:53 -06003241 dev_kfree_skb(rxq->pool[i].skb);
James Ketrenosa613bff2005-08-24 21:43:11 -05003242 rxq->pool[i].skb = NULL;
James Ketrenos43f66a62005-03-25 12:31:53 -06003243 }
3244 list_add_tail(&rxq->pool[i].list, &rxq->rx_used);
3245 }
Jeff Garzikbf794512005-07-31 13:07:26 -04003246
James Ketrenos43f66a62005-03-25 12:31:53 -06003247 /* Set us so that we have processed and used all buffers, but have
3248 * not restocked the Rx queue with fresh buffers */
3249 rxq->read = rxq->write = 0;
3250 rxq->processed = RX_QUEUE_SIZE - 1;
3251 rxq->free_count = 0;
3252 spin_unlock_irqrestore(&rxq->lock, flags);
3253}
3254
3255#ifdef CONFIG_PM
3256static int fw_loaded = 0;
3257static const struct firmware *bootfw = NULL;
3258static const struct firmware *firmware = NULL;
3259static const struct firmware *ucode = NULL;
James Ketrenosafbf30a2005-08-25 00:05:33 -05003260
3261static void free_firmware(void)
3262{
3263 if (fw_loaded) {
3264 release_firmware(bootfw);
3265 release_firmware(ucode);
3266 release_firmware(firmware);
3267 bootfw = ucode = firmware = NULL;
3268 fw_loaded = 0;
3269 }
3270}
3271#else
3272#define free_firmware() do {} while (0)
James Ketrenos43f66a62005-03-25 12:31:53 -06003273#endif
3274
3275static int ipw_load(struct ipw_priv *priv)
3276{
3277#ifndef CONFIG_PM
3278 const struct firmware *bootfw = NULL;
3279 const struct firmware *firmware = NULL;
3280 const struct firmware *ucode = NULL;
3281#endif
Zhu Yi397ae122006-01-24 16:37:22 +08003282 char *ucode_name;
3283 char *fw_name;
James Ketrenos43f66a62005-03-25 12:31:53 -06003284 int rc = 0, retries = 3;
3285
Zhu Yi397ae122006-01-24 16:37:22 +08003286 switch (priv->ieee->iw_mode) {
3287 case IW_MODE_ADHOC:
3288 ucode_name = IPW_FW_NAME("ibss_ucode");
3289 fw_name = IPW_FW_NAME("ibss");
3290 break;
James Ketrenosb095c382005-08-24 22:04:42 -05003291#ifdef CONFIG_IPW2200_MONITOR
Zhu Yi397ae122006-01-24 16:37:22 +08003292 case IW_MODE_MONITOR:
3293 ucode_name = IPW_FW_NAME("sniffer_ucode");
3294 fw_name = IPW_FW_NAME("sniffer");
3295 break;
James Ketrenos43f66a62005-03-25 12:31:53 -06003296#endif
Zhu Yi397ae122006-01-24 16:37:22 +08003297 case IW_MODE_INFRA:
3298 ucode_name = IPW_FW_NAME("bss_ucode");
3299 fw_name = IPW_FW_NAME("bss");
3300 break;
3301 default:
3302 rc = -EINVAL;
James Ketrenos43f66a62005-03-25 12:31:53 -06003303 }
Zhu Yi397ae122006-01-24 16:37:22 +08003304
3305 if (rc < 0)
3306 goto error;
James Ketrenos43f66a62005-03-25 12:31:53 -06003307
3308 if (!priv->rxq)
3309 priv->rxq = ipw_rx_queue_alloc(priv);
3310 else
3311 ipw_rx_queue_reset(priv, priv->rxq);
3312 if (!priv->rxq) {
3313 IPW_ERROR("Unable to initialize Rx queue\n");
3314 goto error;
3315 }
3316
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003317 retry:
James Ketrenos43f66a62005-03-25 12:31:53 -06003318 /* Ensure interrupts are disabled */
James Ketrenosb095c382005-08-24 22:04:42 -05003319 ipw_write32(priv, IPW_INTA_MASK_R, ~IPW_INTA_MASK_ALL);
James Ketrenos43f66a62005-03-25 12:31:53 -06003320 priv->status &= ~STATUS_INT_ENABLED;
3321
3322 /* ack pending interrupts */
James Ketrenosb095c382005-08-24 22:04:42 -05003323 ipw_write32(priv, IPW_INTA_RW, IPW_INTA_MASK_ALL);
Jeff Garzikbf794512005-07-31 13:07:26 -04003324
James Ketrenos43f66a62005-03-25 12:31:53 -06003325 ipw_stop_nic(priv);
3326
3327 rc = ipw_reset_nic(priv);
Zhu Yi397ae122006-01-24 16:37:22 +08003328 if (rc < 0) {
James Ketrenos43f66a62005-03-25 12:31:53 -06003329 IPW_ERROR("Unable to reset NIC\n");
3330 goto error;
3331 }
3332
James Ketrenosb095c382005-08-24 22:04:42 -05003333 ipw_zero_memory(priv, IPW_NIC_SRAM_LOWER_BOUND,
3334 IPW_NIC_SRAM_UPPER_BOUND - IPW_NIC_SRAM_LOWER_BOUND);
James Ketrenos43f66a62005-03-25 12:31:53 -06003335
Zhu Yi397ae122006-01-24 16:37:22 +08003336#ifdef CONFIG_PM
3337 if (!fw_loaded) {
3338#endif
3339 rc = ipw_get_fw(priv, &bootfw, IPW_FW_NAME("boot"));
3340 if (rc < 0)
3341 goto error;
3342#ifdef CONFIG_PM
3343 }
3344#endif
James Ketrenos43f66a62005-03-25 12:31:53 -06003345 /* DMA the initial boot firmware into the device */
Jeff Garzikbf794512005-07-31 13:07:26 -04003346 rc = ipw_load_firmware(priv, bootfw->data + sizeof(struct fw_header),
James Ketrenos43f66a62005-03-25 12:31:53 -06003347 bootfw->size - sizeof(struct fw_header));
3348 if (rc < 0) {
Peter Jonesa4f6bbb2005-08-26 00:33:34 -05003349 IPW_ERROR("Unable to load boot firmware: %d\n", rc);
James Ketrenos43f66a62005-03-25 12:31:53 -06003350 goto error;
3351 }
3352
3353 /* kick start the device */
3354 ipw_start_nic(priv);
3355
Zhu Yic7b6a672006-01-24 16:37:05 +08003356 /* wait for the device to finish its initial startup sequence */
James Ketrenosb095c382005-08-24 22:04:42 -05003357 rc = ipw_poll_bit(priv, IPW_INTA_RW,
3358 IPW_INTA_BIT_FW_INITIALIZATION_DONE, 500);
James Ketrenos43f66a62005-03-25 12:31:53 -06003359 if (rc < 0) {
3360 IPW_ERROR("device failed to boot initial fw image\n");
3361 goto error;
3362 }
3363 IPW_DEBUG_INFO("initial device response after %dms\n", rc);
3364
Jeff Garzikbf794512005-07-31 13:07:26 -04003365 /* ack fw init done interrupt */
James Ketrenosb095c382005-08-24 22:04:42 -05003366 ipw_write32(priv, IPW_INTA_RW, IPW_INTA_BIT_FW_INITIALIZATION_DONE);
James Ketrenos43f66a62005-03-25 12:31:53 -06003367
Zhu Yi397ae122006-01-24 16:37:22 +08003368#ifdef CONFIG_PM
3369 if (!fw_loaded) {
3370#endif
3371 rc = ipw_get_fw(priv, &ucode, ucode_name);
3372 if (rc < 0)
3373 goto error;
3374#ifdef CONFIG_PM
3375 }
3376#endif
3377
James Ketrenos43f66a62005-03-25 12:31:53 -06003378 /* DMA the ucode into the device */
Jeff Garzikbf794512005-07-31 13:07:26 -04003379 rc = ipw_load_ucode(priv, ucode->data + sizeof(struct fw_header),
James Ketrenos43f66a62005-03-25 12:31:53 -06003380 ucode->size - sizeof(struct fw_header));
3381 if (rc < 0) {
Peter Jonesa4f6bbb2005-08-26 00:33:34 -05003382 IPW_ERROR("Unable to load ucode: %d\n", rc);
James Ketrenos43f66a62005-03-25 12:31:53 -06003383 goto error;
3384 }
Jeff Garzikbf794512005-07-31 13:07:26 -04003385
James Ketrenos43f66a62005-03-25 12:31:53 -06003386 /* stop nic */
3387 ipw_stop_nic(priv);
3388
Zhu Yi397ae122006-01-24 16:37:22 +08003389#ifdef CONFIG_PM
3390 if (!fw_loaded) {
3391#endif
3392 rc = ipw_get_fw(priv, &firmware, fw_name);
3393 if (rc < 0)
3394 goto error;
3395#ifdef CONFIG_PM
3396 }
3397#endif
3398
James Ketrenos43f66a62005-03-25 12:31:53 -06003399 /* DMA bss firmware into the device */
Jeff Garzikbf794512005-07-31 13:07:26 -04003400 rc = ipw_load_firmware(priv, firmware->data +
3401 sizeof(struct fw_header),
James Ketrenos43f66a62005-03-25 12:31:53 -06003402 firmware->size - sizeof(struct fw_header));
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003403 if (rc < 0) {
Peter Jonesa4f6bbb2005-08-26 00:33:34 -05003404 IPW_ERROR("Unable to load firmware: %d\n", rc);
James Ketrenos43f66a62005-03-25 12:31:53 -06003405 goto error;
3406 }
3407
Zhu Yi397ae122006-01-24 16:37:22 +08003408#ifdef CONFIG_PM
3409 fw_loaded = 1;
3410#endif
3411
James Ketrenos43f66a62005-03-25 12:31:53 -06003412 ipw_write32(priv, IPW_EEPROM_LOAD_DISABLE, 0);
3413
3414 rc = ipw_queue_reset(priv);
Zhu Yi397ae122006-01-24 16:37:22 +08003415 if (rc < 0) {
James Ketrenos43f66a62005-03-25 12:31:53 -06003416 IPW_ERROR("Unable to initialize queues\n");
3417 goto error;
3418 }
3419
3420 /* Ensure interrupts are disabled */
James Ketrenosb095c382005-08-24 22:04:42 -05003421 ipw_write32(priv, IPW_INTA_MASK_R, ~IPW_INTA_MASK_ALL);
James Ketrenosc848d0a2005-08-24 21:56:24 -05003422 /* ack pending interrupts */
James Ketrenosb095c382005-08-24 22:04:42 -05003423 ipw_write32(priv, IPW_INTA_RW, IPW_INTA_MASK_ALL);
Jeff Garzikbf794512005-07-31 13:07:26 -04003424
James Ketrenos43f66a62005-03-25 12:31:53 -06003425 /* kick start the device */
3426 ipw_start_nic(priv);
3427
James Ketrenosb095c382005-08-24 22:04:42 -05003428 if (ipw_read32(priv, IPW_INTA_RW) & IPW_INTA_BIT_PARITY_ERROR) {
James Ketrenos43f66a62005-03-25 12:31:53 -06003429 if (retries > 0) {
3430 IPW_WARNING("Parity error. Retrying init.\n");
3431 retries--;
3432 goto retry;
3433 }
3434
3435 IPW_ERROR("TODO: Handle parity error -- schedule restart?\n");
3436 rc = -EIO;
3437 goto error;
3438 }
3439
3440 /* wait for the device */
James Ketrenosb095c382005-08-24 22:04:42 -05003441 rc = ipw_poll_bit(priv, IPW_INTA_RW,
3442 IPW_INTA_BIT_FW_INITIALIZATION_DONE, 500);
James Ketrenos43f66a62005-03-25 12:31:53 -06003443 if (rc < 0) {
Zhu Yic7b6a672006-01-24 16:37:05 +08003444 IPW_ERROR("device failed to start within 500ms\n");
James Ketrenos43f66a62005-03-25 12:31:53 -06003445 goto error;
3446 }
3447 IPW_DEBUG_INFO("device response after %dms\n", rc);
3448
3449 /* ack fw init done interrupt */
James Ketrenosb095c382005-08-24 22:04:42 -05003450 ipw_write32(priv, IPW_INTA_RW, IPW_INTA_BIT_FW_INITIALIZATION_DONE);
James Ketrenos43f66a62005-03-25 12:31:53 -06003451
3452 /* read eeprom data and initialize the eeprom region of sram */
3453 priv->eeprom_delay = 1;
Jeff Garzikbf794512005-07-31 13:07:26 -04003454 ipw_eeprom_init_sram(priv);
James Ketrenos43f66a62005-03-25 12:31:53 -06003455
3456 /* enable interrupts */
3457 ipw_enable_interrupts(priv);
3458
3459 /* Ensure our queue has valid packets */
3460 ipw_rx_queue_replenish(priv);
3461
James Ketrenosb095c382005-08-24 22:04:42 -05003462 ipw_write32(priv, IPW_RX_READ_INDEX, priv->rxq->read);
James Ketrenos43f66a62005-03-25 12:31:53 -06003463
3464 /* ack pending interrupts */
James Ketrenosb095c382005-08-24 22:04:42 -05003465 ipw_write32(priv, IPW_INTA_RW, IPW_INTA_MASK_ALL);
James Ketrenos43f66a62005-03-25 12:31:53 -06003466
3467#ifndef CONFIG_PM
3468 release_firmware(bootfw);
3469 release_firmware(ucode);
3470 release_firmware(firmware);
3471#endif
3472 return 0;
3473
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003474 error:
James Ketrenos43f66a62005-03-25 12:31:53 -06003475 if (priv->rxq) {
3476 ipw_rx_queue_free(priv, priv->rxq);
3477 priv->rxq = NULL;
3478 }
3479 ipw_tx_queue_free(priv);
3480 if (bootfw)
3481 release_firmware(bootfw);
3482 if (ucode)
3483 release_firmware(ucode);
3484 if (firmware)
3485 release_firmware(firmware);
3486#ifdef CONFIG_PM
3487 fw_loaded = 0;
3488 bootfw = ucode = firmware = NULL;
3489#endif
3490
3491 return rc;
3492}
3493
Jeff Garzikbf794512005-07-31 13:07:26 -04003494/**
James Ketrenos43f66a62005-03-25 12:31:53 -06003495 * DMA services
3496 *
3497 * Theory of operation
3498 *
3499 * A queue is a circular buffers with 'Read' and 'Write' pointers.
3500 * 2 empty entries always kept in the buffer to protect from overflow.
3501 *
3502 * For Tx queue, there are low mark and high mark limits. If, after queuing
Jeff Garzikbf794512005-07-31 13:07:26 -04003503 * the packet for Tx, free space become < low mark, Tx queue stopped. When
3504 * reclaiming packets (on 'tx done IRQ), if free space become > high mark,
James Ketrenos43f66a62005-03-25 12:31:53 -06003505 * Tx queue resumed.
3506 *
3507 * The IPW operates with six queues, one receive queue in the device's
3508 * sram, one transmit queue for sending commands to the device firmware,
Jeff Garzikbf794512005-07-31 13:07:26 -04003509 * and four transmit queues for data.
James Ketrenos43f66a62005-03-25 12:31:53 -06003510 *
Jeff Garzikbf794512005-07-31 13:07:26 -04003511 * The four transmit queues allow for performing quality of service (qos)
James Ketrenos43f66a62005-03-25 12:31:53 -06003512 * transmissions as per the 802.11 protocol. Currently Linux does not
Jeff Garzikbf794512005-07-31 13:07:26 -04003513 * provide a mechanism to the user for utilizing prioritized queues, so
James Ketrenos43f66a62005-03-25 12:31:53 -06003514 * we only utilize the first data transmit queue (queue1).
3515 */
3516
3517/**
3518 * Driver allocates buffers of this size for Rx
3519 */
3520
3521static inline int ipw_queue_space(const struct clx2_queue *q)
3522{
3523 int s = q->last_used - q->first_empty;
3524 if (s <= 0)
3525 s += q->n_bd;
3526 s -= 2; /* keep some reserve to not confuse empty and full situations */
3527 if (s < 0)
3528 s = 0;
3529 return s;
3530}
3531
3532static inline int ipw_queue_inc_wrap(int index, int n_bd)
3533{
3534 return (++index == n_bd) ? 0 : index;
3535}
3536
3537/**
3538 * Initialize common DMA queue structure
Jeff Garzikbf794512005-07-31 13:07:26 -04003539 *
James Ketrenos43f66a62005-03-25 12:31:53 -06003540 * @param q queue to init
3541 * @param count Number of BD's to allocate. Should be power of 2
3542 * @param read_register Address for 'read' register
3543 * (not offset within BAR, full address)
3544 * @param write_register Address for 'write' register
3545 * (not offset within BAR, full address)
3546 * @param base_register Address for 'base' register
3547 * (not offset within BAR, full address)
3548 * @param size Address for 'size' register
3549 * (not offset within BAR, full address)
3550 */
Jeff Garzikbf794512005-07-31 13:07:26 -04003551static void ipw_queue_init(struct ipw_priv *priv, struct clx2_queue *q,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003552 int count, u32 read, u32 write, u32 base, u32 size)
James Ketrenos43f66a62005-03-25 12:31:53 -06003553{
3554 q->n_bd = count;
3555
3556 q->low_mark = q->n_bd / 4;
3557 if (q->low_mark < 4)
3558 q->low_mark = 4;
3559
3560 q->high_mark = q->n_bd / 8;
3561 if (q->high_mark < 2)
3562 q->high_mark = 2;
3563
3564 q->first_empty = q->last_used = 0;
3565 q->reg_r = read;
3566 q->reg_w = write;
3567
3568 ipw_write32(priv, base, q->dma_addr);
3569 ipw_write32(priv, size, count);
3570 ipw_write32(priv, read, 0);
3571 ipw_write32(priv, write, 0);
3572
3573 _ipw_read32(priv, 0x90);
3574}
3575
Jeff Garzikbf794512005-07-31 13:07:26 -04003576static int ipw_queue_tx_init(struct ipw_priv *priv,
James Ketrenos43f66a62005-03-25 12:31:53 -06003577 struct clx2_tx_queue *q,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003578 int count, u32 read, u32 write, u32 base, u32 size)
James Ketrenos43f66a62005-03-25 12:31:53 -06003579{
3580 struct pci_dev *dev = priv->pci_dev;
3581
3582 q->txb = kmalloc(sizeof(q->txb[0]) * count, GFP_KERNEL);
3583 if (!q->txb) {
3584 IPW_ERROR("vmalloc for auxilary BD structures failed\n");
3585 return -ENOMEM;
3586 }
3587
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003588 q->bd =
3589 pci_alloc_consistent(dev, sizeof(q->bd[0]) * count, &q->q.dma_addr);
James Ketrenos43f66a62005-03-25 12:31:53 -06003590 if (!q->bd) {
Jiri Bencaaa4d302005-06-07 14:58:41 +02003591 IPW_ERROR("pci_alloc_consistent(%zd) failed\n",
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003592 sizeof(q->bd[0]) * count);
James Ketrenos43f66a62005-03-25 12:31:53 -06003593 kfree(q->txb);
3594 q->txb = NULL;
3595 return -ENOMEM;
3596 }
3597
3598 ipw_queue_init(priv, &q->q, count, read, write, base, size);
3599 return 0;
3600}
3601
3602/**
3603 * Free one TFD, those at index [txq->q.last_used].
3604 * Do NOT advance any indexes
Jeff Garzikbf794512005-07-31 13:07:26 -04003605 *
James Ketrenos43f66a62005-03-25 12:31:53 -06003606 * @param dev
3607 * @param txq
3608 */
3609static void ipw_queue_tx_free_tfd(struct ipw_priv *priv,
3610 struct clx2_tx_queue *txq)
3611{
3612 struct tfd_frame *bd = &txq->bd[txq->q.last_used];
3613 struct pci_dev *dev = priv->pci_dev;
3614 int i;
Jeff Garzikbf794512005-07-31 13:07:26 -04003615
James Ketrenos43f66a62005-03-25 12:31:53 -06003616 /* classify bd */
3617 if (bd->control_flags.message_type == TX_HOST_COMMAND_TYPE)
3618 /* nothing to cleanup after for host commands */
3619 return;
3620
3621 /* sanity check */
James Ketrenosa613bff2005-08-24 21:43:11 -05003622 if (le32_to_cpu(bd->u.data.num_chunks) > NUM_TFD_CHUNKS) {
3623 IPW_ERROR("Too many chunks: %i\n",
3624 le32_to_cpu(bd->u.data.num_chunks));
James Ketrenos43f66a62005-03-25 12:31:53 -06003625 /** @todo issue fatal error, it is quite serious situation */
3626 return;
3627 }
3628
3629 /* unmap chunks if any */
James Ketrenosa613bff2005-08-24 21:43:11 -05003630 for (i = 0; i < le32_to_cpu(bd->u.data.num_chunks); i++) {
3631 pci_unmap_single(dev, le32_to_cpu(bd->u.data.chunk_ptr[i]),
3632 le16_to_cpu(bd->u.data.chunk_len[i]),
3633 PCI_DMA_TODEVICE);
James Ketrenos43f66a62005-03-25 12:31:53 -06003634 if (txq->txb[txq->q.last_used]) {
3635 ieee80211_txb_free(txq->txb[txq->q.last_used]);
3636 txq->txb[txq->q.last_used] = NULL;
3637 }
3638 }
3639}
3640
3641/**
3642 * Deallocate DMA queue.
Jeff Garzikbf794512005-07-31 13:07:26 -04003643 *
James Ketrenos43f66a62005-03-25 12:31:53 -06003644 * Empty queue by removing and destroying all BD's.
3645 * Free all buffers.
Jeff Garzikbf794512005-07-31 13:07:26 -04003646 *
James Ketrenos43f66a62005-03-25 12:31:53 -06003647 * @param dev
3648 * @param q
3649 */
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003650static void ipw_queue_tx_free(struct ipw_priv *priv, struct clx2_tx_queue *txq)
James Ketrenos43f66a62005-03-25 12:31:53 -06003651{
3652 struct clx2_queue *q = &txq->q;
3653 struct pci_dev *dev = priv->pci_dev;
3654
Jeff Garzikbf794512005-07-31 13:07:26 -04003655 if (q->n_bd == 0)
3656 return;
James Ketrenos43f66a62005-03-25 12:31:53 -06003657
3658 /* first, empty all BD's */
3659 for (; q->first_empty != q->last_used;
3660 q->last_used = ipw_queue_inc_wrap(q->last_used, q->n_bd)) {
3661 ipw_queue_tx_free_tfd(priv, txq);
3662 }
Jeff Garzikbf794512005-07-31 13:07:26 -04003663
James Ketrenos43f66a62005-03-25 12:31:53 -06003664 /* free buffers belonging to queue itself */
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003665 pci_free_consistent(dev, sizeof(txq->bd[0]) * q->n_bd, txq->bd,
James Ketrenos43f66a62005-03-25 12:31:53 -06003666 q->dma_addr);
3667 kfree(txq->txb);
3668
3669 /* 0 fill whole structure */
3670 memset(txq, 0, sizeof(*txq));
3671}
3672
James Ketrenos43f66a62005-03-25 12:31:53 -06003673/**
3674 * Destroy all DMA queues and structures
Jeff Garzikbf794512005-07-31 13:07:26 -04003675 *
James Ketrenos43f66a62005-03-25 12:31:53 -06003676 * @param priv
3677 */
3678static void ipw_tx_queue_free(struct ipw_priv *priv)
3679{
3680 /* Tx CMD queue */
3681 ipw_queue_tx_free(priv, &priv->txq_cmd);
3682
3683 /* Tx queues */
3684 ipw_queue_tx_free(priv, &priv->txq[0]);
3685 ipw_queue_tx_free(priv, &priv->txq[1]);
3686 ipw_queue_tx_free(priv, &priv->txq[2]);
3687 ipw_queue_tx_free(priv, &priv->txq[3]);
3688}
3689
Arjan van de Ven858119e2006-01-14 13:20:43 -08003690static void ipw_create_bssid(struct ipw_priv *priv, u8 * bssid)
James Ketrenos43f66a62005-03-25 12:31:53 -06003691{
3692 /* First 3 bytes are manufacturer */
3693 bssid[0] = priv->mac_addr[0];
3694 bssid[1] = priv->mac_addr[1];
3695 bssid[2] = priv->mac_addr[2];
3696
3697 /* Last bytes are random */
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003698 get_random_bytes(&bssid[3], ETH_ALEN - 3);
James Ketrenos43f66a62005-03-25 12:31:53 -06003699
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003700 bssid[0] &= 0xfe; /* clear multicast bit */
3701 bssid[0] |= 0x02; /* set local assignment bit (IEEE802) */
James Ketrenos43f66a62005-03-25 12:31:53 -06003702}
3703
Arjan van de Ven858119e2006-01-14 13:20:43 -08003704static u8 ipw_add_station(struct ipw_priv *priv, u8 * bssid)
James Ketrenos43f66a62005-03-25 12:31:53 -06003705{
3706 struct ipw_station_entry entry;
3707 int i;
3708
3709 for (i = 0; i < priv->num_stations; i++) {
3710 if (!memcmp(priv->stations[i], bssid, ETH_ALEN)) {
3711 /* Another node is active in network */
3712 priv->missed_adhoc_beacons = 0;
3713 if (!(priv->config & CFG_STATIC_CHANNEL))
3714 /* when other nodes drop out, we drop out */
3715 priv->config &= ~CFG_ADHOC_PERSIST;
3716
3717 return i;
3718 }
3719 }
3720
3721 if (i == MAX_STATIONS)
3722 return IPW_INVALID_STATION;
3723
3724 IPW_DEBUG_SCAN("Adding AdHoc station: " MAC_FMT "\n", MAC_ARG(bssid));
3725
3726 entry.reserved = 0;
3727 entry.support_mode = 0;
3728 memcpy(entry.mac_addr, bssid, ETH_ALEN);
3729 memcpy(priv->stations[i], bssid, ETH_ALEN);
3730 ipw_write_direct(priv, IPW_STATION_TABLE_LOWER + i * sizeof(entry),
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003731 &entry, sizeof(entry));
James Ketrenos43f66a62005-03-25 12:31:53 -06003732 priv->num_stations++;
3733
3734 return i;
3735}
3736
Arjan van de Ven858119e2006-01-14 13:20:43 -08003737static u8 ipw_find_station(struct ipw_priv *priv, u8 * bssid)
James Ketrenos43f66a62005-03-25 12:31:53 -06003738{
3739 int i;
3740
Jeff Garzikbf794512005-07-31 13:07:26 -04003741 for (i = 0; i < priv->num_stations; i++)
3742 if (!memcmp(priv->stations[i], bssid, ETH_ALEN))
James Ketrenos43f66a62005-03-25 12:31:53 -06003743 return i;
3744
3745 return IPW_INVALID_STATION;
3746}
3747
3748static void ipw_send_disassociate(struct ipw_priv *priv, int quiet)
3749{
3750 int err;
3751
Hong Liu7b996592005-08-25 17:36:13 +08003752 if (priv->status & STATUS_ASSOCIATING) {
3753 IPW_DEBUG_ASSOC("Disassociating while associating.\n");
3754 queue_work(priv->workqueue, &priv->disassociate);
3755 return;
3756 }
3757
3758 if (!(priv->status & STATUS_ASSOCIATED)) {
James Ketrenos43f66a62005-03-25 12:31:53 -06003759 IPW_DEBUG_ASSOC("Disassociating while not associated.\n");
3760 return;
3761 }
3762
3763 IPW_DEBUG_ASSOC("Disassocation attempt from " MAC_FMT " "
3764 "on channel %d.\n",
Jeff Garzikbf794512005-07-31 13:07:26 -04003765 MAC_ARG(priv->assoc_request.bssid),
James Ketrenos43f66a62005-03-25 12:31:53 -06003766 priv->assoc_request.channel);
3767
3768 priv->status &= ~(STATUS_ASSOCIATING | STATUS_ASSOCIATED);
3769 priv->status |= STATUS_DISASSOCIATING;
3770
3771 if (quiet)
3772 priv->assoc_request.assoc_type = HC_DISASSOC_QUIET;
3773 else
3774 priv->assoc_request.assoc_type = HC_DISASSOCIATE;
Hong Liue6324722005-09-14 21:04:15 -05003775
James Ketrenos43f66a62005-03-25 12:31:53 -06003776 err = ipw_send_associate(priv, &priv->assoc_request);
3777 if (err) {
3778 IPW_DEBUG_HC("Attempt to send [dis]associate command "
3779 "failed.\n");
3780 return;
3781 }
3782
3783}
3784
James Ketrenosc848d0a2005-08-24 21:56:24 -05003785static int ipw_disassociate(void *data)
James Ketrenos43f66a62005-03-25 12:31:53 -06003786{
James Ketrenosc848d0a2005-08-24 21:56:24 -05003787 struct ipw_priv *priv = data;
3788 if (!(priv->status & (STATUS_ASSOCIATED | STATUS_ASSOCIATING)))
3789 return 0;
James Ketrenos43f66a62005-03-25 12:31:53 -06003790 ipw_send_disassociate(data, 0);
James Ketrenosc848d0a2005-08-24 21:56:24 -05003791 return 1;
James Ketrenos43f66a62005-03-25 12:31:53 -06003792}
3793
James Ketrenosc848d0a2005-08-24 21:56:24 -05003794static void ipw_bg_disassociate(void *data)
James Ketrenos43f66a62005-03-25 12:31:53 -06003795{
James Ketrenosc848d0a2005-08-24 21:56:24 -05003796 struct ipw_priv *priv = data;
3797 down(&priv->sem);
3798 ipw_disassociate(data);
3799 up(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06003800}
3801
Zhu Yid8bad6d2005-07-13 12:25:38 -05003802static void ipw_system_config(void *data)
3803{
3804 struct ipw_priv *priv = data;
3805 ipw_send_system_config(priv, &priv->sys_config);
James Ketrenos43f66a62005-03-25 12:31:53 -06003806}
3807
3808struct ipw_status_code {
3809 u16 status;
3810 const char *reason;
3811};
3812
3813static const struct ipw_status_code ipw_status_codes[] = {
3814 {0x00, "Successful"},
3815 {0x01, "Unspecified failure"},
3816 {0x0A, "Cannot support all requested capabilities in the "
3817 "Capability information field"},
3818 {0x0B, "Reassociation denied due to inability to confirm that "
3819 "association exists"},
3820 {0x0C, "Association denied due to reason outside the scope of this "
3821 "standard"},
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003822 {0x0D,
3823 "Responding station does not support the specified authentication "
James Ketrenos43f66a62005-03-25 12:31:53 -06003824 "algorithm"},
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003825 {0x0E,
3826 "Received an Authentication frame with authentication sequence "
James Ketrenos43f66a62005-03-25 12:31:53 -06003827 "transaction sequence number out of expected sequence"},
3828 {0x0F, "Authentication rejected because of challenge failure"},
3829 {0x10, "Authentication rejected due to timeout waiting for next "
3830 "frame in sequence"},
3831 {0x11, "Association denied because AP is unable to handle additional "
3832 "associated stations"},
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003833 {0x12,
3834 "Association denied due to requesting station not supporting all "
James Ketrenos43f66a62005-03-25 12:31:53 -06003835 "of the datarates in the BSSBasicServiceSet Parameter"},
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003836 {0x13,
3837 "Association denied due to requesting station not supporting "
James Ketrenos43f66a62005-03-25 12:31:53 -06003838 "short preamble operation"},
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003839 {0x14,
3840 "Association denied due to requesting station not supporting "
James Ketrenos43f66a62005-03-25 12:31:53 -06003841 "PBCC encoding"},
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003842 {0x15,
3843 "Association denied due to requesting station not supporting "
James Ketrenos43f66a62005-03-25 12:31:53 -06003844 "channel agility"},
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003845 {0x19,
3846 "Association denied due to requesting station not supporting "
James Ketrenos43f66a62005-03-25 12:31:53 -06003847 "short slot operation"},
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003848 {0x1A,
3849 "Association denied due to requesting station not supporting "
James Ketrenos43f66a62005-03-25 12:31:53 -06003850 "DSSS-OFDM operation"},
3851 {0x28, "Invalid Information Element"},
3852 {0x29, "Group Cipher is not valid"},
3853 {0x2A, "Pairwise Cipher is not valid"},
3854 {0x2B, "AKMP is not valid"},
3855 {0x2C, "Unsupported RSN IE version"},
3856 {0x2D, "Invalid RSN IE Capabilities"},
3857 {0x2E, "Cipher suite is rejected per security policy"},
3858};
3859
Brice Goglin0f52bf92005-12-01 01:41:46 -08003860#ifdef CONFIG_IPW2200_DEBUG
Jeff Garzikbf794512005-07-31 13:07:26 -04003861static const char *ipw_get_status_code(u16 status)
James Ketrenos43f66a62005-03-25 12:31:53 -06003862{
3863 int i;
Jeff Garzikbf794512005-07-31 13:07:26 -04003864 for (i = 0; i < ARRAY_SIZE(ipw_status_codes); i++)
James Ketrenosea2b26e2005-08-24 21:25:16 -05003865 if (ipw_status_codes[i].status == (status & 0xff))
James Ketrenos43f66a62005-03-25 12:31:53 -06003866 return ipw_status_codes[i].reason;
3867 return "Unknown status value.";
3868}
3869#endif
3870
3871static void inline average_init(struct average *avg)
3872{
3873 memset(avg, 0, sizeof(*avg));
3874}
3875
Arjan van de Ven858119e2006-01-14 13:20:43 -08003876static void average_add(struct average *avg, s16 val)
James Ketrenos43f66a62005-03-25 12:31:53 -06003877{
3878 avg->sum -= avg->entries[avg->pos];
3879 avg->sum += val;
3880 avg->entries[avg->pos++] = val;
3881 if (unlikely(avg->pos == AVG_ENTRIES)) {
3882 avg->init = 1;
3883 avg->pos = 0;
3884 }
3885}
3886
Arjan van de Ven858119e2006-01-14 13:20:43 -08003887static s16 average_value(struct average *avg)
James Ketrenos43f66a62005-03-25 12:31:53 -06003888{
3889 if (!unlikely(avg->init)) {
3890 if (avg->pos)
3891 return avg->sum / avg->pos;
3892 return 0;
3893 }
3894
3895 return avg->sum / AVG_ENTRIES;
3896}
3897
3898static void ipw_reset_stats(struct ipw_priv *priv)
3899{
3900 u32 len = sizeof(u32);
3901
3902 priv->quality = 0;
3903
3904 average_init(&priv->average_missed_beacons);
3905 average_init(&priv->average_rssi);
3906 average_init(&priv->average_noise);
3907
3908 priv->last_rate = 0;
3909 priv->last_missed_beacons = 0;
3910 priv->last_rx_packets = 0;
3911 priv->last_tx_packets = 0;
3912 priv->last_tx_failures = 0;
Jeff Garzikbf794512005-07-31 13:07:26 -04003913
James Ketrenos43f66a62005-03-25 12:31:53 -06003914 /* Firmware managed, reset only when NIC is restarted, so we have to
3915 * normalize on the current value */
Jeff Garzikbf794512005-07-31 13:07:26 -04003916 ipw_get_ordinal(priv, IPW_ORD_STAT_RX_ERR_CRC,
James Ketrenos43f66a62005-03-25 12:31:53 -06003917 &priv->last_rx_err, &len);
Jeff Garzikbf794512005-07-31 13:07:26 -04003918 ipw_get_ordinal(priv, IPW_ORD_STAT_TX_FAILURE,
James Ketrenos43f66a62005-03-25 12:31:53 -06003919 &priv->last_tx_failures, &len);
3920
3921 /* Driver managed, reset with each association */
3922 priv->missed_adhoc_beacons = 0;
3923 priv->missed_beacons = 0;
3924 priv->tx_packets = 0;
3925 priv->rx_packets = 0;
3926
3927}
3928
Arjan van de Ven858119e2006-01-14 13:20:43 -08003929static u32 ipw_get_max_rate(struct ipw_priv *priv)
James Ketrenos43f66a62005-03-25 12:31:53 -06003930{
3931 u32 i = 0x80000000;
3932 u32 mask = priv->rates_mask;
3933 /* If currently associated in B mode, restrict the maximum
3934 * rate match to B rates */
3935 if (priv->assoc_request.ieee_mode == IPW_B_MODE)
3936 mask &= IEEE80211_CCK_RATES_MASK;
3937
3938 /* TODO: Verify that the rate is supported by the current rates
3939 * list. */
3940
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003941 while (i && !(mask & i))
3942 i >>= 1;
James Ketrenos43f66a62005-03-25 12:31:53 -06003943 switch (i) {
James Ketrenosea2b26e2005-08-24 21:25:16 -05003944 case IEEE80211_CCK_RATE_1MB_MASK:
3945 return 1000000;
3946 case IEEE80211_CCK_RATE_2MB_MASK:
3947 return 2000000;
3948 case IEEE80211_CCK_RATE_5MB_MASK:
3949 return 5500000;
3950 case IEEE80211_OFDM_RATE_6MB_MASK:
3951 return 6000000;
3952 case IEEE80211_OFDM_RATE_9MB_MASK:
3953 return 9000000;
3954 case IEEE80211_CCK_RATE_11MB_MASK:
3955 return 11000000;
3956 case IEEE80211_OFDM_RATE_12MB_MASK:
3957 return 12000000;
3958 case IEEE80211_OFDM_RATE_18MB_MASK:
3959 return 18000000;
3960 case IEEE80211_OFDM_RATE_24MB_MASK:
3961 return 24000000;
3962 case IEEE80211_OFDM_RATE_36MB_MASK:
3963 return 36000000;
3964 case IEEE80211_OFDM_RATE_48MB_MASK:
3965 return 48000000;
3966 case IEEE80211_OFDM_RATE_54MB_MASK:
3967 return 54000000;
James Ketrenos43f66a62005-03-25 12:31:53 -06003968 }
3969
Jeff Garzikbf794512005-07-31 13:07:26 -04003970 if (priv->ieee->mode == IEEE_B)
James Ketrenos43f66a62005-03-25 12:31:53 -06003971 return 11000000;
3972 else
3973 return 54000000;
3974}
3975
3976static u32 ipw_get_current_rate(struct ipw_priv *priv)
3977{
3978 u32 rate, len = sizeof(rate);
3979 int err;
3980
Jeff Garzikbf794512005-07-31 13:07:26 -04003981 if (!(priv->status & STATUS_ASSOCIATED))
James Ketrenos43f66a62005-03-25 12:31:53 -06003982 return 0;
3983
3984 if (priv->tx_packets > IPW_REAL_RATE_RX_PACKET_THRESHOLD) {
Jeff Garzikbf794512005-07-31 13:07:26 -04003985 err = ipw_get_ordinal(priv, IPW_ORD_STAT_TX_CURR_RATE, &rate,
James Ketrenos43f66a62005-03-25 12:31:53 -06003986 &len);
3987 if (err) {
3988 IPW_DEBUG_INFO("failed querying ordinals.\n");
3989 return 0;
3990 }
Jeff Garzikbf794512005-07-31 13:07:26 -04003991 } else
James Ketrenos43f66a62005-03-25 12:31:53 -06003992 return ipw_get_max_rate(priv);
3993
3994 switch (rate) {
James Ketrenosea2b26e2005-08-24 21:25:16 -05003995 case IPW_TX_RATE_1MB:
3996 return 1000000;
3997 case IPW_TX_RATE_2MB:
3998 return 2000000;
3999 case IPW_TX_RATE_5MB:
4000 return 5500000;
4001 case IPW_TX_RATE_6MB:
4002 return 6000000;
4003 case IPW_TX_RATE_9MB:
4004 return 9000000;
4005 case IPW_TX_RATE_11MB:
4006 return 11000000;
4007 case IPW_TX_RATE_12MB:
4008 return 12000000;
4009 case IPW_TX_RATE_18MB:
4010 return 18000000;
4011 case IPW_TX_RATE_24MB:
4012 return 24000000;
4013 case IPW_TX_RATE_36MB:
4014 return 36000000;
4015 case IPW_TX_RATE_48MB:
4016 return 48000000;
4017 case IPW_TX_RATE_54MB:
4018 return 54000000;
James Ketrenos43f66a62005-03-25 12:31:53 -06004019 }
4020
4021 return 0;
4022}
4023
James Ketrenos43f66a62005-03-25 12:31:53 -06004024#define IPW_STATS_INTERVAL (2 * HZ)
4025static void ipw_gather_stats(struct ipw_priv *priv)
4026{
4027 u32 rx_err, rx_err_delta, rx_packets_delta;
4028 u32 tx_failures, tx_failures_delta, tx_packets_delta;
4029 u32 missed_beacons_percent, missed_beacons_delta;
4030 u32 quality = 0;
4031 u32 len = sizeof(u32);
4032 s16 rssi;
Jeff Garzikbf794512005-07-31 13:07:26 -04004033 u32 beacon_quality, signal_quality, tx_quality, rx_quality,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004034 rate_quality;
James Ketrenosea2b26e2005-08-24 21:25:16 -05004035 u32 max_rate;
James Ketrenos43f66a62005-03-25 12:31:53 -06004036
4037 if (!(priv->status & STATUS_ASSOCIATED)) {
4038 priv->quality = 0;
4039 return;
4040 }
4041
4042 /* Update the statistics */
Jeff Garzikbf794512005-07-31 13:07:26 -04004043 ipw_get_ordinal(priv, IPW_ORD_STAT_MISSED_BEACONS,
James Ketrenos43f66a62005-03-25 12:31:53 -06004044 &priv->missed_beacons, &len);
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004045 missed_beacons_delta = priv->missed_beacons - priv->last_missed_beacons;
James Ketrenos43f66a62005-03-25 12:31:53 -06004046 priv->last_missed_beacons = priv->missed_beacons;
4047 if (priv->assoc_request.beacon_interval) {
4048 missed_beacons_percent = missed_beacons_delta *
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004049 (HZ * priv->assoc_request.beacon_interval) /
4050 (IPW_STATS_INTERVAL * 10);
James Ketrenos43f66a62005-03-25 12:31:53 -06004051 } else {
4052 missed_beacons_percent = 0;
4053 }
4054 average_add(&priv->average_missed_beacons, missed_beacons_percent);
4055
4056 ipw_get_ordinal(priv, IPW_ORD_STAT_RX_ERR_CRC, &rx_err, &len);
4057 rx_err_delta = rx_err - priv->last_rx_err;
4058 priv->last_rx_err = rx_err;
4059
4060 ipw_get_ordinal(priv, IPW_ORD_STAT_TX_FAILURE, &tx_failures, &len);
4061 tx_failures_delta = tx_failures - priv->last_tx_failures;
4062 priv->last_tx_failures = tx_failures;
4063
4064 rx_packets_delta = priv->rx_packets - priv->last_rx_packets;
4065 priv->last_rx_packets = priv->rx_packets;
4066
4067 tx_packets_delta = priv->tx_packets - priv->last_tx_packets;
4068 priv->last_tx_packets = priv->tx_packets;
4069
4070 /* Calculate quality based on the following:
Jeff Garzikbf794512005-07-31 13:07:26 -04004071 *
James Ketrenos43f66a62005-03-25 12:31:53 -06004072 * Missed beacon: 100% = 0, 0% = 70% missed
4073 * Rate: 60% = 1Mbs, 100% = Max
4074 * Rx and Tx errors represent a straight % of total Rx/Tx
4075 * RSSI: 100% = > -50, 0% = < -80
4076 * Rx errors: 100% = 0, 0% = 50% missed
Jeff Garzikbf794512005-07-31 13:07:26 -04004077 *
James Ketrenos43f66a62005-03-25 12:31:53 -06004078 * The lowest computed quality is used.
4079 *
4080 */
4081#define BEACON_THRESHOLD 5
4082 beacon_quality = 100 - missed_beacons_percent;
4083 if (beacon_quality < BEACON_THRESHOLD)
4084 beacon_quality = 0;
4085 else
Jeff Garzikbf794512005-07-31 13:07:26 -04004086 beacon_quality = (beacon_quality - BEACON_THRESHOLD) * 100 /
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004087 (100 - BEACON_THRESHOLD);
Jeff Garzikbf794512005-07-31 13:07:26 -04004088 IPW_DEBUG_STATS("Missed beacon: %3d%% (%d%%)\n",
James Ketrenos43f66a62005-03-25 12:31:53 -06004089 beacon_quality, missed_beacons_percent);
Jeff Garzikbf794512005-07-31 13:07:26 -04004090
James Ketrenos43f66a62005-03-25 12:31:53 -06004091 priv->last_rate = ipw_get_current_rate(priv);
James Ketrenosea2b26e2005-08-24 21:25:16 -05004092 max_rate = ipw_get_max_rate(priv);
4093 rate_quality = priv->last_rate * 40 / max_rate + 60;
James Ketrenos43f66a62005-03-25 12:31:53 -06004094 IPW_DEBUG_STATS("Rate quality : %3d%% (%dMbs)\n",
4095 rate_quality, priv->last_rate / 1000000);
Jeff Garzikbf794512005-07-31 13:07:26 -04004096
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004097 if (rx_packets_delta > 100 && rx_packets_delta + rx_err_delta)
Jeff Garzikbf794512005-07-31 13:07:26 -04004098 rx_quality = 100 - (rx_err_delta * 100) /
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004099 (rx_packets_delta + rx_err_delta);
James Ketrenos43f66a62005-03-25 12:31:53 -06004100 else
4101 rx_quality = 100;
4102 IPW_DEBUG_STATS("Rx quality : %3d%% (%u errors, %u packets)\n",
4103 rx_quality, rx_err_delta, rx_packets_delta);
Jeff Garzikbf794512005-07-31 13:07:26 -04004104
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004105 if (tx_packets_delta > 100 && tx_packets_delta + tx_failures_delta)
Jeff Garzikbf794512005-07-31 13:07:26 -04004106 tx_quality = 100 - (tx_failures_delta * 100) /
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004107 (tx_packets_delta + tx_failures_delta);
James Ketrenos43f66a62005-03-25 12:31:53 -06004108 else
4109 tx_quality = 100;
4110 IPW_DEBUG_STATS("Tx quality : %3d%% (%u errors, %u packets)\n",
4111 tx_quality, tx_failures_delta, tx_packets_delta);
Jeff Garzikbf794512005-07-31 13:07:26 -04004112
James Ketrenos43f66a62005-03-25 12:31:53 -06004113 rssi = average_value(&priv->average_rssi);
James Ketrenosc848d0a2005-08-24 21:56:24 -05004114 signal_quality =
4115 (100 *
4116 (priv->ieee->perfect_rssi - priv->ieee->worst_rssi) *
4117 (priv->ieee->perfect_rssi - priv->ieee->worst_rssi) -
4118 (priv->ieee->perfect_rssi - rssi) *
4119 (15 * (priv->ieee->perfect_rssi - priv->ieee->worst_rssi) +
4120 62 * (priv->ieee->perfect_rssi - rssi))) /
4121 ((priv->ieee->perfect_rssi - priv->ieee->worst_rssi) *
4122 (priv->ieee->perfect_rssi - priv->ieee->worst_rssi));
4123 if (signal_quality > 100)
James Ketrenos43f66a62005-03-25 12:31:53 -06004124 signal_quality = 100;
James Ketrenosc848d0a2005-08-24 21:56:24 -05004125 else if (signal_quality < 1)
James Ketrenos43f66a62005-03-25 12:31:53 -06004126 signal_quality = 0;
James Ketrenosea2b26e2005-08-24 21:25:16 -05004127
James Ketrenos43f66a62005-03-25 12:31:53 -06004128 IPW_DEBUG_STATS("Signal level : %3d%% (%d dBm)\n",
4129 signal_quality, rssi);
Jeff Garzikbf794512005-07-31 13:07:26 -04004130
4131 quality = min(beacon_quality,
James Ketrenos43f66a62005-03-25 12:31:53 -06004132 min(rate_quality,
4133 min(tx_quality, min(rx_quality, signal_quality))));
4134 if (quality == beacon_quality)
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004135 IPW_DEBUG_STATS("Quality (%d%%): Clamped to missed beacons.\n",
4136 quality);
James Ketrenos43f66a62005-03-25 12:31:53 -06004137 if (quality == rate_quality)
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004138 IPW_DEBUG_STATS("Quality (%d%%): Clamped to rate quality.\n",
4139 quality);
James Ketrenos43f66a62005-03-25 12:31:53 -06004140 if (quality == tx_quality)
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004141 IPW_DEBUG_STATS("Quality (%d%%): Clamped to Tx quality.\n",
4142 quality);
James Ketrenos43f66a62005-03-25 12:31:53 -06004143 if (quality == rx_quality)
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004144 IPW_DEBUG_STATS("Quality (%d%%): Clamped to Rx quality.\n",
4145 quality);
James Ketrenos43f66a62005-03-25 12:31:53 -06004146 if (quality == signal_quality)
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004147 IPW_DEBUG_STATS("Quality (%d%%): Clamped to signal quality.\n",
4148 quality);
James Ketrenos43f66a62005-03-25 12:31:53 -06004149
4150 priv->quality = quality;
Jeff Garzikbf794512005-07-31 13:07:26 -04004151
4152 queue_delayed_work(priv->workqueue, &priv->gather_stats,
James Ketrenos43f66a62005-03-25 12:31:53 -06004153 IPW_STATS_INTERVAL);
4154}
4155
James Ketrenosc848d0a2005-08-24 21:56:24 -05004156static void ipw_bg_gather_stats(void *data)
4157{
4158 struct ipw_priv *priv = data;
4159 down(&priv->sem);
4160 ipw_gather_stats(data);
4161 up(&priv->sem);
4162}
4163
Ben Cahille7582562005-10-06 15:34:41 -05004164/* Missed beacon behavior:
4165 * 1st missed -> roaming_threshold, just wait, don't do any scan/roam.
4166 * roaming_threshold -> disassociate_threshold, scan and roam for better signal.
4167 * Above disassociate threshold, give up and stop scanning.
4168 * Roaming is disabled if disassociate_threshold <= roaming_threshold */
Arjan van de Ven858119e2006-01-14 13:20:43 -08004169static void ipw_handle_missed_beacon(struct ipw_priv *priv,
James Ketrenosea2b26e2005-08-24 21:25:16 -05004170 int missed_count)
4171{
4172 priv->notif_missed_beacons = missed_count;
4173
James Ketrenosafbf30a2005-08-25 00:05:33 -05004174 if (missed_count > priv->disassociate_threshold &&
James Ketrenosea2b26e2005-08-24 21:25:16 -05004175 priv->status & STATUS_ASSOCIATED) {
4176 /* If associated and we've hit the missed
4177 * beacon threshold, disassociate, turn
4178 * off roaming, and abort any active scans */
4179 IPW_DEBUG(IPW_DL_INFO | IPW_DL_NOTIF |
James Ketrenosafbf30a2005-08-25 00:05:33 -05004180 IPW_DL_STATE | IPW_DL_ASSOC,
James Ketrenosea2b26e2005-08-24 21:25:16 -05004181 "Missed beacon: %d - disassociate\n", missed_count);
4182 priv->status &= ~STATUS_ROAMING;
James Ketrenosa613bff2005-08-24 21:43:11 -05004183 if (priv->status & STATUS_SCANNING) {
4184 IPW_DEBUG(IPW_DL_INFO | IPW_DL_NOTIF |
4185 IPW_DL_STATE,
4186 "Aborting scan with missed beacon.\n");
James Ketrenosea2b26e2005-08-24 21:25:16 -05004187 queue_work(priv->workqueue, &priv->abort_scan);
James Ketrenosa613bff2005-08-24 21:43:11 -05004188 }
4189
James Ketrenosea2b26e2005-08-24 21:25:16 -05004190 queue_work(priv->workqueue, &priv->disassociate);
4191 return;
4192 }
4193
4194 if (priv->status & STATUS_ROAMING) {
4195 /* If we are currently roaming, then just
4196 * print a debug statement... */
4197 IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE,
4198 "Missed beacon: %d - roam in progress\n",
4199 missed_count);
4200 return;
4201 }
4202
Zhu Yi4bfdb912006-01-24 16:37:16 +08004203 if (roaming &&
4204 (missed_count > priv->roaming_threshold &&
4205 missed_count <= priv->disassociate_threshold)) {
James Ketrenosea2b26e2005-08-24 21:25:16 -05004206 /* If we are not already roaming, set the ROAM
Ben Cahille7582562005-10-06 15:34:41 -05004207 * bit in the status and kick off a scan.
4208 * This can happen several times before we reach
4209 * disassociate_threshold. */
James Ketrenosea2b26e2005-08-24 21:25:16 -05004210 IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE,
4211 "Missed beacon: %d - initiate "
4212 "roaming\n", missed_count);
4213 if (!(priv->status & STATUS_ROAMING)) {
4214 priv->status |= STATUS_ROAMING;
4215 if (!(priv->status & STATUS_SCANNING))
4216 queue_work(priv->workqueue,
4217 &priv->request_scan);
4218 }
4219 return;
4220 }
4221
4222 if (priv->status & STATUS_SCANNING) {
4223 /* Stop scan to keep fw from getting
4224 * stuck (only if we aren't roaming --
4225 * otherwise we'll never scan more than 2 or 3
4226 * channels..) */
James Ketrenosb095c382005-08-24 22:04:42 -05004227 IPW_DEBUG(IPW_DL_INFO | IPW_DL_NOTIF | IPW_DL_STATE,
4228 "Aborting scan with missed beacon.\n");
James Ketrenosea2b26e2005-08-24 21:25:16 -05004229 queue_work(priv->workqueue, &priv->abort_scan);
4230 }
4231
4232 IPW_DEBUG_NOTIF("Missed beacon: %d\n", missed_count);
James Ketrenosea2b26e2005-08-24 21:25:16 -05004233}
4234
James Ketrenos43f66a62005-03-25 12:31:53 -06004235/**
4236 * Handle host notification packet.
4237 * Called from interrupt routine
4238 */
Arjan van de Ven858119e2006-01-14 13:20:43 -08004239static void ipw_rx_notification(struct ipw_priv *priv,
James Ketrenos43f66a62005-03-25 12:31:53 -06004240 struct ipw_rx_notification *notif)
4241{
James Ketrenosa613bff2005-08-24 21:43:11 -05004242 notif->size = le16_to_cpu(notif->size);
4243
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004244 IPW_DEBUG_NOTIF("type = %i (%d bytes)\n", notif->subtype, notif->size);
Jeff Garzikbf794512005-07-31 13:07:26 -04004245
James Ketrenos43f66a62005-03-25 12:31:53 -06004246 switch (notif->subtype) {
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004247 case HOST_NOTIFICATION_STATUS_ASSOCIATED:{
4248 struct notif_association *assoc = &notif->u.assoc;
Jeff Garzikbf794512005-07-31 13:07:26 -04004249
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004250 switch (assoc->state) {
4251 case CMAS_ASSOCIATED:{
4252 IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE |
4253 IPW_DL_ASSOC,
4254 "associated: '%s' " MAC_FMT
4255 " \n",
4256 escape_essid(priv->essid,
4257 priv->essid_len),
4258 MAC_ARG(priv->bssid));
Jeff Garzikbf794512005-07-31 13:07:26 -04004259
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004260 switch (priv->ieee->iw_mode) {
4261 case IW_MODE_INFRA:
4262 memcpy(priv->ieee->bssid,
4263 priv->bssid, ETH_ALEN);
4264 break;
James Ketrenos43f66a62005-03-25 12:31:53 -06004265
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004266 case IW_MODE_ADHOC:
4267 memcpy(priv->ieee->bssid,
4268 priv->bssid, ETH_ALEN);
Jeff Garzikbf794512005-07-31 13:07:26 -04004269
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004270 /* clear out the station table */
4271 priv->num_stations = 0;
James Ketrenos43f66a62005-03-25 12:31:53 -06004272
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004273 IPW_DEBUG_ASSOC
4274 ("queueing adhoc check\n");
4275 queue_delayed_work(priv->
4276 workqueue,
4277 &priv->
4278 adhoc_check,
4279 priv->
4280 assoc_request.
4281 beacon_interval);
4282 break;
4283 }
James Ketrenos43f66a62005-03-25 12:31:53 -06004284
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004285 priv->status &= ~STATUS_ASSOCIATING;
4286 priv->status |= STATUS_ASSOCIATED;
Zhu Yid8bad6d2005-07-13 12:25:38 -05004287 queue_work(priv->workqueue,
4288 &priv->system_config);
James Ketrenos43f66a62005-03-25 12:31:53 -06004289
James Ketrenosb095c382005-08-24 22:04:42 -05004290#ifdef CONFIG_IPW_QOS
James Ketrenosafbf30a2005-08-25 00:05:33 -05004291#define IPW_GET_PACKET_STYPE(x) WLAN_FC_GET_STYPE( \
4292 le16_to_cpu(((struct ieee80211_hdr *)(x))->frame_ctl))
4293 if ((priv->status & STATUS_AUTH) &&
4294 (IPW_GET_PACKET_STYPE(&notif->u.raw)
4295 == IEEE80211_STYPE_ASSOC_RESP)) {
James Ketrenosb095c382005-08-24 22:04:42 -05004296 if ((sizeof
4297 (struct
James Ketrenos2b184d52005-08-03 20:33:14 -05004298 ieee80211_assoc_response)
James Ketrenosb095c382005-08-24 22:04:42 -05004299 <= notif->size)
4300 && (notif->size <= 2314)) {
4301 struct
4302 ieee80211_rx_stats
4303 stats = {
4304 .len =
4305 notif->
4306 size - 1,
4307 };
4308
4309 IPW_DEBUG_QOS
4310 ("QoS Associate "
4311 "size %d\n",
4312 notif->size);
4313 ieee80211_rx_mgt(priv->
4314 ieee,
4315 (struct
James Ketrenos2b184d52005-08-03 20:33:14 -05004316 ieee80211_hdr_4addr
James Ketrenosb095c382005-08-24 22:04:42 -05004317 *)
4318 &notif->u.raw, &stats);
4319 }
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004320 }
James Ketrenosb095c382005-08-24 22:04:42 -05004321#endif
James Ketrenos43f66a62005-03-25 12:31:53 -06004322
James Ketrenosa613bff2005-08-24 21:43:11 -05004323 schedule_work(&priv->link_up);
James Ketrenos43f66a62005-03-25 12:31:53 -06004324
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004325 break;
4326 }
4327
4328 case CMAS_AUTHENTICATED:{
4329 if (priv->
4330 status & (STATUS_ASSOCIATED |
4331 STATUS_AUTH)) {
Brice Goglin0f52bf92005-12-01 01:41:46 -08004332#ifdef CONFIG_IPW2200_DEBUG
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004333 struct notif_authenticate *auth
4334 = &notif->u.auth;
4335 IPW_DEBUG(IPW_DL_NOTIF |
4336 IPW_DL_STATE |
4337 IPW_DL_ASSOC,
4338 "deauthenticated: '%s' "
4339 MAC_FMT
4340 ": (0x%04X) - %s \n",
4341 escape_essid(priv->
4342 essid,
4343 priv->
4344 essid_len),
4345 MAC_ARG(priv->bssid),
4346 ntohs(auth->status),
4347 ipw_get_status_code
4348 (ntohs
4349 (auth->status)));
4350#endif
4351
4352 priv->status &=
4353 ~(STATUS_ASSOCIATING |
4354 STATUS_AUTH |
4355 STATUS_ASSOCIATED);
4356
James Ketrenosa613bff2005-08-24 21:43:11 -05004357 schedule_work(&priv->link_down);
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004358 break;
4359 }
4360
4361 IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE |
4362 IPW_DL_ASSOC,
4363 "authenticated: '%s' " MAC_FMT
4364 "\n",
4365 escape_essid(priv->essid,
4366 priv->essid_len),
4367 MAC_ARG(priv->bssid));
4368 break;
4369 }
4370
4371 case CMAS_INIT:{
James Ketrenosea2b26e2005-08-24 21:25:16 -05004372 if (priv->status & STATUS_AUTH) {
4373 struct
4374 ieee80211_assoc_response
4375 *resp;
4376 resp =
4377 (struct
4378 ieee80211_assoc_response
4379 *)&notif->u.raw;
4380 IPW_DEBUG(IPW_DL_NOTIF |
4381 IPW_DL_STATE |
4382 IPW_DL_ASSOC,
4383 "association failed (0x%04X): %s\n",
4384 ntohs(resp->status),
4385 ipw_get_status_code
4386 (ntohs
4387 (resp->status)));
4388 }
4389
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004390 IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE |
4391 IPW_DL_ASSOC,
4392 "disassociated: '%s' " MAC_FMT
4393 " \n",
4394 escape_essid(priv->essid,
4395 priv->essid_len),
4396 MAC_ARG(priv->bssid));
4397
4398 priv->status &=
4399 ~(STATUS_DISASSOCIATING |
4400 STATUS_ASSOCIATING |
4401 STATUS_ASSOCIATED | STATUS_AUTH);
James Ketrenosb095c382005-08-24 22:04:42 -05004402 if (priv->assoc_network
4403 && (priv->assoc_network->
4404 capability &
4405 WLAN_CAPABILITY_IBSS))
4406 ipw_remove_current_network
4407 (priv);
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004408
James Ketrenosa613bff2005-08-24 21:43:11 -05004409 schedule_work(&priv->link_down);
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004410
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004411 break;
4412 }
4413
James Ketrenosb095c382005-08-24 22:04:42 -05004414 case CMAS_RX_ASSOC_RESP:
4415 break;
4416
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004417 default:
4418 IPW_ERROR("assoc: unknown (%d)\n",
4419 assoc->state);
4420 break;
4421 }
4422
James Ketrenos43f66a62005-03-25 12:31:53 -06004423 break;
4424 }
Jeff Garzikbf794512005-07-31 13:07:26 -04004425
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004426 case HOST_NOTIFICATION_STATUS_AUTHENTICATE:{
4427 struct notif_authenticate *auth = &notif->u.auth;
4428 switch (auth->state) {
4429 case CMAS_AUTHENTICATED:
4430 IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE,
4431 "authenticated: '%s' " MAC_FMT " \n",
4432 escape_essid(priv->essid,
4433 priv->essid_len),
4434 MAC_ARG(priv->bssid));
4435 priv->status |= STATUS_AUTH;
4436 break;
4437
4438 case CMAS_INIT:
4439 if (priv->status & STATUS_AUTH) {
4440 IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE |
4441 IPW_DL_ASSOC,
4442 "authentication failed (0x%04X): %s\n",
4443 ntohs(auth->status),
4444 ipw_get_status_code(ntohs
4445 (auth->
4446 status)));
4447 }
4448 IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE |
4449 IPW_DL_ASSOC,
4450 "deauthenticated: '%s' " MAC_FMT "\n",
4451 escape_essid(priv->essid,
4452 priv->essid_len),
4453 MAC_ARG(priv->bssid));
James Ketrenos43f66a62005-03-25 12:31:53 -06004454
4455 priv->status &= ~(STATUS_ASSOCIATING |
4456 STATUS_AUTH |
4457 STATUS_ASSOCIATED);
4458
James Ketrenosa613bff2005-08-24 21:43:11 -05004459 schedule_work(&priv->link_down);
James Ketrenos43f66a62005-03-25 12:31:53 -06004460 break;
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004461
4462 case CMAS_TX_AUTH_SEQ_1:
4463 IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE |
4464 IPW_DL_ASSOC, "AUTH_SEQ_1\n");
4465 break;
4466 case CMAS_RX_AUTH_SEQ_2:
4467 IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE |
4468 IPW_DL_ASSOC, "AUTH_SEQ_2\n");
4469 break;
4470 case CMAS_AUTH_SEQ_1_PASS:
4471 IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE |
4472 IPW_DL_ASSOC, "AUTH_SEQ_1_PASS\n");
4473 break;
4474 case CMAS_AUTH_SEQ_1_FAIL:
4475 IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE |
4476 IPW_DL_ASSOC, "AUTH_SEQ_1_FAIL\n");
4477 break;
4478 case CMAS_TX_AUTH_SEQ_3:
4479 IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE |
4480 IPW_DL_ASSOC, "AUTH_SEQ_3\n");
4481 break;
4482 case CMAS_RX_AUTH_SEQ_4:
4483 IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE |
4484 IPW_DL_ASSOC, "RX_AUTH_SEQ_4\n");
4485 break;
4486 case CMAS_AUTH_SEQ_2_PASS:
4487 IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE |
4488 IPW_DL_ASSOC, "AUTH_SEQ_2_PASS\n");
4489 break;
4490 case CMAS_AUTH_SEQ_2_FAIL:
4491 IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE |
4492 IPW_DL_ASSOC, "AUT_SEQ_2_FAIL\n");
4493 break;
4494 case CMAS_TX_ASSOC:
4495 IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE |
4496 IPW_DL_ASSOC, "TX_ASSOC\n");
4497 break;
4498 case CMAS_RX_ASSOC_RESP:
4499 IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE |
4500 IPW_DL_ASSOC, "RX_ASSOC_RESP\n");
James Ketrenosb095c382005-08-24 22:04:42 -05004501
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004502 break;
4503 case CMAS_ASSOCIATED:
4504 IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE |
4505 IPW_DL_ASSOC, "ASSOCIATED\n");
4506 break;
4507 default:
4508 IPW_DEBUG_NOTIF("auth: failure - %d\n",
4509 auth->state);
4510 break;
4511 }
4512 break;
4513 }
4514
4515 case HOST_NOTIFICATION_STATUS_SCAN_CHANNEL_RESULT:{
4516 struct notif_channel_result *x =
4517 &notif->u.channel_result;
4518
4519 if (notif->size == sizeof(*x)) {
4520 IPW_DEBUG_SCAN("Scan result for channel %d\n",
4521 x->channel_num);
4522 } else {
4523 IPW_DEBUG_SCAN("Scan result of wrong size %d "
4524 "(should be %zd)\n",
4525 notif->size, sizeof(*x));
4526 }
4527 break;
4528 }
4529
4530 case HOST_NOTIFICATION_STATUS_SCAN_COMPLETED:{
4531 struct notif_scan_complete *x = &notif->u.scan_complete;
4532 if (notif->size == sizeof(*x)) {
4533 IPW_DEBUG_SCAN
4534 ("Scan completed: type %d, %d channels, "
4535 "%d status\n", x->scan_type,
4536 x->num_channels, x->status);
4537 } else {
4538 IPW_ERROR("Scan completed of wrong size %d "
4539 "(should be %zd)\n",
4540 notif->size, sizeof(*x));
4541 }
4542
4543 priv->status &=
4544 ~(STATUS_SCANNING | STATUS_SCAN_ABORTING);
4545
James Ketrenosa0e04ab2005-08-25 00:49:43 -05004546 wake_up_interruptible(&priv->wait_state);
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004547 cancel_delayed_work(&priv->scan_check);
4548
James Ketrenosb095c382005-08-24 22:04:42 -05004549 if (priv->status & STATUS_EXIT_PENDING)
4550 break;
4551
4552 priv->ieee->scans++;
4553
4554#ifdef CONFIG_IPW2200_MONITOR
4555 if (priv->ieee->iw_mode == IW_MODE_MONITOR) {
James Ketrenosafbf30a2005-08-25 00:05:33 -05004556 priv->status |= STATUS_SCAN_FORCED;
James Ketrenosb095c382005-08-24 22:04:42 -05004557 queue_work(priv->workqueue,
4558 &priv->request_scan);
4559 break;
4560 }
James Ketrenosafbf30a2005-08-25 00:05:33 -05004561 priv->status &= ~STATUS_SCAN_FORCED;
James Ketrenosb095c382005-08-24 22:04:42 -05004562#endif /* CONFIG_IPW2200_MONITOR */
4563
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004564 if (!(priv->status & (STATUS_ASSOCIATED |
4565 STATUS_ASSOCIATING |
4566 STATUS_ROAMING |
4567 STATUS_DISASSOCIATING)))
4568 queue_work(priv->workqueue, &priv->associate);
4569 else if (priv->status & STATUS_ROAMING) {
Ben Cahille7582562005-10-06 15:34:41 -05004570 if (x->status == SCAN_COMPLETED_STATUS_COMPLETE)
4571 /* If a scan completed and we are in roam mode, then
4572 * the scan that completed was the one requested as a
4573 * result of entering roam... so, schedule the
4574 * roam work */
4575 queue_work(priv->workqueue,
4576 &priv->roam);
4577 else
4578 /* Don't schedule if we aborted the scan */
4579 priv->status &= ~STATUS_ROAMING;
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004580 } else if (priv->status & STATUS_SCAN_PENDING)
4581 queue_work(priv->workqueue,
4582 &priv->request_scan);
James Ketrenosa613bff2005-08-24 21:43:11 -05004583 else if (priv->config & CFG_BACKGROUND_SCAN
4584 && priv->status & STATUS_ASSOCIATED)
4585 queue_delayed_work(priv->workqueue,
4586 &priv->request_scan, HZ);
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004587 break;
4588 }
4589
4590 case HOST_NOTIFICATION_STATUS_FRAG_LENGTH:{
4591 struct notif_frag_length *x = &notif->u.frag_len;
4592
James Ketrenosa613bff2005-08-24 21:43:11 -05004593 if (notif->size == sizeof(*x))
4594 IPW_ERROR("Frag length: %d\n",
4595 le16_to_cpu(x->frag_length));
4596 else
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004597 IPW_ERROR("Frag length of wrong size %d "
4598 "(should be %zd)\n",
4599 notif->size, sizeof(*x));
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004600 break;
4601 }
4602
4603 case HOST_NOTIFICATION_STATUS_LINK_DETERIORATION:{
4604 struct notif_link_deterioration *x =
4605 &notif->u.link_deterioration;
James Ketrenosafbf30a2005-08-25 00:05:33 -05004606
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004607 if (notif->size == sizeof(*x)) {
4608 IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE,
4609 "link deterioration: '%s' " MAC_FMT
4610 " \n", escape_essid(priv->essid,
4611 priv->essid_len),
4612 MAC_ARG(priv->bssid));
4613 memcpy(&priv->last_link_deterioration, x,
4614 sizeof(*x));
4615 } else {
4616 IPW_ERROR("Link Deterioration of wrong size %d "
4617 "(should be %zd)\n",
4618 notif->size, sizeof(*x));
4619 }
4620 break;
4621 }
4622
4623 case HOST_NOTIFICATION_DINO_CONFIG_RESPONSE:{
4624 IPW_ERROR("Dino config\n");
4625 if (priv->hcmd
James Ketrenosa613bff2005-08-24 21:43:11 -05004626 && priv->hcmd->cmd != HOST_CMD_DINO_CONFIG)
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004627 IPW_ERROR("Unexpected DINO_CONFIG_RESPONSE\n");
James Ketrenosa613bff2005-08-24 21:43:11 -05004628
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004629 break;
4630 }
4631
4632 case HOST_NOTIFICATION_STATUS_BEACON_STATE:{
4633 struct notif_beacon_state *x = &notif->u.beacon_state;
4634 if (notif->size != sizeof(*x)) {
4635 IPW_ERROR
4636 ("Beacon state of wrong size %d (should "
4637 "be %zd)\n", notif->size, sizeof(*x));
4638 break;
Jeff Garzikbf794512005-07-31 13:07:26 -04004639 }
James Ketrenos43f66a62005-03-25 12:31:53 -06004640
James Ketrenosa613bff2005-08-24 21:43:11 -05004641 if (le32_to_cpu(x->state) ==
4642 HOST_NOTIFICATION_STATUS_BEACON_MISSING)
4643 ipw_handle_missed_beacon(priv,
4644 le32_to_cpu(x->
4645 number));
Jeff Garzikbf794512005-07-31 13:07:26 -04004646
James Ketrenos43f66a62005-03-25 12:31:53 -06004647 break;
4648 }
Jeff Garzikbf794512005-07-31 13:07:26 -04004649
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004650 case HOST_NOTIFICATION_STATUS_TGI_TX_KEY:{
4651 struct notif_tgi_tx_key *x = &notif->u.tgi_tx_key;
4652 if (notif->size == sizeof(*x)) {
4653 IPW_ERROR("TGi Tx Key: state 0x%02x sec type "
4654 "0x%02x station %d\n",
4655 x->key_state, x->security_type,
4656 x->station_index);
4657 break;
James Ketrenos43f66a62005-03-25 12:31:53 -06004658 }
4659
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004660 IPW_ERROR
4661 ("TGi Tx Key of wrong size %d (should be %zd)\n",
4662 notif->size, sizeof(*x));
4663 break;
4664 }
4665
4666 case HOST_NOTIFICATION_CALIB_KEEP_RESULTS:{
4667 struct notif_calibration *x = &notif->u.calibration;
4668
4669 if (notif->size == sizeof(*x)) {
4670 memcpy(&priv->calib, x, sizeof(*x));
4671 IPW_DEBUG_INFO("TODO: Calibration\n");
4672 break;
James Ketrenos43f66a62005-03-25 12:31:53 -06004673 }
4674
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004675 IPW_ERROR
4676 ("Calibration of wrong size %d (should be %zd)\n",
4677 notif->size, sizeof(*x));
James Ketrenos43f66a62005-03-25 12:31:53 -06004678 break;
Jeff Garzikbf794512005-07-31 13:07:26 -04004679 }
James Ketrenos43f66a62005-03-25 12:31:53 -06004680
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004681 case HOST_NOTIFICATION_NOISE_STATS:{
4682 if (notif->size == sizeof(u32)) {
4683 priv->last_noise =
James Ketrenosa613bff2005-08-24 21:43:11 -05004684 (u8) (le32_to_cpu(notif->u.noise.value) &
4685 0xff);
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004686 average_add(&priv->average_noise,
4687 priv->last_noise);
4688 break;
4689 }
James Ketrenos43f66a62005-03-25 12:31:53 -06004690
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004691 IPW_ERROR
4692 ("Noise stat is wrong size %d (should be %zd)\n",
4693 notif->size, sizeof(u32));
James Ketrenos43f66a62005-03-25 12:31:53 -06004694 break;
Jeff Garzikbf794512005-07-31 13:07:26 -04004695 }
4696
James Ketrenos43f66a62005-03-25 12:31:53 -06004697 default:
4698 IPW_ERROR("Unknown notification: "
4699 "subtype=%d,flags=0x%2x,size=%d\n",
4700 notif->subtype, notif->flags, notif->size);
4701 }
4702}
4703
4704/**
4705 * Destroys all DMA structures and initialise them again
Jeff Garzikbf794512005-07-31 13:07:26 -04004706 *
James Ketrenos43f66a62005-03-25 12:31:53 -06004707 * @param priv
4708 * @return error code
4709 */
4710static int ipw_queue_reset(struct ipw_priv *priv)
4711{
4712 int rc = 0;
4713 /** @todo customize queue sizes */
4714 int nTx = 64, nTxCmd = 8;
4715 ipw_tx_queue_free(priv);
4716 /* Tx CMD queue */
4717 rc = ipw_queue_tx_init(priv, &priv->txq_cmd, nTxCmd,
James Ketrenosb095c382005-08-24 22:04:42 -05004718 IPW_TX_CMD_QUEUE_READ_INDEX,
4719 IPW_TX_CMD_QUEUE_WRITE_INDEX,
4720 IPW_TX_CMD_QUEUE_BD_BASE,
4721 IPW_TX_CMD_QUEUE_BD_SIZE);
James Ketrenos43f66a62005-03-25 12:31:53 -06004722 if (rc) {
4723 IPW_ERROR("Tx Cmd queue init failed\n");
4724 goto error;
4725 }
4726 /* Tx queue(s) */
4727 rc = ipw_queue_tx_init(priv, &priv->txq[0], nTx,
James Ketrenosb095c382005-08-24 22:04:42 -05004728 IPW_TX_QUEUE_0_READ_INDEX,
4729 IPW_TX_QUEUE_0_WRITE_INDEX,
4730 IPW_TX_QUEUE_0_BD_BASE, IPW_TX_QUEUE_0_BD_SIZE);
James Ketrenos43f66a62005-03-25 12:31:53 -06004731 if (rc) {
4732 IPW_ERROR("Tx 0 queue init failed\n");
4733 goto error;
4734 }
4735 rc = ipw_queue_tx_init(priv, &priv->txq[1], nTx,
James Ketrenosb095c382005-08-24 22:04:42 -05004736 IPW_TX_QUEUE_1_READ_INDEX,
4737 IPW_TX_QUEUE_1_WRITE_INDEX,
4738 IPW_TX_QUEUE_1_BD_BASE, IPW_TX_QUEUE_1_BD_SIZE);
James Ketrenos43f66a62005-03-25 12:31:53 -06004739 if (rc) {
4740 IPW_ERROR("Tx 1 queue init failed\n");
4741 goto error;
4742 }
4743 rc = ipw_queue_tx_init(priv, &priv->txq[2], nTx,
James Ketrenosb095c382005-08-24 22:04:42 -05004744 IPW_TX_QUEUE_2_READ_INDEX,
4745 IPW_TX_QUEUE_2_WRITE_INDEX,
4746 IPW_TX_QUEUE_2_BD_BASE, IPW_TX_QUEUE_2_BD_SIZE);
James Ketrenos43f66a62005-03-25 12:31:53 -06004747 if (rc) {
4748 IPW_ERROR("Tx 2 queue init failed\n");
4749 goto error;
4750 }
4751 rc = ipw_queue_tx_init(priv, &priv->txq[3], nTx,
James Ketrenosb095c382005-08-24 22:04:42 -05004752 IPW_TX_QUEUE_3_READ_INDEX,
4753 IPW_TX_QUEUE_3_WRITE_INDEX,
4754 IPW_TX_QUEUE_3_BD_BASE, IPW_TX_QUEUE_3_BD_SIZE);
James Ketrenos43f66a62005-03-25 12:31:53 -06004755 if (rc) {
4756 IPW_ERROR("Tx 3 queue init failed\n");
4757 goto error;
4758 }
4759 /* statistics */
4760 priv->rx_bufs_min = 0;
4761 priv->rx_pend_max = 0;
4762 return rc;
4763
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004764 error:
James Ketrenos43f66a62005-03-25 12:31:53 -06004765 ipw_tx_queue_free(priv);
4766 return rc;
4767}
4768
4769/**
4770 * Reclaim Tx queue entries no more used by NIC.
Jeff Garzikbf794512005-07-31 13:07:26 -04004771 *
James Ketrenos43f66a62005-03-25 12:31:53 -06004772 * When FW adwances 'R' index, all entries between old and
4773 * new 'R' index need to be reclaimed. As result, some free space
4774 * forms. If there is enough free space (> low mark), wake Tx queue.
Jeff Garzikbf794512005-07-31 13:07:26 -04004775 *
James Ketrenos43f66a62005-03-25 12:31:53 -06004776 * @note Need to protect against garbage in 'R' index
4777 * @param priv
4778 * @param txq
4779 * @param qindex
4780 * @return Number of used entries remains in the queue
4781 */
Jeff Garzikbf794512005-07-31 13:07:26 -04004782static int ipw_queue_tx_reclaim(struct ipw_priv *priv,
James Ketrenos43f66a62005-03-25 12:31:53 -06004783 struct clx2_tx_queue *txq, int qindex)
4784{
4785 u32 hw_tail;
4786 int used;
4787 struct clx2_queue *q = &txq->q;
4788
4789 hw_tail = ipw_read32(priv, q->reg_r);
4790 if (hw_tail >= q->n_bd) {
4791 IPW_ERROR
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004792 ("Read index for DMA queue (%d) is out of range [0-%d)\n",
4793 hw_tail, q->n_bd);
James Ketrenos43f66a62005-03-25 12:31:53 -06004794 goto done;
4795 }
4796 for (; q->last_used != hw_tail;
4797 q->last_used = ipw_queue_inc_wrap(q->last_used, q->n_bd)) {
4798 ipw_queue_tx_free_tfd(priv, txq);
4799 priv->tx_packets++;
4800 }
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004801 done:
James Ketrenos9ddf84f2005-08-16 17:07:11 -05004802 if ((ipw_queue_space(q) > q->low_mark) &&
4803 (qindex >= 0) &&
4804 (priv->status & STATUS_ASSOCIATED) && netif_running(priv->net_dev))
4805 netif_wake_queue(priv->net_dev);
James Ketrenos43f66a62005-03-25 12:31:53 -06004806 used = q->first_empty - q->last_used;
4807 if (used < 0)
4808 used += q->n_bd;
4809
4810 return used;
4811}
4812
4813static int ipw_queue_tx_hcmd(struct ipw_priv *priv, int hcmd, void *buf,
4814 int len, int sync)
4815{
4816 struct clx2_tx_queue *txq = &priv->txq_cmd;
4817 struct clx2_queue *q = &txq->q;
4818 struct tfd_frame *tfd;
4819
4820 if (ipw_queue_space(q) < (sync ? 1 : 2)) {
4821 IPW_ERROR("No space for Tx\n");
4822 return -EBUSY;
4823 }
4824
4825 tfd = &txq->bd[q->first_empty];
4826 txq->txb[q->first_empty] = NULL;
4827
4828 memset(tfd, 0, sizeof(*tfd));
4829 tfd->control_flags.message_type = TX_HOST_COMMAND_TYPE;
4830 tfd->control_flags.control_bits = TFD_NEED_IRQ_MASK;
4831 priv->hcmd_seq++;
4832 tfd->u.cmd.index = hcmd;
4833 tfd->u.cmd.length = len;
4834 memcpy(tfd->u.cmd.payload, buf, len);
4835 q->first_empty = ipw_queue_inc_wrap(q->first_empty, q->n_bd);
4836 ipw_write32(priv, q->reg_w, q->first_empty);
4837 _ipw_read32(priv, 0x90);
4838
4839 return 0;
4840}
4841
Jeff Garzikbf794512005-07-31 13:07:26 -04004842/*
James Ketrenos43f66a62005-03-25 12:31:53 -06004843 * Rx theory of operation
4844 *
4845 * The host allocates 32 DMA target addresses and passes the host address
James Ketrenosb095c382005-08-24 22:04:42 -05004846 * to the firmware at register IPW_RFDS_TABLE_LOWER + N * RFD_SIZE where N is
James Ketrenos43f66a62005-03-25 12:31:53 -06004847 * 0 to 31
4848 *
4849 * Rx Queue Indexes
4850 * The host/firmware share two index registers for managing the Rx buffers.
4851 *
Jeff Garzikbf794512005-07-31 13:07:26 -04004852 * The READ index maps to the first position that the firmware may be writing
4853 * to -- the driver can read up to (but not including) this position and get
4854 * good data.
James Ketrenos43f66a62005-03-25 12:31:53 -06004855 * The READ index is managed by the firmware once the card is enabled.
4856 *
4857 * The WRITE index maps to the last position the driver has read from -- the
4858 * position preceding WRITE is the last slot the firmware can place a packet.
4859 *
4860 * The queue is empty (no good data) if WRITE = READ - 1, and is full if
Jeff Garzikbf794512005-07-31 13:07:26 -04004861 * WRITE = READ.
James Ketrenos43f66a62005-03-25 12:31:53 -06004862 *
Jeff Garzikbf794512005-07-31 13:07:26 -04004863 * During initialization the host sets up the READ queue position to the first
James Ketrenos43f66a62005-03-25 12:31:53 -06004864 * INDEX position, and WRITE to the last (READ - 1 wrapped)
4865 *
4866 * When the firmware places a packet in a buffer it will advance the READ index
4867 * and fire the RX interrupt. The driver can then query the READ index and
4868 * process as many packets as possible, moving the WRITE index forward as it
4869 * resets the Rx queue buffers with new memory.
Jeff Garzikbf794512005-07-31 13:07:26 -04004870 *
James Ketrenos43f66a62005-03-25 12:31:53 -06004871 * The management in the driver is as follows:
Jeff Garzikbf794512005-07-31 13:07:26 -04004872 * + A list of pre-allocated SKBs is stored in ipw->rxq->rx_free. When
James Ketrenos43f66a62005-03-25 12:31:53 -06004873 * ipw->rxq->free_count drops to or below RX_LOW_WATERMARK, work is scheduled
Jeff Garzikbf794512005-07-31 13:07:26 -04004874 * to replensish the ipw->rxq->rx_free.
James Ketrenos43f66a62005-03-25 12:31:53 -06004875 * + In ipw_rx_queue_replenish (scheduled) if 'processed' != 'read' then the
4876 * ipw->rxq is replenished and the READ INDEX is updated (updating the
4877 * 'processed' and 'read' driver indexes as well)
4878 * + A received packet is processed and handed to the kernel network stack,
4879 * detached from the ipw->rxq. The driver 'processed' index is updated.
4880 * + The Host/Firmware ipw->rxq is replenished at tasklet time from the rx_free
Jeff Garzikbf794512005-07-31 13:07:26 -04004881 * list. If there are no allocated buffers in ipw->rxq->rx_free, the READ
4882 * INDEX is not incremented and ipw->status(RX_STALLED) is set. If there
James Ketrenos43f66a62005-03-25 12:31:53 -06004883 * were enough free buffers and RX_STALLED is set it is cleared.
4884 *
4885 *
4886 * Driver sequence:
4887 *
Jeff Garzikbf794512005-07-31 13:07:26 -04004888 * ipw_rx_queue_alloc() Allocates rx_free
James Ketrenos43f66a62005-03-25 12:31:53 -06004889 * ipw_rx_queue_replenish() Replenishes rx_free list from rx_used, and calls
4890 * ipw_rx_queue_restock
4891 * ipw_rx_queue_restock() Moves available buffers from rx_free into Rx
4892 * queue, updates firmware pointers, and updates
4893 * the WRITE index. If insufficient rx_free buffers
4894 * are available, schedules ipw_rx_queue_replenish
4895 *
4896 * -- enable interrupts --
4897 * ISR - ipw_rx() Detach ipw_rx_mem_buffers from pool up to the
Jeff Garzikbf794512005-07-31 13:07:26 -04004898 * READ INDEX, detaching the SKB from the pool.
James Ketrenos43f66a62005-03-25 12:31:53 -06004899 * Moves the packet buffer from queue to rx_used.
4900 * Calls ipw_rx_queue_restock to refill any empty
4901 * slots.
4902 * ...
4903 *
4904 */
4905
Jeff Garzikbf794512005-07-31 13:07:26 -04004906/*
James Ketrenos43f66a62005-03-25 12:31:53 -06004907 * If there are slots in the RX queue that need to be restocked,
4908 * and we have free pre-allocated buffers, fill the ranks as much
4909 * as we can pulling from rx_free.
4910 *
4911 * This moves the 'write' index forward to catch up with 'processed', and
4912 * also updates the memory address in the firmware to reference the new
4913 * target buffer.
4914 */
4915static void ipw_rx_queue_restock(struct ipw_priv *priv)
4916{
4917 struct ipw_rx_queue *rxq = priv->rxq;
4918 struct list_head *element;
4919 struct ipw_rx_mem_buffer *rxb;
4920 unsigned long flags;
4921 int write;
4922
4923 spin_lock_irqsave(&rxq->lock, flags);
4924 write = rxq->write;
4925 while ((rxq->write != rxq->processed) && (rxq->free_count)) {
4926 element = rxq->rx_free.next;
4927 rxb = list_entry(element, struct ipw_rx_mem_buffer, list);
4928 list_del(element);
4929
James Ketrenosb095c382005-08-24 22:04:42 -05004930 ipw_write32(priv, IPW_RFDS_TABLE_LOWER + rxq->write * RFD_SIZE,
James Ketrenos43f66a62005-03-25 12:31:53 -06004931 rxb->dma_addr);
4932 rxq->queue[rxq->write] = rxb;
4933 rxq->write = (rxq->write + 1) % RX_QUEUE_SIZE;
4934 rxq->free_count--;
4935 }
4936 spin_unlock_irqrestore(&rxq->lock, flags);
4937
Jeff Garzikbf794512005-07-31 13:07:26 -04004938 /* If the pre-allocated buffer pool is dropping low, schedule to
James Ketrenos43f66a62005-03-25 12:31:53 -06004939 * refill it */
4940 if (rxq->free_count <= RX_LOW_WATERMARK)
4941 queue_work(priv->workqueue, &priv->rx_replenish);
4942
4943 /* If we've added more space for the firmware to place data, tell it */
Jeff Garzikbf794512005-07-31 13:07:26 -04004944 if (write != rxq->write)
James Ketrenosb095c382005-08-24 22:04:42 -05004945 ipw_write32(priv, IPW_RX_WRITE_INDEX, rxq->write);
James Ketrenos43f66a62005-03-25 12:31:53 -06004946}
4947
4948/*
4949 * Move all used packet from rx_used to rx_free, allocating a new SKB for each.
Jeff Garzikbf794512005-07-31 13:07:26 -04004950 * Also restock the Rx queue via ipw_rx_queue_restock.
4951 *
James Ketrenos43f66a62005-03-25 12:31:53 -06004952 * This is called as a scheduled work item (except for during intialization)
4953 */
4954static void ipw_rx_queue_replenish(void *data)
4955{
4956 struct ipw_priv *priv = data;
4957 struct ipw_rx_queue *rxq = priv->rxq;
4958 struct list_head *element;
4959 struct ipw_rx_mem_buffer *rxb;
4960 unsigned long flags;
4961
4962 spin_lock_irqsave(&rxq->lock, flags);
4963 while (!list_empty(&rxq->rx_used)) {
4964 element = rxq->rx_used.next;
4965 rxb = list_entry(element, struct ipw_rx_mem_buffer, list);
James Ketrenosb095c382005-08-24 22:04:42 -05004966 rxb->skb = alloc_skb(IPW_RX_BUF_SIZE, GFP_ATOMIC);
James Ketrenos43f66a62005-03-25 12:31:53 -06004967 if (!rxb->skb) {
4968 printk(KERN_CRIT "%s: Can not allocate SKB buffers.\n",
4969 priv->net_dev->name);
4970 /* We don't reschedule replenish work here -- we will
4971 * call the restock method and if it still needs
4972 * more buffers it will schedule replenish */
4973 break;
4974 }
4975 list_del(element);
Jeff Garzikbf794512005-07-31 13:07:26 -04004976
James Ketrenos43f66a62005-03-25 12:31:53 -06004977 rxb->rxb = (struct ipw_rx_buffer *)rxb->skb->data;
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004978 rxb->dma_addr =
4979 pci_map_single(priv->pci_dev, rxb->skb->data,
James Ketrenosb095c382005-08-24 22:04:42 -05004980 IPW_RX_BUF_SIZE, PCI_DMA_FROMDEVICE);
Jeff Garzikbf794512005-07-31 13:07:26 -04004981
James Ketrenos43f66a62005-03-25 12:31:53 -06004982 list_add_tail(&rxb->list, &rxq->rx_free);
4983 rxq->free_count++;
4984 }
4985 spin_unlock_irqrestore(&rxq->lock, flags);
4986
4987 ipw_rx_queue_restock(priv);
4988}
4989
James Ketrenosc848d0a2005-08-24 21:56:24 -05004990static void ipw_bg_rx_queue_replenish(void *data)
4991{
4992 struct ipw_priv *priv = data;
4993 down(&priv->sem);
4994 ipw_rx_queue_replenish(data);
4995 up(&priv->sem);
4996}
4997
James Ketrenos43f66a62005-03-25 12:31:53 -06004998/* Assumes that the skb field of the buffers in 'pool' is kept accurate.
Zhu Yic7b6a672006-01-24 16:37:05 +08004999 * If an SKB has been detached, the POOL needs to have its SKB set to NULL
Jeff Garzikbf794512005-07-31 13:07:26 -04005000 * This free routine walks the list of POOL entries and if SKB is set to
James Ketrenos43f66a62005-03-25 12:31:53 -06005001 * non NULL it is unmapped and freed
5002 */
Jeff Garzik0edd5b42005-09-07 00:48:31 -04005003static void ipw_rx_queue_free(struct ipw_priv *priv, struct ipw_rx_queue *rxq)
James Ketrenos43f66a62005-03-25 12:31:53 -06005004{
5005 int i;
5006
5007 if (!rxq)
5008 return;
Jeff Garzikbf794512005-07-31 13:07:26 -04005009
James Ketrenos43f66a62005-03-25 12:31:53 -06005010 for (i = 0; i < RX_QUEUE_SIZE + RX_FREE_BUFFERS; i++) {
5011 if (rxq->pool[i].skb != NULL) {
5012 pci_unmap_single(priv->pci_dev, rxq->pool[i].dma_addr,
James Ketrenosb095c382005-08-24 22:04:42 -05005013 IPW_RX_BUF_SIZE, PCI_DMA_FROMDEVICE);
James Ketrenos43f66a62005-03-25 12:31:53 -06005014 dev_kfree_skb(rxq->pool[i].skb);
5015 }
5016 }
5017
5018 kfree(rxq);
5019}
5020
5021static struct ipw_rx_queue *ipw_rx_queue_alloc(struct ipw_priv *priv)
5022{
5023 struct ipw_rx_queue *rxq;
5024 int i;
5025
Takisc75f4742005-12-01 01:41:45 -08005026 rxq = kzalloc(sizeof(*rxq), GFP_KERNEL);
Panagiotis Issarisad18b0e2005-09-05 04:14:10 +02005027 if (unlikely(!rxq)) {
5028 IPW_ERROR("memory allocation failed\n");
5029 return NULL;
5030 }
James Ketrenos43f66a62005-03-25 12:31:53 -06005031 spin_lock_init(&rxq->lock);
5032 INIT_LIST_HEAD(&rxq->rx_free);
5033 INIT_LIST_HEAD(&rxq->rx_used);
5034
5035 /* Fill the rx_used queue with _all_ of the Rx buffers */
Jeff Garzikbf794512005-07-31 13:07:26 -04005036 for (i = 0; i < RX_FREE_BUFFERS + RX_QUEUE_SIZE; i++)
James Ketrenos43f66a62005-03-25 12:31:53 -06005037 list_add_tail(&rxq->pool[i].list, &rxq->rx_used);
5038
5039 /* Set us so that we have processed and used all buffers, but have
5040 * not restocked the Rx queue with fresh buffers */
5041 rxq->read = rxq->write = 0;
5042 rxq->processed = RX_QUEUE_SIZE - 1;
5043 rxq->free_count = 0;
5044
5045 return rxq;
5046}
5047
5048static int ipw_is_rate_in_mask(struct ipw_priv *priv, int ieee_mode, u8 rate)
5049{
5050 rate &= ~IEEE80211_BASIC_RATE_MASK;
5051 if (ieee_mode == IEEE_A) {
5052 switch (rate) {
Jeff Garzikbf794512005-07-31 13:07:26 -04005053 case IEEE80211_OFDM_RATE_6MB:
5054 return priv->rates_mask & IEEE80211_OFDM_RATE_6MB_MASK ?
Jeff Garzik0edd5b42005-09-07 00:48:31 -04005055 1 : 0;
Jeff Garzikbf794512005-07-31 13:07:26 -04005056 case IEEE80211_OFDM_RATE_9MB:
5057 return priv->rates_mask & IEEE80211_OFDM_RATE_9MB_MASK ?
Jeff Garzik0edd5b42005-09-07 00:48:31 -04005058 1 : 0;
Jeff Garzikbf794512005-07-31 13:07:26 -04005059 case IEEE80211_OFDM_RATE_12MB:
Jeff Garzik0edd5b42005-09-07 00:48:31 -04005060 return priv->
5061 rates_mask & IEEE80211_OFDM_RATE_12MB_MASK ? 1 : 0;
Jeff Garzikbf794512005-07-31 13:07:26 -04005062 case IEEE80211_OFDM_RATE_18MB:
Jeff Garzik0edd5b42005-09-07 00:48:31 -04005063 return priv->
5064 rates_mask & IEEE80211_OFDM_RATE_18MB_MASK ? 1 : 0;
Jeff Garzikbf794512005-07-31 13:07:26 -04005065 case IEEE80211_OFDM_RATE_24MB:
Jeff Garzik0edd5b42005-09-07 00:48:31 -04005066 return priv->
5067 rates_mask & IEEE80211_OFDM_RATE_24MB_MASK ? 1 : 0;
Jeff Garzikbf794512005-07-31 13:07:26 -04005068 case IEEE80211_OFDM_RATE_36MB:
Jeff Garzik0edd5b42005-09-07 00:48:31 -04005069 return priv->
5070 rates_mask & IEEE80211_OFDM_RATE_36MB_MASK ? 1 : 0;
Jeff Garzikbf794512005-07-31 13:07:26 -04005071 case IEEE80211_OFDM_RATE_48MB:
Jeff Garzik0edd5b42005-09-07 00:48:31 -04005072 return priv->
5073 rates_mask & IEEE80211_OFDM_RATE_48MB_MASK ? 1 : 0;
Jeff Garzikbf794512005-07-31 13:07:26 -04005074 case IEEE80211_OFDM_RATE_54MB:
Jeff Garzik0edd5b42005-09-07 00:48:31 -04005075 return priv->
5076 rates_mask & IEEE80211_OFDM_RATE_54MB_MASK ? 1 : 0;
James Ketrenos43f66a62005-03-25 12:31:53 -06005077 default:
5078 return 0;
5079 }
5080 }
Jeff Garzikbf794512005-07-31 13:07:26 -04005081
James Ketrenos43f66a62005-03-25 12:31:53 -06005082 /* B and G mixed */
5083 switch (rate) {
Jeff Garzikbf794512005-07-31 13:07:26 -04005084 case IEEE80211_CCK_RATE_1MB:
James Ketrenos43f66a62005-03-25 12:31:53 -06005085 return priv->rates_mask & IEEE80211_CCK_RATE_1MB_MASK ? 1 : 0;
Jeff Garzikbf794512005-07-31 13:07:26 -04005086 case IEEE80211_CCK_RATE_2MB:
James Ketrenos43f66a62005-03-25 12:31:53 -06005087 return priv->rates_mask & IEEE80211_CCK_RATE_2MB_MASK ? 1 : 0;
Jeff Garzikbf794512005-07-31 13:07:26 -04005088 case IEEE80211_CCK_RATE_5MB:
James Ketrenos43f66a62005-03-25 12:31:53 -06005089 return priv->rates_mask & IEEE80211_CCK_RATE_5MB_MASK ? 1 : 0;
Jeff Garzikbf794512005-07-31 13:07:26 -04005090 case IEEE80211_CCK_RATE_11MB:
James Ketrenos43f66a62005-03-25 12:31:53 -06005091 return priv->rates_mask & IEEE80211_CCK_RATE_11MB_MASK ? 1 : 0;
5092 }
5093
5094 /* If we are limited to B modulations, bail at this point */
5095 if (ieee_mode == IEEE_B)
5096 return 0;
5097
5098 /* G */
5099 switch (rate) {
Jeff Garzikbf794512005-07-31 13:07:26 -04005100 case IEEE80211_OFDM_RATE_6MB:
James Ketrenos43f66a62005-03-25 12:31:53 -06005101 return priv->rates_mask & IEEE80211_OFDM_RATE_6MB_MASK ? 1 : 0;
Jeff Garzikbf794512005-07-31 13:07:26 -04005102 case IEEE80211_OFDM_RATE_9MB:
James Ketrenos43f66a62005-03-25 12:31:53 -06005103 return priv->rates_mask & IEEE80211_OFDM_RATE_9MB_MASK ? 1 : 0;
Jeff Garzikbf794512005-07-31 13:07:26 -04005104 case IEEE80211_OFDM_RATE_12MB:
James Ketrenos43f66a62005-03-25 12:31:53 -06005105 return priv->rates_mask & IEEE80211_OFDM_RATE_12MB_MASK ? 1 : 0;
Jeff Garzikbf794512005-07-31 13:07:26 -04005106 case IEEE80211_OFDM_RATE_18MB:
James Ketrenos43f66a62005-03-25 12:31:53 -06005107 return priv->rates_mask & IEEE80211_OFDM_RATE_18MB_MASK ? 1 : 0;
Jeff Garzikbf794512005-07-31 13:07:26 -04005108 case IEEE80211_OFDM_RATE_24MB:
James Ketrenos43f66a62005-03-25 12:31:53 -06005109 return priv->rates_mask & IEEE80211_OFDM_RATE_24MB_MASK ? 1 : 0;
Jeff Garzikbf794512005-07-31 13:07:26 -04005110 case IEEE80211_OFDM_RATE_36MB:
James Ketrenos43f66a62005-03-25 12:31:53 -06005111 return priv->rates_mask & IEEE80211_OFDM_RATE_36MB_MASK ? 1 : 0;
Jeff Garzikbf794512005-07-31 13:07:26 -04005112 case IEEE80211_OFDM_RATE_48MB:
James Ketrenos43f66a62005-03-25 12:31:53 -06005113 return priv->rates_mask & IEEE80211_OFDM_RATE_48MB_MASK ? 1 : 0;
Jeff Garzikbf794512005-07-31 13:07:26 -04005114 case IEEE80211_OFDM_RATE_54MB:
James Ketrenos43f66a62005-03-25 12:31:53 -06005115 return priv->rates_mask & IEEE80211_OFDM_RATE_54MB_MASK ? 1 : 0;
5116 }
5117
5118 return 0;
5119}
5120
Jeff Garzikbf794512005-07-31 13:07:26 -04005121static int ipw_compatible_rates(struct ipw_priv *priv,
James Ketrenos43f66a62005-03-25 12:31:53 -06005122 const struct ieee80211_network *network,
5123 struct ipw_supported_rates *rates)
5124{
5125 int num_rates, i;
5126
5127 memset(rates, 0, sizeof(*rates));
Jeff Garzik0edd5b42005-09-07 00:48:31 -04005128 num_rates = min(network->rates_len, (u8) IPW_MAX_RATES);
James Ketrenos43f66a62005-03-25 12:31:53 -06005129 rates->num_rates = 0;
5130 for (i = 0; i < num_rates; i++) {
James Ketrenosa613bff2005-08-24 21:43:11 -05005131 if (!ipw_is_rate_in_mask(priv, network->mode,
5132 network->rates[i])) {
5133
James Ketrenosea2b26e2005-08-24 21:25:16 -05005134 if (network->rates[i] & IEEE80211_BASIC_RATE_MASK) {
James Ketrenosa613bff2005-08-24 21:43:11 -05005135 IPW_DEBUG_SCAN("Adding masked mandatory "
5136 "rate %02X\n",
5137 network->rates[i]);
5138 rates->supported_rates[rates->num_rates++] =
5139 network->rates[i];
5140 continue;
James Ketrenosea2b26e2005-08-24 21:25:16 -05005141 }
5142
James Ketrenos43f66a62005-03-25 12:31:53 -06005143 IPW_DEBUG_SCAN("Rate %02X masked : 0x%08X\n",
5144 network->rates[i], priv->rates_mask);
5145 continue;
5146 }
Jeff Garzikbf794512005-07-31 13:07:26 -04005147
James Ketrenos43f66a62005-03-25 12:31:53 -06005148 rates->supported_rates[rates->num_rates++] = network->rates[i];
5149 }
5150
James Ketrenosa613bff2005-08-24 21:43:11 -05005151 num_rates = min(network->rates_ex_len,
5152 (u8) (IPW_MAX_RATES - num_rates));
James Ketrenos43f66a62005-03-25 12:31:53 -06005153 for (i = 0; i < num_rates; i++) {
James Ketrenosa613bff2005-08-24 21:43:11 -05005154 if (!ipw_is_rate_in_mask(priv, network->mode,
5155 network->rates_ex[i])) {
James Ketrenosea2b26e2005-08-24 21:25:16 -05005156 if (network->rates_ex[i] & IEEE80211_BASIC_RATE_MASK) {
James Ketrenosa613bff2005-08-24 21:43:11 -05005157 IPW_DEBUG_SCAN("Adding masked mandatory "
5158 "rate %02X\n",
5159 network->rates_ex[i]);
5160 rates->supported_rates[rates->num_rates++] =
5161 network->rates[i];
5162 continue;
James Ketrenosea2b26e2005-08-24 21:25:16 -05005163 }
5164
James Ketrenos43f66a62005-03-25 12:31:53 -06005165 IPW_DEBUG_SCAN("Rate %02X masked : 0x%08X\n",
5166 network->rates_ex[i], priv->rates_mask);
5167 continue;
5168 }
Jeff Garzikbf794512005-07-31 13:07:26 -04005169
Jeff Garzik0edd5b42005-09-07 00:48:31 -04005170 rates->supported_rates[rates->num_rates++] =
5171 network->rates_ex[i];
James Ketrenos43f66a62005-03-25 12:31:53 -06005172 }
5173
James Ketrenosea2b26e2005-08-24 21:25:16 -05005174 return 1;
James Ketrenos43f66a62005-03-25 12:31:53 -06005175}
5176
Arjan van de Ven858119e2006-01-14 13:20:43 -08005177static void ipw_copy_rates(struct ipw_supported_rates *dest,
James Ketrenos43f66a62005-03-25 12:31:53 -06005178 const struct ipw_supported_rates *src)
5179{
5180 u8 i;
5181 for (i = 0; i < src->num_rates; i++)
5182 dest->supported_rates[i] = src->supported_rates[i];
5183 dest->num_rates = src->num_rates;
5184}
5185
5186/* TODO: Look at sniffed packets in the air to determine if the basic rate
5187 * mask should ever be used -- right now all callers to add the scan rates are
5188 * set with the modulation = CCK, so BASIC_RATE_MASK is never set... */
5189static void ipw_add_cck_scan_rates(struct ipw_supported_rates *rates,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04005190 u8 modulation, u32 rate_mask)
James Ketrenos43f66a62005-03-25 12:31:53 -06005191{
Jeff Garzikbf794512005-07-31 13:07:26 -04005192 u8 basic_mask = (IEEE80211_OFDM_MODULATION == modulation) ?
Jeff Garzik0edd5b42005-09-07 00:48:31 -04005193 IEEE80211_BASIC_RATE_MASK : 0;
Jeff Garzikbf794512005-07-31 13:07:26 -04005194
James Ketrenos43f66a62005-03-25 12:31:53 -06005195 if (rate_mask & IEEE80211_CCK_RATE_1MB_MASK)
Jeff Garzikbf794512005-07-31 13:07:26 -04005196 rates->supported_rates[rates->num_rates++] =
Jeff Garzik0edd5b42005-09-07 00:48:31 -04005197 IEEE80211_BASIC_RATE_MASK | IEEE80211_CCK_RATE_1MB;
James Ketrenos43f66a62005-03-25 12:31:53 -06005198
5199 if (rate_mask & IEEE80211_CCK_RATE_2MB_MASK)
Jeff Garzikbf794512005-07-31 13:07:26 -04005200 rates->supported_rates[rates->num_rates++] =
Jeff Garzik0edd5b42005-09-07 00:48:31 -04005201 IEEE80211_BASIC_RATE_MASK | IEEE80211_CCK_RATE_2MB;
James Ketrenos43f66a62005-03-25 12:31:53 -06005202
5203 if (rate_mask & IEEE80211_CCK_RATE_5MB_MASK)
Jeff Garzikbf794512005-07-31 13:07:26 -04005204 rates->supported_rates[rates->num_rates++] = basic_mask |
Jeff Garzik0edd5b42005-09-07 00:48:31 -04005205 IEEE80211_CCK_RATE_5MB;
James Ketrenos43f66a62005-03-25 12:31:53 -06005206
5207 if (rate_mask & IEEE80211_CCK_RATE_11MB_MASK)
Jeff Garzikbf794512005-07-31 13:07:26 -04005208 rates->supported_rates[rates->num_rates++] = basic_mask |
Jeff Garzik0edd5b42005-09-07 00:48:31 -04005209 IEEE80211_CCK_RATE_11MB;
James Ketrenos43f66a62005-03-25 12:31:53 -06005210}
5211
5212static void ipw_add_ofdm_scan_rates(struct ipw_supported_rates *rates,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04005213 u8 modulation, u32 rate_mask)
James Ketrenos43f66a62005-03-25 12:31:53 -06005214{
Jeff Garzikbf794512005-07-31 13:07:26 -04005215 u8 basic_mask = (IEEE80211_OFDM_MODULATION == modulation) ?
Jeff Garzik0edd5b42005-09-07 00:48:31 -04005216 IEEE80211_BASIC_RATE_MASK : 0;
James Ketrenos43f66a62005-03-25 12:31:53 -06005217
5218 if (rate_mask & IEEE80211_OFDM_RATE_6MB_MASK)
Jeff Garzikbf794512005-07-31 13:07:26 -04005219 rates->supported_rates[rates->num_rates++] = basic_mask |
Jeff Garzik0edd5b42005-09-07 00:48:31 -04005220 IEEE80211_OFDM_RATE_6MB;
James Ketrenos43f66a62005-03-25 12:31:53 -06005221
5222 if (rate_mask & IEEE80211_OFDM_RATE_9MB_MASK)
Jeff Garzikbf794512005-07-31 13:07:26 -04005223 rates->supported_rates[rates->num_rates++] =
Jeff Garzik0edd5b42005-09-07 00:48:31 -04005224 IEEE80211_OFDM_RATE_9MB;
James Ketrenos43f66a62005-03-25 12:31:53 -06005225
5226 if (rate_mask & IEEE80211_OFDM_RATE_12MB_MASK)
Jeff Garzikbf794512005-07-31 13:07:26 -04005227 rates->supported_rates[rates->num_rates++] = basic_mask |
Jeff Garzik0edd5b42005-09-07 00:48:31 -04005228 IEEE80211_OFDM_RATE_12MB;
James Ketrenos43f66a62005-03-25 12:31:53 -06005229
5230 if (rate_mask & IEEE80211_OFDM_RATE_18MB_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_18MB;
James Ketrenos43f66a62005-03-25 12:31:53 -06005233
5234 if (rate_mask & IEEE80211_OFDM_RATE_24MB_MASK)
Jeff Garzikbf794512005-07-31 13:07:26 -04005235 rates->supported_rates[rates->num_rates++] = basic_mask |
Jeff Garzik0edd5b42005-09-07 00:48:31 -04005236 IEEE80211_OFDM_RATE_24MB;
James Ketrenos43f66a62005-03-25 12:31:53 -06005237
5238 if (rate_mask & IEEE80211_OFDM_RATE_36MB_MASK)
Jeff Garzikbf794512005-07-31 13:07:26 -04005239 rates->supported_rates[rates->num_rates++] =
Jeff Garzik0edd5b42005-09-07 00:48:31 -04005240 IEEE80211_OFDM_RATE_36MB;
James Ketrenos43f66a62005-03-25 12:31:53 -06005241
5242 if (rate_mask & IEEE80211_OFDM_RATE_48MB_MASK)
Jeff Garzikbf794512005-07-31 13:07:26 -04005243 rates->supported_rates[rates->num_rates++] =
Jeff Garzik0edd5b42005-09-07 00:48:31 -04005244 IEEE80211_OFDM_RATE_48MB;
James Ketrenos43f66a62005-03-25 12:31:53 -06005245
5246 if (rate_mask & IEEE80211_OFDM_RATE_54MB_MASK)
Jeff Garzikbf794512005-07-31 13:07:26 -04005247 rates->supported_rates[rates->num_rates++] =
Jeff Garzik0edd5b42005-09-07 00:48:31 -04005248 IEEE80211_OFDM_RATE_54MB;
James Ketrenos43f66a62005-03-25 12:31:53 -06005249}
5250
5251struct ipw_network_match {
5252 struct ieee80211_network *network;
5253 struct ipw_supported_rates rates;
5254};
5255
James Ketrenosc848d0a2005-08-24 21:56:24 -05005256static int ipw_find_adhoc_network(struct ipw_priv *priv,
5257 struct ipw_network_match *match,
5258 struct ieee80211_network *network,
5259 int roaming)
5260{
5261 struct ipw_supported_rates rates;
5262
5263 /* Verify that this network's capability is compatible with the
5264 * current mode (AdHoc or Infrastructure) */
5265 if ((priv->ieee->iw_mode == IW_MODE_ADHOC &&
5266 !(network->capability & WLAN_CAPABILITY_IBSS))) {
5267 IPW_DEBUG_MERGE("Network '%s (" MAC_FMT ")' excluded due to "
5268 "capability mismatch.\n",
5269 escape_essid(network->ssid, network->ssid_len),
5270 MAC_ARG(network->bssid));
5271 return 0;
5272 }
5273
5274 /* If we do not have an ESSID for this AP, we can not associate with
5275 * it */
5276 if (network->flags & NETWORK_EMPTY_ESSID) {
5277 IPW_DEBUG_MERGE("Network '%s (" MAC_FMT ")' excluded "
5278 "because of hidden ESSID.\n",
5279 escape_essid(network->ssid, network->ssid_len),
5280 MAC_ARG(network->bssid));
5281 return 0;
5282 }
5283
5284 if (unlikely(roaming)) {
5285 /* If we are roaming, then ensure check if this is a valid
5286 * network to try and roam to */
5287 if ((network->ssid_len != match->network->ssid_len) ||
5288 memcmp(network->ssid, match->network->ssid,
5289 network->ssid_len)) {
5290 IPW_DEBUG_MERGE("Netowrk '%s (" MAC_FMT ")' excluded "
5291 "because of non-network ESSID.\n",
5292 escape_essid(network->ssid,
5293 network->ssid_len),
5294 MAC_ARG(network->bssid));
5295 return 0;
5296 }
5297 } else {
5298 /* If an ESSID has been configured then compare the broadcast
5299 * ESSID to ours */
5300 if ((priv->config & CFG_STATIC_ESSID) &&
5301 ((network->ssid_len != priv->essid_len) ||
5302 memcmp(network->ssid, priv->essid,
5303 min(network->ssid_len, priv->essid_len)))) {
5304 char escaped[IW_ESSID_MAX_SIZE * 2 + 1];
James Ketrenosafbf30a2005-08-25 00:05:33 -05005305
James Ketrenosc848d0a2005-08-24 21:56:24 -05005306 strncpy(escaped,
5307 escape_essid(network->ssid, network->ssid_len),
5308 sizeof(escaped));
5309 IPW_DEBUG_MERGE("Network '%s (" MAC_FMT ")' excluded "
5310 "because of ESSID mismatch: '%s'.\n",
5311 escaped, MAC_ARG(network->bssid),
5312 escape_essid(priv->essid,
5313 priv->essid_len));
5314 return 0;
5315 }
5316 }
5317
5318 /* If the old network rate is better than this one, don't bother
5319 * testing everything else. */
5320
5321 if (network->time_stamp[0] < match->network->time_stamp[0]) {
James Ketrenosafbf30a2005-08-25 00:05:33 -05005322 IPW_DEBUG_MERGE("Network '%s excluded because newer than "
5323 "current network.\n",
5324 escape_essid(match->network->ssid,
5325 match->network->ssid_len));
James Ketrenosc848d0a2005-08-24 21:56:24 -05005326 return 0;
5327 } else if (network->time_stamp[1] < match->network->time_stamp[1]) {
James Ketrenosafbf30a2005-08-25 00:05:33 -05005328 IPW_DEBUG_MERGE("Network '%s excluded because newer than "
5329 "current network.\n",
5330 escape_essid(match->network->ssid,
5331 match->network->ssid_len));
James Ketrenosc848d0a2005-08-24 21:56:24 -05005332 return 0;
5333 }
5334
5335 /* Now go through and see if the requested network is valid... */
5336 if (priv->ieee->scan_age != 0 &&
5337 time_after(jiffies, network->last_scanned + priv->ieee->scan_age)) {
5338 IPW_DEBUG_MERGE("Network '%s (" MAC_FMT ")' excluded "
Zhu Yic7b6a672006-01-24 16:37:05 +08005339 "because of age: %ums.\n",
James Ketrenosc848d0a2005-08-24 21:56:24 -05005340 escape_essid(network->ssid, network->ssid_len),
5341 MAC_ARG(network->bssid),
Zhu Yic7b6a672006-01-24 16:37:05 +08005342 jiffies_to_msecs(jiffies - network->last_scanned));
James Ketrenosc848d0a2005-08-24 21:56:24 -05005343 return 0;
5344 }
5345
5346 if ((priv->config & CFG_STATIC_CHANNEL) &&
5347 (network->channel != priv->channel)) {
5348 IPW_DEBUG_MERGE("Network '%s (" MAC_FMT ")' excluded "
5349 "because of channel mismatch: %d != %d.\n",
5350 escape_essid(network->ssid, network->ssid_len),
5351 MAC_ARG(network->bssid),
5352 network->channel, priv->channel);
5353 return 0;
5354 }
5355
5356 /* Verify privacy compatability */
5357 if (((priv->capability & CAP_PRIVACY_ON) ? 1 : 0) !=
5358 ((network->capability & WLAN_CAPABILITY_PRIVACY) ? 1 : 0)) {
5359 IPW_DEBUG_MERGE("Network '%s (" MAC_FMT ")' excluded "
5360 "because of privacy mismatch: %s != %s.\n",
5361 escape_essid(network->ssid, network->ssid_len),
5362 MAC_ARG(network->bssid),
James Ketrenosafbf30a2005-08-25 00:05:33 -05005363 priv->
5364 capability & CAP_PRIVACY_ON ? "on" : "off",
5365 network->
5366 capability & WLAN_CAPABILITY_PRIVACY ? "on" :
5367 "off");
James Ketrenosc848d0a2005-08-24 21:56:24 -05005368 return 0;
5369 }
5370
5371 if (!memcmp(network->bssid, priv->bssid, ETH_ALEN)) {
5372 IPW_DEBUG_MERGE("Network '%s (" MAC_FMT ")' excluded "
5373 "because of the same BSSID match: " MAC_FMT
5374 ".\n", escape_essid(network->ssid,
5375 network->ssid_len),
5376 MAC_ARG(network->bssid), MAC_ARG(priv->bssid));
5377 return 0;
5378 }
5379
5380 /* Filter out any incompatible freq / mode combinations */
5381 if (!ieee80211_is_valid_mode(priv->ieee, network->mode)) {
5382 IPW_DEBUG_MERGE("Network '%s (" MAC_FMT ")' excluded "
5383 "because of invalid frequency/mode "
5384 "combination.\n",
5385 escape_essid(network->ssid, network->ssid_len),
5386 MAC_ARG(network->bssid));
5387 return 0;
5388 }
5389
5390 /* Ensure that the rates supported by the driver are compatible with
5391 * this AP, including verification of basic rates (mandatory) */
5392 if (!ipw_compatible_rates(priv, network, &rates)) {
5393 IPW_DEBUG_MERGE("Network '%s (" MAC_FMT ")' excluded "
5394 "because configured rate mask excludes "
5395 "AP mandatory rate.\n",
5396 escape_essid(network->ssid, network->ssid_len),
5397 MAC_ARG(network->bssid));
5398 return 0;
5399 }
5400
5401 if (rates.num_rates == 0) {
5402 IPW_DEBUG_MERGE("Network '%s (" MAC_FMT ")' excluded "
5403 "because of no compatible rates.\n",
5404 escape_essid(network->ssid, network->ssid_len),
5405 MAC_ARG(network->bssid));
5406 return 0;
5407 }
5408
5409 /* TODO: Perform any further minimal comparititive tests. We do not
5410 * want to put too much policy logic here; intelligent scan selection
5411 * should occur within a generic IEEE 802.11 user space tool. */
5412
5413 /* Set up 'new' AP to this network */
5414 ipw_copy_rates(&match->rates, &rates);
5415 match->network = network;
5416 IPW_DEBUG_MERGE("Network '%s (" MAC_FMT ")' is a viable match.\n",
5417 escape_essid(network->ssid, network->ssid_len),
5418 MAC_ARG(network->bssid));
5419
5420 return 1;
5421}
5422
5423static void ipw_merge_adhoc_network(void *data)
5424{
5425 struct ipw_priv *priv = data;
5426 struct ieee80211_network *network = NULL;
5427 struct ipw_network_match match = {
5428 .network = priv->assoc_network
5429 };
5430
James Ketrenosafbf30a2005-08-25 00:05:33 -05005431 if ((priv->status & STATUS_ASSOCIATED) &&
5432 (priv->ieee->iw_mode == IW_MODE_ADHOC)) {
James Ketrenosc848d0a2005-08-24 21:56:24 -05005433 /* First pass through ROAM process -- look for a better
5434 * network */
5435 unsigned long flags;
5436
5437 spin_lock_irqsave(&priv->ieee->lock, flags);
5438 list_for_each_entry(network, &priv->ieee->network_list, list) {
5439 if (network != priv->assoc_network)
5440 ipw_find_adhoc_network(priv, &match, network,
5441 1);
5442 }
5443 spin_unlock_irqrestore(&priv->ieee->lock, flags);
5444
5445 if (match.network == priv->assoc_network) {
5446 IPW_DEBUG_MERGE("No better ADHOC in this network to "
5447 "merge to.\n");
5448 return;
5449 }
5450
5451 down(&priv->sem);
5452 if ((priv->ieee->iw_mode == IW_MODE_ADHOC)) {
5453 IPW_DEBUG_MERGE("remove network %s\n",
5454 escape_essid(priv->essid,
5455 priv->essid_len));
5456 ipw_remove_current_network(priv);
5457 }
5458
5459 ipw_disassociate(priv);
5460 priv->assoc_network = match.network;
5461 up(&priv->sem);
5462 return;
5463 }
James Ketrenosc848d0a2005-08-24 21:56:24 -05005464}
5465
Jeff Garzik0edd5b42005-09-07 00:48:31 -04005466static int ipw_best_network(struct ipw_priv *priv,
5467 struct ipw_network_match *match,
5468 struct ieee80211_network *network, int roaming)
James Ketrenos43f66a62005-03-25 12:31:53 -06005469{
5470 struct ipw_supported_rates rates;
5471
5472 /* Verify that this network's capability is compatible with the
5473 * current mode (AdHoc or Infrastructure) */
5474 if ((priv->ieee->iw_mode == IW_MODE_INFRA &&
Jouni Malinen24743852005-08-14 20:59:59 -07005475 !(network->capability & WLAN_CAPABILITY_ESS)) ||
James Ketrenos43f66a62005-03-25 12:31:53 -06005476 (priv->ieee->iw_mode == IW_MODE_ADHOC &&
5477 !(network->capability & WLAN_CAPABILITY_IBSS))) {
5478 IPW_DEBUG_ASSOC("Network '%s (" MAC_FMT ")' excluded due to "
Jeff Garzikbf794512005-07-31 13:07:26 -04005479 "capability mismatch.\n",
James Ketrenos43f66a62005-03-25 12:31:53 -06005480 escape_essid(network->ssid, network->ssid_len),
5481 MAC_ARG(network->bssid));
5482 return 0;
5483 }
5484
5485 /* If we do not have an ESSID for this AP, we can not associate with
5486 * it */
5487 if (network->flags & NETWORK_EMPTY_ESSID) {
5488 IPW_DEBUG_ASSOC("Network '%s (" MAC_FMT ")' excluded "
5489 "because of hidden ESSID.\n",
5490 escape_essid(network->ssid, network->ssid_len),
5491 MAC_ARG(network->bssid));
5492 return 0;
5493 }
Jeff Garzikbf794512005-07-31 13:07:26 -04005494
James Ketrenos43f66a62005-03-25 12:31:53 -06005495 if (unlikely(roaming)) {
5496 /* If we are roaming, then ensure check if this is a valid
5497 * network to try and roam to */
5498 if ((network->ssid_len != match->network->ssid_len) ||
Jeff Garzikbf794512005-07-31 13:07:26 -04005499 memcmp(network->ssid, match->network->ssid,
James Ketrenos43f66a62005-03-25 12:31:53 -06005500 network->ssid_len)) {
5501 IPW_DEBUG_ASSOC("Netowrk '%s (" MAC_FMT ")' excluded "
5502 "because of non-network ESSID.\n",
Jeff Garzikbf794512005-07-31 13:07:26 -04005503 escape_essid(network->ssid,
James Ketrenos43f66a62005-03-25 12:31:53 -06005504 network->ssid_len),
5505 MAC_ARG(network->bssid));
5506 return 0;
5507 }
5508 } else {
Jeff Garzikbf794512005-07-31 13:07:26 -04005509 /* If an ESSID has been configured then compare the broadcast
5510 * ESSID to ours */
5511 if ((priv->config & CFG_STATIC_ESSID) &&
James Ketrenos43f66a62005-03-25 12:31:53 -06005512 ((network->ssid_len != priv->essid_len) ||
Jeff Garzikbf794512005-07-31 13:07:26 -04005513 memcmp(network->ssid, priv->essid,
James Ketrenos43f66a62005-03-25 12:31:53 -06005514 min(network->ssid_len, priv->essid_len)))) {
5515 char escaped[IW_ESSID_MAX_SIZE * 2 + 1];
Jeff Garzik0edd5b42005-09-07 00:48:31 -04005516 strncpy(escaped,
5517 escape_essid(network->ssid, network->ssid_len),
James Ketrenos43f66a62005-03-25 12:31:53 -06005518 sizeof(escaped));
5519 IPW_DEBUG_ASSOC("Network '%s (" MAC_FMT ")' excluded "
Jeff Garzikbf794512005-07-31 13:07:26 -04005520 "because of ESSID mismatch: '%s'.\n",
James Ketrenos43f66a62005-03-25 12:31:53 -06005521 escaped, MAC_ARG(network->bssid),
Jeff Garzik0edd5b42005-09-07 00:48:31 -04005522 escape_essid(priv->essid,
5523 priv->essid_len));
James Ketrenos43f66a62005-03-25 12:31:53 -06005524 return 0;
5525 }
5526 }
5527
5528 /* If the old network rate is better than this one, don't bother
5529 * testing everything else. */
Jeff Garzik0edd5b42005-09-07 00:48:31 -04005530 if (match->network && match->network->stats.rssi > network->stats.rssi) {
James Ketrenos43f66a62005-03-25 12:31:53 -06005531 char escaped[IW_ESSID_MAX_SIZE * 2 + 1];
Jeff Garzikbf794512005-07-31 13:07:26 -04005532 strncpy(escaped,
5533 escape_essid(network->ssid, network->ssid_len),
James Ketrenos43f66a62005-03-25 12:31:53 -06005534 sizeof(escaped));
5535 IPW_DEBUG_ASSOC("Network '%s (" MAC_FMT ")' excluded because "
5536 "'%s (" MAC_FMT ")' has a stronger signal.\n",
5537 escaped, MAC_ARG(network->bssid),
5538 escape_essid(match->network->ssid,
5539 match->network->ssid_len),
5540 MAC_ARG(match->network->bssid));
5541 return 0;
5542 }
Jeff Garzikbf794512005-07-31 13:07:26 -04005543
James Ketrenos43f66a62005-03-25 12:31:53 -06005544 /* If this network has already had an association attempt within the
5545 * last 3 seconds, do not try and associate again... */
5546 if (network->last_associate &&
James Ketrenosea2b26e2005-08-24 21:25:16 -05005547 time_after(network->last_associate + (HZ * 3UL), jiffies)) {
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 storming (%ums since last "
James Ketrenos43f66a62005-03-25 12:31:53 -06005550 "assoc attempt).\n",
5551 escape_essid(network->ssid, network->ssid_len),
5552 MAC_ARG(network->bssid),
Zhu Yic7b6a672006-01-24 16:37:05 +08005553 jiffies_to_msecs(jiffies - network->last_associate));
James Ketrenos43f66a62005-03-25 12:31:53 -06005554 return 0;
5555 }
5556
5557 /* Now go through and see if the requested network is valid... */
Jeff Garzikbf794512005-07-31 13:07:26 -04005558 if (priv->ieee->scan_age != 0 &&
James Ketrenosea2b26e2005-08-24 21:25:16 -05005559 time_after(jiffies, network->last_scanned + priv->ieee->scan_age)) {
James Ketrenos43f66a62005-03-25 12:31:53 -06005560 IPW_DEBUG_ASSOC("Network '%s (" MAC_FMT ")' excluded "
Zhu Yic7b6a672006-01-24 16:37:05 +08005561 "because of age: %ums.\n",
James Ketrenos43f66a62005-03-25 12:31:53 -06005562 escape_essid(network->ssid, network->ssid_len),
5563 MAC_ARG(network->bssid),
Zhu Yic7b6a672006-01-24 16:37:05 +08005564 jiffies_to_msecs(jiffies - network->last_scanned));
James Ketrenos43f66a62005-03-25 12:31:53 -06005565 return 0;
Jeff Garzikbf794512005-07-31 13:07:26 -04005566 }
James Ketrenos43f66a62005-03-25 12:31:53 -06005567
Jeff Garzikbf794512005-07-31 13:07:26 -04005568 if ((priv->config & CFG_STATIC_CHANNEL) &&
James Ketrenos43f66a62005-03-25 12:31:53 -06005569 (network->channel != priv->channel)) {
5570 IPW_DEBUG_ASSOC("Network '%s (" MAC_FMT ")' excluded "
5571 "because of channel mismatch: %d != %d.\n",
5572 escape_essid(network->ssid, network->ssid_len),
5573 MAC_ARG(network->bssid),
5574 network->channel, priv->channel);
5575 return 0;
5576 }
Jeff Garzikbf794512005-07-31 13:07:26 -04005577
James Ketrenos43f66a62005-03-25 12:31:53 -06005578 /* Verify privacy compatability */
Jeff Garzikbf794512005-07-31 13:07:26 -04005579 if (((priv->capability & CAP_PRIVACY_ON) ? 1 : 0) !=
James Ketrenos43f66a62005-03-25 12:31:53 -06005580 ((network->capability & WLAN_CAPABILITY_PRIVACY) ? 1 : 0)) {
5581 IPW_DEBUG_ASSOC("Network '%s (" MAC_FMT ")' excluded "
5582 "because of privacy mismatch: %s != %s.\n",
5583 escape_essid(network->ssid, network->ssid_len),
5584 MAC_ARG(network->bssid),
Jeff Garzikbf794512005-07-31 13:07:26 -04005585 priv->capability & CAP_PRIVACY_ON ? "on" :
James Ketrenos43f66a62005-03-25 12:31:53 -06005586 "off",
Jeff Garzikbf794512005-07-31 13:07:26 -04005587 network->capability &
Jeff Garzik0edd5b42005-09-07 00:48:31 -04005588 WLAN_CAPABILITY_PRIVACY ? "on" : "off");
James Ketrenos43f66a62005-03-25 12:31:53 -06005589 return 0;
5590 }
Jeff Garzikbf794512005-07-31 13:07:26 -04005591
Hong Liucdd1fa12005-08-31 18:14:27 +08005592 if (!priv->ieee->wpa_enabled && (network->wpa_ie_len > 0 ||
5593 network->rsn_ie_len > 0)) {
5594 IPW_DEBUG_ASSOC("Network '%s (" MAC_FMT ")' excluded "
5595 "because of WPA capability mismatch.\n",
5596 escape_essid(network->ssid, network->ssid_len),
5597 MAC_ARG(network->bssid));
5598 return 0;
5599 }
5600
Jeff Garzikbf794512005-07-31 13:07:26 -04005601 if ((priv->config & CFG_STATIC_BSSID) &&
James Ketrenos43f66a62005-03-25 12:31:53 -06005602 memcmp(network->bssid, priv->bssid, ETH_ALEN)) {
5603 IPW_DEBUG_ASSOC("Network '%s (" MAC_FMT ")' excluded "
5604 "because of BSSID mismatch: " MAC_FMT ".\n",
5605 escape_essid(network->ssid, network->ssid_len),
Jeff Garzik0edd5b42005-09-07 00:48:31 -04005606 MAC_ARG(network->bssid), MAC_ARG(priv->bssid));
James Ketrenos43f66a62005-03-25 12:31:53 -06005607 return 0;
5608 }
Jeff Garzikbf794512005-07-31 13:07:26 -04005609
James Ketrenos43f66a62005-03-25 12:31:53 -06005610 /* Filter out any incompatible freq / mode combinations */
5611 if (!ieee80211_is_valid_mode(priv->ieee, network->mode)) {
5612 IPW_DEBUG_ASSOC("Network '%s (" MAC_FMT ")' excluded "
5613 "because of invalid frequency/mode "
5614 "combination.\n",
5615 escape_essid(network->ssid, network->ssid_len),
5616 MAC_ARG(network->bssid));
5617 return 0;
5618 }
Jeff Garzikbf794512005-07-31 13:07:26 -04005619
Liu Hong1fe0adb2005-08-19 09:33:10 -05005620 /* Filter out invalid channel in current GEO */
5621 if (!ipw_is_valid_channel(priv->ieee, network->channel)) {
5622 IPW_DEBUG_ASSOC("Network '%s (" MAC_FMT ")' excluded "
5623 "because of invalid channel in current GEO\n",
5624 escape_essid(network->ssid, network->ssid_len),
5625 MAC_ARG(network->bssid));
5626 return 0;
5627 }
5628
James Ketrenosea2b26e2005-08-24 21:25:16 -05005629 /* Ensure that the rates supported by the driver are compatible with
5630 * this AP, including verification of basic rates (mandatory) */
5631 if (!ipw_compatible_rates(priv, network, &rates)) {
5632 IPW_DEBUG_ASSOC("Network '%s (" MAC_FMT ")' excluded "
5633 "because configured rate mask excludes "
5634 "AP mandatory rate.\n",
5635 escape_essid(network->ssid, network->ssid_len),
5636 MAC_ARG(network->bssid));
5637 return 0;
5638 }
5639
James Ketrenos43f66a62005-03-25 12:31:53 -06005640 if (rates.num_rates == 0) {
5641 IPW_DEBUG_ASSOC("Network '%s (" MAC_FMT ")' excluded "
5642 "because of no compatible rates.\n",
5643 escape_essid(network->ssid, network->ssid_len),
5644 MAC_ARG(network->bssid));
5645 return 0;
5646 }
Jeff Garzikbf794512005-07-31 13:07:26 -04005647
James Ketrenos43f66a62005-03-25 12:31:53 -06005648 /* TODO: Perform any further minimal comparititive tests. We do not
5649 * want to put too much policy logic here; intelligent scan selection
5650 * should occur within a generic IEEE 802.11 user space tool. */
5651
5652 /* Set up 'new' AP to this network */
5653 ipw_copy_rates(&match->rates, &rates);
5654 match->network = network;
5655
5656 IPW_DEBUG_ASSOC("Network '%s (" MAC_FMT ")' is a viable match.\n",
5657 escape_essid(network->ssid, network->ssid_len),
5658 MAC_ARG(network->bssid));
5659
5660 return 1;
5661}
5662
Jeff Garzikbf794512005-07-31 13:07:26 -04005663static void ipw_adhoc_create(struct ipw_priv *priv,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04005664 struct ieee80211_network *network)
James Ketrenos43f66a62005-03-25 12:31:53 -06005665{
Liu Hong1fe0adb2005-08-19 09:33:10 -05005666 const struct ieee80211_geo *geo = ipw_get_geo(priv->ieee);
James Ketrenosafbf30a2005-08-25 00:05:33 -05005667 int i;
5668
James Ketrenos43f66a62005-03-25 12:31:53 -06005669 /*
5670 * For the purposes of scanning, we can set our wireless mode
5671 * to trigger scans across combinations of bands, but when it
5672 * comes to creating a new ad-hoc network, we have tell the FW
5673 * exactly which band to use.
5674 *
Jeff Garzikbf794512005-07-31 13:07:26 -04005675 * We also have the possibility of an invalid channel for the
James Ketrenos43f66a62005-03-25 12:31:53 -06005676 * chossen band. Attempting to create a new ad-hoc network
5677 * with an invalid channel for wireless mode will trigger a
5678 * FW fatal error.
James Ketrenosafbf30a2005-08-25 00:05:33 -05005679 *
James Ketrenos43f66a62005-03-25 12:31:53 -06005680 */
Liu Hong1fe0adb2005-08-19 09:33:10 -05005681 switch (ipw_is_valid_channel(priv->ieee, priv->channel)) {
James Ketrenosafbf30a2005-08-25 00:05:33 -05005682 case IEEE80211_52GHZ_BAND:
5683 network->mode = IEEE_A;
Liu Hong1fe0adb2005-08-19 09:33:10 -05005684 i = ipw_channel_to_index(priv->ieee, priv->channel);
James Ketrenosafbf30a2005-08-25 00:05:33 -05005685 if (i == -1)
5686 BUG();
5687 if (geo->a[i].flags & IEEE80211_CH_PASSIVE_ONLY) {
5688 IPW_WARNING("Overriding invalid channel\n");
5689 priv->channel = geo->a[0].channel;
5690 }
5691 break;
5692
5693 case IEEE80211_24GHZ_BAND:
5694 if (priv->ieee->mode & IEEE_G)
5695 network->mode = IEEE_G;
5696 else
5697 network->mode = IEEE_B;
Liu Hong1fe0adb2005-08-19 09:33:10 -05005698 i = ipw_channel_to_index(priv->ieee, priv->channel);
5699 if (i == -1)
5700 BUG();
5701 if (geo->bg[i].flags & IEEE80211_CH_PASSIVE_ONLY) {
5702 IPW_WARNING("Overriding invalid channel\n");
5703 priv->channel = geo->bg[0].channel;
5704 }
James Ketrenosafbf30a2005-08-25 00:05:33 -05005705 break;
5706
5707 default:
James Ketrenos43f66a62005-03-25 12:31:53 -06005708 IPW_WARNING("Overriding invalid channel\n");
5709 if (priv->ieee->mode & IEEE_A) {
5710 network->mode = IEEE_A;
James Ketrenosb095c382005-08-24 22:04:42 -05005711 priv->channel = geo->a[0].channel;
James Ketrenos43f66a62005-03-25 12:31:53 -06005712 } else if (priv->ieee->mode & IEEE_G) {
5713 network->mode = IEEE_G;
James Ketrenosb095c382005-08-24 22:04:42 -05005714 priv->channel = geo->bg[0].channel;
James Ketrenos43f66a62005-03-25 12:31:53 -06005715 } else {
5716 network->mode = IEEE_B;
James Ketrenosb095c382005-08-24 22:04:42 -05005717 priv->channel = geo->bg[0].channel;
James Ketrenos43f66a62005-03-25 12:31:53 -06005718 }
James Ketrenosafbf30a2005-08-25 00:05:33 -05005719 break;
James Ketrenos43f66a62005-03-25 12:31:53 -06005720 }
5721
5722 network->channel = priv->channel;
5723 priv->config |= CFG_ADHOC_PERSIST;
5724 ipw_create_bssid(priv, network->bssid);
5725 network->ssid_len = priv->essid_len;
5726 memcpy(network->ssid, priv->essid, priv->essid_len);
5727 memset(&network->stats, 0, sizeof(network->stats));
5728 network->capability = WLAN_CAPABILITY_IBSS;
James Ketrenosea2b26e2005-08-24 21:25:16 -05005729 if (!(priv->config & CFG_PREAMBLE_LONG))
5730 network->capability |= WLAN_CAPABILITY_SHORT_PREAMBLE;
James Ketrenos43f66a62005-03-25 12:31:53 -06005731 if (priv->capability & CAP_PRIVACY_ON)
5732 network->capability |= WLAN_CAPABILITY_PRIVACY;
5733 network->rates_len = min(priv->rates.num_rates, MAX_RATES_LENGTH);
Jeff Garzik0edd5b42005-09-07 00:48:31 -04005734 memcpy(network->rates, priv->rates.supported_rates, network->rates_len);
James Ketrenos43f66a62005-03-25 12:31:53 -06005735 network->rates_ex_len = priv->rates.num_rates - network->rates_len;
Jeff Garzikbf794512005-07-31 13:07:26 -04005736 memcpy(network->rates_ex,
James Ketrenos43f66a62005-03-25 12:31:53 -06005737 &priv->rates.supported_rates[network->rates_len],
5738 network->rates_ex_len);
5739 network->last_scanned = 0;
5740 network->flags = 0;
5741 network->last_associate = 0;
5742 network->time_stamp[0] = 0;
5743 network->time_stamp[1] = 0;
Jeff Garzik0edd5b42005-09-07 00:48:31 -04005744 network->beacon_interval = 100; /* Default */
5745 network->listen_interval = 10; /* Default */
5746 network->atim_window = 0; /* Default */
James Ketrenos43f66a62005-03-25 12:31:53 -06005747 network->wpa_ie_len = 0;
5748 network->rsn_ie_len = 0;
James Ketrenos43f66a62005-03-25 12:31:53 -06005749}
5750
James Ketrenosb095c382005-08-24 22:04:42 -05005751static void ipw_send_tgi_tx_key(struct ipw_priv *priv, int type, int index)
5752{
5753 struct ipw_tgi_tx_key *key;
5754 struct host_cmd cmd = {
5755 .cmd = IPW_CMD_TGI_TX_KEY,
5756 .len = sizeof(*key)
5757 };
5758
5759 if (!(priv->ieee->sec.flags & (1 << index)))
5760 return;
5761
5762 key = (struct ipw_tgi_tx_key *)&cmd.param;
5763 key->key_id = index;
5764 memcpy(key->key, priv->ieee->sec.keys[index], SCM_TEMPORAL_KEY_LENGTH);
5765 key->security_type = type;
5766 key->station_index = 0; /* always 0 for BSS */
5767 key->flags = 0;
5768 /* 0 for new key; previous value of counter (after fatal error) */
5769 key->tx_counter[0] = 0;
5770 key->tx_counter[1] = 0;
5771
James Ketrenos9ddf84f2005-08-16 17:07:11 -05005772 ipw_send_cmd(priv, &cmd);
James Ketrenosb095c382005-08-24 22:04:42 -05005773}
5774
5775static void ipw_send_wep_keys(struct ipw_priv *priv, int type)
James Ketrenos43f66a62005-03-25 12:31:53 -06005776{
5777 struct ipw_wep_key *key;
5778 int i;
5779 struct host_cmd cmd = {
5780 .cmd = IPW_CMD_WEP_KEY,
5781 .len = sizeof(*key)
5782 };
5783
5784 key = (struct ipw_wep_key *)&cmd.param;
5785 key->cmd_id = DINO_CMD_WEP_KEY;
5786 key->seq_num = 0;
5787
James Ketrenosb095c382005-08-24 22:04:42 -05005788 /* Note: AES keys cannot be set for multiple times.
5789 * Only set it at the first time. */
Jeff Garzikbf794512005-07-31 13:07:26 -04005790 for (i = 0; i < 4; i++) {
James Ketrenosb095c382005-08-24 22:04:42 -05005791 key->key_index = i | type;
5792 if (!(priv->ieee->sec.flags & (1 << i))) {
James Ketrenos43f66a62005-03-25 12:31:53 -06005793 key->key_size = 0;
James Ketrenosb095c382005-08-24 22:04:42 -05005794 continue;
James Ketrenos43f66a62005-03-25 12:31:53 -06005795 }
5796
James Ketrenosb095c382005-08-24 22:04:42 -05005797 key->key_size = priv->ieee->sec.key_sizes[i];
5798 memcpy(key->key, priv->ieee->sec.keys[i], key->key_size);
5799
James Ketrenos9ddf84f2005-08-16 17:07:11 -05005800 ipw_send_cmd(priv, &cmd);
Jeff Garzikbf794512005-07-31 13:07:26 -04005801 }
James Ketrenos43f66a62005-03-25 12:31:53 -06005802}
5803
Zhu Yi1fbfea52005-08-05 17:22:56 +08005804static void ipw_set_hw_decrypt_unicast(struct ipw_priv *priv, int level)
5805{
5806 if (priv->ieee->host_encrypt)
5807 return;
5808
5809 switch (level) {
5810 case SEC_LEVEL_3:
5811 priv->sys_config.disable_unicast_decryption = 0;
5812 priv->ieee->host_decrypt = 0;
5813 break;
5814 case SEC_LEVEL_2:
5815 priv->sys_config.disable_unicast_decryption = 1;
5816 priv->ieee->host_decrypt = 1;
5817 break;
5818 case SEC_LEVEL_1:
5819 priv->sys_config.disable_unicast_decryption = 0;
5820 priv->ieee->host_decrypt = 0;
5821 break;
5822 case SEC_LEVEL_0:
5823 priv->sys_config.disable_unicast_decryption = 1;
5824 break;
5825 default:
5826 break;
5827 }
5828}
5829
5830static void ipw_set_hw_decrypt_multicast(struct ipw_priv *priv, int level)
5831{
5832 if (priv->ieee->host_encrypt)
5833 return;
5834
5835 switch (level) {
5836 case SEC_LEVEL_3:
5837 priv->sys_config.disable_multicast_decryption = 0;
5838 break;
5839 case SEC_LEVEL_2:
5840 priv->sys_config.disable_multicast_decryption = 1;
5841 break;
5842 case SEC_LEVEL_1:
5843 priv->sys_config.disable_multicast_decryption = 0;
5844 break;
5845 case SEC_LEVEL_0:
5846 priv->sys_config.disable_multicast_decryption = 1;
5847 break;
5848 default:
5849 break;
5850 }
5851}
5852
James Ketrenosb095c382005-08-24 22:04:42 -05005853static void ipw_set_hwcrypto_keys(struct ipw_priv *priv)
5854{
5855 switch (priv->ieee->sec.level) {
5856 case SEC_LEVEL_3:
Zhu Yid8bad6d2005-07-13 12:25:38 -05005857 if (priv->ieee->sec.flags & SEC_ACTIVE_KEY)
5858 ipw_send_tgi_tx_key(priv,
5859 DCT_FLAG_EXT_SECURITY_CCM,
5860 priv->ieee->sec.active_key);
James Ketrenosafbf30a2005-08-25 00:05:33 -05005861
Hong Liu567deaf2005-08-31 18:07:22 +08005862 if (!priv->ieee->host_mc_decrypt)
5863 ipw_send_wep_keys(priv, DCW_WEP_KEY_SEC_TYPE_CCM);
James Ketrenosb095c382005-08-24 22:04:42 -05005864 break;
5865 case SEC_LEVEL_2:
Zhu Yid8bad6d2005-07-13 12:25:38 -05005866 if (priv->ieee->sec.flags & SEC_ACTIVE_KEY)
5867 ipw_send_tgi_tx_key(priv,
5868 DCT_FLAG_EXT_SECURITY_TKIP,
5869 priv->ieee->sec.active_key);
James Ketrenosb095c382005-08-24 22:04:42 -05005870 break;
5871 case SEC_LEVEL_1:
5872 ipw_send_wep_keys(priv, DCW_WEP_KEY_SEC_TYPE_WEP);
Hong Liu29cb8432005-09-12 10:43:33 -05005873 ipw_set_hw_decrypt_unicast(priv, priv->ieee->sec.level);
5874 ipw_set_hw_decrypt_multicast(priv, priv->ieee->sec.level);
James Ketrenosb095c382005-08-24 22:04:42 -05005875 break;
5876 case SEC_LEVEL_0:
5877 default:
5878 break;
James Ketrenos43f66a62005-03-25 12:31:53 -06005879 }
5880}
5881
5882static void ipw_adhoc_check(void *data)
5883{
5884 struct ipw_priv *priv = data;
Jeff Garzikbf794512005-07-31 13:07:26 -04005885
James Ketrenosafbf30a2005-08-25 00:05:33 -05005886 if (priv->missed_adhoc_beacons++ > priv->disassociate_threshold &&
James Ketrenos43f66a62005-03-25 12:31:53 -06005887 !(priv->config & CFG_ADHOC_PERSIST)) {
James Ketrenosafbf30a2005-08-25 00:05:33 -05005888 IPW_DEBUG(IPW_DL_INFO | IPW_DL_NOTIF |
5889 IPW_DL_STATE | IPW_DL_ASSOC,
5890 "Missed beacon: %d - disassociate\n",
5891 priv->missed_adhoc_beacons);
James Ketrenos43f66a62005-03-25 12:31:53 -06005892 ipw_remove_current_network(priv);
5893 ipw_disassociate(priv);
5894 return;
5895 }
5896
Jeff Garzikbf794512005-07-31 13:07:26 -04005897 queue_delayed_work(priv->workqueue, &priv->adhoc_check,
James Ketrenos43f66a62005-03-25 12:31:53 -06005898 priv->assoc_request.beacon_interval);
5899}
5900
James Ketrenosc848d0a2005-08-24 21:56:24 -05005901static void ipw_bg_adhoc_check(void *data)
5902{
5903 struct ipw_priv *priv = data;
5904 down(&priv->sem);
5905 ipw_adhoc_check(data);
5906 up(&priv->sem);
5907}
5908
Brice Goglin0f52bf92005-12-01 01:41:46 -08005909#ifdef CONFIG_IPW2200_DEBUG
James Ketrenos43f66a62005-03-25 12:31:53 -06005910static void ipw_debug_config(struct ipw_priv *priv)
5911{
5912 IPW_DEBUG_INFO("Scan completed, no valid APs matched "
5913 "[CFG 0x%08X]\n", priv->config);
5914 if (priv->config & CFG_STATIC_CHANNEL)
Jeff Garzik0edd5b42005-09-07 00:48:31 -04005915 IPW_DEBUG_INFO("Channel locked to %d\n", priv->channel);
James Ketrenos43f66a62005-03-25 12:31:53 -06005916 else
5917 IPW_DEBUG_INFO("Channel unlocked.\n");
5918 if (priv->config & CFG_STATIC_ESSID)
Jeff Garzikbf794512005-07-31 13:07:26 -04005919 IPW_DEBUG_INFO("ESSID locked to '%s'\n",
Jeff Garzik0edd5b42005-09-07 00:48:31 -04005920 escape_essid(priv->essid, priv->essid_len));
James Ketrenos43f66a62005-03-25 12:31:53 -06005921 else
5922 IPW_DEBUG_INFO("ESSID unlocked.\n");
5923 if (priv->config & CFG_STATIC_BSSID)
James Ketrenosea2b26e2005-08-24 21:25:16 -05005924 IPW_DEBUG_INFO("BSSID locked to " MAC_FMT "\n",
5925 MAC_ARG(priv->bssid));
James Ketrenos43f66a62005-03-25 12:31:53 -06005926 else
5927 IPW_DEBUG_INFO("BSSID unlocked.\n");
5928 if (priv->capability & CAP_PRIVACY_ON)
5929 IPW_DEBUG_INFO("PRIVACY on\n");
5930 else
5931 IPW_DEBUG_INFO("PRIVACY off\n");
5932 IPW_DEBUG_INFO("RATE MASK: 0x%08X\n", priv->rates_mask);
5933}
5934#else
Jiri Benc8d45ff72005-08-25 20:09:39 -04005935#define ipw_debug_config(x) do {} while (0)
James Ketrenos43f66a62005-03-25 12:31:53 -06005936#endif
5937
Arjan van de Ven858119e2006-01-14 13:20:43 -08005938static void ipw_set_fixed_rate(struct ipw_priv *priv, int mode)
James Ketrenos43f66a62005-03-25 12:31:53 -06005939{
5940 /* TODO: Verify that this works... */
5941 struct ipw_fixed_rate fr = {
5942 .tx_rates = priv->rates_mask
5943 };
5944 u32 reg;
5945 u16 mask = 0;
5946
Jeff Garzikbf794512005-07-31 13:07:26 -04005947 /* Identify 'current FW band' and match it with the fixed
James Ketrenos43f66a62005-03-25 12:31:53 -06005948 * Tx rates */
Jeff Garzikbf794512005-07-31 13:07:26 -04005949
James Ketrenos43f66a62005-03-25 12:31:53 -06005950 switch (priv->ieee->freq_band) {
Jeff Garzik0edd5b42005-09-07 00:48:31 -04005951 case IEEE80211_52GHZ_BAND: /* A only */
James Ketrenos43f66a62005-03-25 12:31:53 -06005952 /* IEEE_A */
5953 if (priv->rates_mask & ~IEEE80211_OFDM_RATES_MASK) {
5954 /* Invalid fixed rate mask */
James Ketrenosea2b26e2005-08-24 21:25:16 -05005955 IPW_DEBUG_WX
5956 ("invalid fixed rate mask in ipw_set_fixed_rate\n");
James Ketrenos43f66a62005-03-25 12:31:53 -06005957 fr.tx_rates = 0;
5958 break;
5959 }
Jeff Garzikbf794512005-07-31 13:07:26 -04005960
James Ketrenos43f66a62005-03-25 12:31:53 -06005961 fr.tx_rates >>= IEEE80211_OFDM_SHIFT_MASK_A;
5962 break;
5963
Jeff Garzik0edd5b42005-09-07 00:48:31 -04005964 default: /* 2.4Ghz or Mixed */
James Ketrenos43f66a62005-03-25 12:31:53 -06005965 /* IEEE_B */
James Ketrenosb095c382005-08-24 22:04:42 -05005966 if (mode == IEEE_B) {
James Ketrenos43f66a62005-03-25 12:31:53 -06005967 if (fr.tx_rates & ~IEEE80211_CCK_RATES_MASK) {
5968 /* Invalid fixed rate mask */
James Ketrenosea2b26e2005-08-24 21:25:16 -05005969 IPW_DEBUG_WX
5970 ("invalid fixed rate mask in ipw_set_fixed_rate\n");
James Ketrenos43f66a62005-03-25 12:31:53 -06005971 fr.tx_rates = 0;
5972 }
5973 break;
Jeff Garzikbf794512005-07-31 13:07:26 -04005974 }
James Ketrenos43f66a62005-03-25 12:31:53 -06005975
5976 /* IEEE_G */
5977 if (fr.tx_rates & ~(IEEE80211_CCK_RATES_MASK |
5978 IEEE80211_OFDM_RATES_MASK)) {
5979 /* Invalid fixed rate mask */
James Ketrenosea2b26e2005-08-24 21:25:16 -05005980 IPW_DEBUG_WX
5981 ("invalid fixed rate mask in ipw_set_fixed_rate\n");
James Ketrenos43f66a62005-03-25 12:31:53 -06005982 fr.tx_rates = 0;
5983 break;
5984 }
5985
5986 if (IEEE80211_OFDM_RATE_6MB_MASK & fr.tx_rates) {
5987 mask |= (IEEE80211_OFDM_RATE_6MB_MASK >> 1);
5988 fr.tx_rates &= ~IEEE80211_OFDM_RATE_6MB_MASK;
5989 }
Jeff Garzikbf794512005-07-31 13:07:26 -04005990
James Ketrenos43f66a62005-03-25 12:31:53 -06005991 if (IEEE80211_OFDM_RATE_9MB_MASK & fr.tx_rates) {
5992 mask |= (IEEE80211_OFDM_RATE_9MB_MASK >> 1);
5993 fr.tx_rates &= ~IEEE80211_OFDM_RATE_9MB_MASK;
5994 }
Jeff Garzikbf794512005-07-31 13:07:26 -04005995
James Ketrenos43f66a62005-03-25 12:31:53 -06005996 if (IEEE80211_OFDM_RATE_12MB_MASK & fr.tx_rates) {
5997 mask |= (IEEE80211_OFDM_RATE_12MB_MASK >> 1);
5998 fr.tx_rates &= ~IEEE80211_OFDM_RATE_12MB_MASK;
5999 }
Jeff Garzikbf794512005-07-31 13:07:26 -04006000
James Ketrenos43f66a62005-03-25 12:31:53 -06006001 fr.tx_rates |= mask;
6002 break;
6003 }
6004
6005 reg = ipw_read32(priv, IPW_MEM_FIXED_OVERRIDE);
Jeff Garzik0edd5b42005-09-07 00:48:31 -04006006 ipw_write_reg32(priv, reg, *(u32 *) & fr);
James Ketrenos43f66a62005-03-25 12:31:53 -06006007}
6008
James Ketrenosea2b26e2005-08-24 21:25:16 -05006009static void ipw_abort_scan(struct ipw_priv *priv)
6010{
6011 int err;
6012
6013 if (priv->status & STATUS_SCAN_ABORTING) {
6014 IPW_DEBUG_HC("Ignoring concurrent scan abort request.\n");
6015 return;
6016 }
6017 priv->status |= STATUS_SCAN_ABORTING;
6018
6019 err = ipw_send_scan_abort(priv);
6020 if (err)
6021 IPW_DEBUG_HC("Request to abort scan failed.\n");
6022}
6023
James Ketrenosafbf30a2005-08-25 00:05:33 -05006024static void ipw_add_scan_channels(struct ipw_priv *priv,
6025 struct ipw_scan_request_ext *scan,
6026 int scan_type)
6027{
6028 int channel_index = 0;
6029 const struct ieee80211_geo *geo;
6030 int i;
6031
Liu Hong1fe0adb2005-08-19 09:33:10 -05006032 geo = ipw_get_geo(priv->ieee);
James Ketrenosafbf30a2005-08-25 00:05:33 -05006033
6034 if (priv->ieee->freq_band & IEEE80211_52GHZ_BAND) {
6035 int start = channel_index;
6036 for (i = 0; i < geo->a_channels; i++) {
6037 if ((priv->status & STATUS_ASSOCIATED) &&
6038 geo->a[i].channel == priv->channel)
6039 continue;
6040 channel_index++;
6041 scan->channels_list[channel_index] = geo->a[i].channel;
Liu Hong1fe0adb2005-08-19 09:33:10 -05006042 ipw_set_scan_type(scan, channel_index,
6043 geo->a[i].
6044 flags & IEEE80211_CH_PASSIVE_ONLY ?
6045 IPW_SCAN_PASSIVE_FULL_DWELL_SCAN :
6046 scan_type);
James Ketrenosafbf30a2005-08-25 00:05:33 -05006047 }
6048
6049 if (start != channel_index) {
6050 scan->channels_list[start] = (u8) (IPW_A_MODE << 6) |
6051 (channel_index - start);
6052 channel_index++;
6053 }
6054 }
6055
6056 if (priv->ieee->freq_band & IEEE80211_24GHZ_BAND) {
6057 int start = channel_index;
6058 if (priv->config & CFG_SPEED_SCAN) {
Liu Hong1fe0adb2005-08-19 09:33:10 -05006059 int index;
James Ketrenosafbf30a2005-08-25 00:05:33 -05006060 u8 channels[IEEE80211_24GHZ_CHANNELS] = {
6061 /* nop out the list */
6062 [0] = 0
6063 };
6064
6065 u8 channel;
6066 while (channel_index < IPW_SCAN_CHANNELS) {
6067 channel =
6068 priv->speed_scan[priv->speed_scan_pos];
6069 if (channel == 0) {
6070 priv->speed_scan_pos = 0;
6071 channel = priv->speed_scan[0];
6072 }
6073 if ((priv->status & STATUS_ASSOCIATED) &&
6074 channel == priv->channel) {
6075 priv->speed_scan_pos++;
6076 continue;
6077 }
6078
6079 /* If this channel has already been
6080 * added in scan, break from loop
6081 * and this will be the first channel
6082 * in the next scan.
6083 */
6084 if (channels[channel - 1] != 0)
6085 break;
6086
6087 channels[channel - 1] = 1;
6088 priv->speed_scan_pos++;
6089 channel_index++;
6090 scan->channels_list[channel_index] = channel;
Liu Hong1fe0adb2005-08-19 09:33:10 -05006091 index =
6092 ipw_channel_to_index(priv->ieee, channel);
James Ketrenosafbf30a2005-08-25 00:05:33 -05006093 ipw_set_scan_type(scan, channel_index,
Liu Hong1fe0adb2005-08-19 09:33:10 -05006094 geo->bg[index].
6095 flags &
6096 IEEE80211_CH_PASSIVE_ONLY ?
6097 IPW_SCAN_PASSIVE_FULL_DWELL_SCAN
6098 : scan_type);
James Ketrenosafbf30a2005-08-25 00:05:33 -05006099 }
6100 } else {
6101 for (i = 0; i < geo->bg_channels; i++) {
6102 if ((priv->status & STATUS_ASSOCIATED) &&
6103 geo->bg[i].channel == priv->channel)
6104 continue;
6105 channel_index++;
6106 scan->channels_list[channel_index] =
6107 geo->bg[i].channel;
6108 ipw_set_scan_type(scan, channel_index,
Liu Hong1fe0adb2005-08-19 09:33:10 -05006109 geo->bg[i].
6110 flags &
6111 IEEE80211_CH_PASSIVE_ONLY ?
6112 IPW_SCAN_PASSIVE_FULL_DWELL_SCAN
6113 : scan_type);
James Ketrenosafbf30a2005-08-25 00:05:33 -05006114 }
6115 }
6116
6117 if (start != channel_index) {
6118 scan->channels_list[start] = (u8) (IPW_B_MODE << 6) |
6119 (channel_index - start);
6120 }
6121 }
6122}
6123
James Ketrenosea2b26e2005-08-24 21:25:16 -05006124static int ipw_request_scan(struct ipw_priv *priv)
6125{
6126 struct ipw_scan_request_ext scan;
James Ketrenosafbf30a2005-08-25 00:05:33 -05006127 int err = 0, scan_type;
6128
6129 if (!(priv->status & STATUS_INIT) ||
6130 (priv->status & STATUS_EXIT_PENDING))
6131 return 0;
James Ketrenosea2b26e2005-08-24 21:25:16 -05006132
James Ketrenosb095c382005-08-24 22:04:42 -05006133 down(&priv->sem);
6134
James Ketrenosea2b26e2005-08-24 21:25:16 -05006135 if (priv->status & STATUS_SCANNING) {
James Ketrenosa613bff2005-08-24 21:43:11 -05006136 IPW_DEBUG_HC("Concurrent scan requested. Ignoring.\n");
James Ketrenosea2b26e2005-08-24 21:25:16 -05006137 priv->status |= STATUS_SCAN_PENDING;
James Ketrenosb095c382005-08-24 22:04:42 -05006138 goto done;
James Ketrenosea2b26e2005-08-24 21:25:16 -05006139 }
6140
James Ketrenosafbf30a2005-08-25 00:05:33 -05006141 if (!(priv->status & STATUS_SCAN_FORCED) &&
6142 priv->status & STATUS_SCAN_ABORTING) {
James Ketrenosea2b26e2005-08-24 21:25:16 -05006143 IPW_DEBUG_HC("Scan request while abort pending. Queuing.\n");
6144 priv->status |= STATUS_SCAN_PENDING;
James Ketrenosb095c382005-08-24 22:04:42 -05006145 goto done;
James Ketrenosea2b26e2005-08-24 21:25:16 -05006146 }
6147
6148 if (priv->status & STATUS_RF_KILL_MASK) {
6149 IPW_DEBUG_HC("Aborting scan due to RF Kill activation\n");
6150 priv->status |= STATUS_SCAN_PENDING;
James Ketrenosb095c382005-08-24 22:04:42 -05006151 goto done;
James Ketrenosea2b26e2005-08-24 21:25:16 -05006152 }
6153
6154 memset(&scan, 0, sizeof(scan));
6155
James Ketrenosb095c382005-08-24 22:04:42 -05006156 if (priv->config & CFG_SPEED_SCAN)
6157 scan.dwell_time[IPW_SCAN_ACTIVE_BROADCAST_SCAN] =
6158 cpu_to_le16(30);
6159 else
6160 scan.dwell_time[IPW_SCAN_ACTIVE_BROADCAST_SCAN] =
6161 cpu_to_le16(20);
6162
James Ketrenosa613bff2005-08-24 21:43:11 -05006163 scan.dwell_time[IPW_SCAN_ACTIVE_BROADCAST_AND_DIRECT_SCAN] =
6164 cpu_to_le16(20);
Liu Hong1fe0adb2005-08-19 09:33:10 -05006165 scan.dwell_time[IPW_SCAN_PASSIVE_FULL_DWELL_SCAN] = cpu_to_le16(120);
James Ketrenosea2b26e2005-08-24 21:25:16 -05006166
James Ketrenosa613bff2005-08-24 21:43:11 -05006167 scan.full_scan_index = cpu_to_le32(ieee80211_get_scans(priv->ieee));
James Ketrenosea2b26e2005-08-24 21:25:16 -05006168
James Ketrenosb095c382005-08-24 22:04:42 -05006169#ifdef CONFIG_IPW2200_MONITOR
James Ketrenosea2b26e2005-08-24 21:25:16 -05006170 if (priv->ieee->iw_mode == IW_MODE_MONITOR) {
James Ketrenosafbf30a2005-08-25 00:05:33 -05006171 u8 channel;
James Ketrenosb095c382005-08-24 22:04:42 -05006172 u8 band = 0;
James Ketrenosea2b26e2005-08-24 21:25:16 -05006173
Liu Hong1fe0adb2005-08-19 09:33:10 -05006174 switch (ipw_is_valid_channel(priv->ieee, priv->channel)) {
James Ketrenosb095c382005-08-24 22:04:42 -05006175 case IEEE80211_52GHZ_BAND:
James Ketrenosea2b26e2005-08-24 21:25:16 -05006176 band = (u8) (IPW_A_MODE << 6) | 1;
James Ketrenosb095c382005-08-24 22:04:42 -05006177 channel = priv->channel;
6178 break;
James Ketrenosea2b26e2005-08-24 21:25:16 -05006179
James Ketrenosb095c382005-08-24 22:04:42 -05006180 case IEEE80211_24GHZ_BAND:
James Ketrenosea2b26e2005-08-24 21:25:16 -05006181 band = (u8) (IPW_B_MODE << 6) | 1;
James Ketrenosb095c382005-08-24 22:04:42 -05006182 channel = priv->channel;
6183 break;
James Ketrenosea2b26e2005-08-24 21:25:16 -05006184
James Ketrenosb095c382005-08-24 22:04:42 -05006185 default:
James Ketrenosea2b26e2005-08-24 21:25:16 -05006186 band = (u8) (IPW_B_MODE << 6) | 1;
6187 channel = 9;
James Ketrenosb095c382005-08-24 22:04:42 -05006188 break;
James Ketrenosea2b26e2005-08-24 21:25:16 -05006189 }
6190
James Ketrenosb095c382005-08-24 22:04:42 -05006191 scan.channels_list[0] = band;
6192 scan.channels_list[1] = channel;
6193 ipw_set_scan_type(&scan, 1, IPW_SCAN_PASSIVE_FULL_DWELL_SCAN);
James Ketrenosea2b26e2005-08-24 21:25:16 -05006194
James Ketrenosb095c382005-08-24 22:04:42 -05006195 /* NOTE: The card will sit on this channel for this time
6196 * period. Scan aborts are timing sensitive and frequently
6197 * result in firmware restarts. As such, it is best to
6198 * set a small dwell_time here and just keep re-issuing
6199 * scans. Otherwise fast channel hopping will not actually
6200 * hop channels.
6201 *
6202 * TODO: Move SPEED SCAN support to all modes and bands */
James Ketrenosa613bff2005-08-24 21:43:11 -05006203 scan.dwell_time[IPW_SCAN_PASSIVE_FULL_DWELL_SCAN] =
6204 cpu_to_le16(2000);
James Ketrenosea2b26e2005-08-24 21:25:16 -05006205 } else {
James Ketrenosb095c382005-08-24 22:04:42 -05006206#endif /* CONFIG_IPW2200_MONITOR */
6207 /* If we are roaming, then make this a directed scan for the
6208 * current network. Otherwise, ensure that every other scan
6209 * is a fast channel hop scan */
6210 if ((priv->status & STATUS_ROAMING)
6211 || (!(priv->status & STATUS_ASSOCIATED)
6212 && (priv->config & CFG_STATIC_ESSID)
6213 && (le32_to_cpu(scan.full_scan_index) % 2))) {
James Ketrenosea2b26e2005-08-24 21:25:16 -05006214 err = ipw_send_ssid(priv, priv->essid, priv->essid_len);
6215 if (err) {
James Ketrenosb095c382005-08-24 22:04:42 -05006216 IPW_DEBUG_HC("Attempt to send SSID command "
6217 "failed.\n");
6218 goto done;
James Ketrenosea2b26e2005-08-24 21:25:16 -05006219 }
6220
6221 scan_type = IPW_SCAN_ACTIVE_BROADCAST_AND_DIRECT_SCAN;
James Ketrenosafbf30a2005-08-25 00:05:33 -05006222 } else
James Ketrenosea2b26e2005-08-24 21:25:16 -05006223 scan_type = IPW_SCAN_ACTIVE_BROADCAST_SCAN;
James Ketrenosea2b26e2005-08-24 21:25:16 -05006224
James Ketrenosafbf30a2005-08-25 00:05:33 -05006225 ipw_add_scan_channels(priv, &scan, scan_type);
James Ketrenosb095c382005-08-24 22:04:42 -05006226#ifdef CONFIG_IPW2200_MONITOR
James Ketrenosea2b26e2005-08-24 21:25:16 -05006227 }
6228#endif
6229
6230 err = ipw_send_scan_request_ext(priv, &scan);
6231 if (err) {
6232 IPW_DEBUG_HC("Sending scan command failed: %08X\n", err);
James Ketrenosb095c382005-08-24 22:04:42 -05006233 goto done;
James Ketrenosea2b26e2005-08-24 21:25:16 -05006234 }
6235
6236 priv->status |= STATUS_SCANNING;
6237 priv->status &= ~STATUS_SCAN_PENDING;
James Ketrenosafbf30a2005-08-25 00:05:33 -05006238 queue_delayed_work(priv->workqueue, &priv->scan_check,
6239 IPW_SCAN_CHECK_WATCHDOG);
James Ketrenosb095c382005-08-24 22:04:42 -05006240 done:
James Ketrenosc848d0a2005-08-24 21:56:24 -05006241 up(&priv->sem);
James Ketrenosb095c382005-08-24 22:04:42 -05006242 return err;
James Ketrenosc848d0a2005-08-24 21:56:24 -05006243}
6244
6245static void ipw_bg_abort_scan(void *data)
6246{
6247 struct ipw_priv *priv = data;
6248 down(&priv->sem);
6249 ipw_abort_scan(data);
6250 up(&priv->sem);
6251}
6252
James Ketrenosea2b26e2005-08-24 21:25:16 -05006253static int ipw_wpa_enable(struct ipw_priv *priv, int value)
6254{
James Ketrenosb095c382005-08-24 22:04:42 -05006255 /* This is called when wpa_supplicant loads and closes the driver
6256 * interface. */
Hong Liucdd1fa12005-08-31 18:14:27 +08006257 priv->ieee->wpa_enabled = value;
James Ketrenosb095c382005-08-24 22:04:42 -05006258 return 0;
James Ketrenosea2b26e2005-08-24 21:25:16 -05006259}
6260
James Ketrenosea2b26e2005-08-24 21:25:16 -05006261static int ipw_wpa_set_auth_algs(struct ipw_priv *priv, int value)
6262{
6263 struct ieee80211_device *ieee = priv->ieee;
6264 struct ieee80211_security sec = {
6265 .flags = SEC_AUTH_MODE,
6266 };
6267 int ret = 0;
6268
James Ketrenosafbf30a2005-08-25 00:05:33 -05006269 if (value & IW_AUTH_ALG_SHARED_KEY) {
James Ketrenosea2b26e2005-08-24 21:25:16 -05006270 sec.auth_mode = WLAN_AUTH_SHARED_KEY;
6271 ieee->open_wep = 0;
James Ketrenosafbf30a2005-08-25 00:05:33 -05006272 } else if (value & IW_AUTH_ALG_OPEN_SYSTEM) {
James Ketrenosea2b26e2005-08-24 21:25:16 -05006273 sec.auth_mode = WLAN_AUTH_OPEN;
6274 ieee->open_wep = 1;
Zhu Yi3e234b42006-01-24 16:36:52 +08006275 } else if (value & IW_AUTH_ALG_LEAP) {
6276 sec.auth_mode = WLAN_AUTH_LEAP;
6277 ieee->open_wep = 1;
James Ketrenosafbf30a2005-08-25 00:05:33 -05006278 } else
6279 return -EINVAL;
James Ketrenosea2b26e2005-08-24 21:25:16 -05006280
6281 if (ieee->set_security)
6282 ieee->set_security(ieee->dev, &sec);
6283 else
6284 ret = -EOPNOTSUPP;
6285
6286 return ret;
6287}
6288
Adrian Bunka73e22b2006-01-21 01:39:42 +01006289static void ipw_wpa_assoc_frame(struct ipw_priv *priv, char *wpa_ie,
6290 int wpa_ie_len)
James Ketrenosafbf30a2005-08-25 00:05:33 -05006291{
6292 /* make sure WPA is enabled */
6293 ipw_wpa_enable(priv, 1);
6294
6295 ipw_disassociate(priv);
6296}
6297
6298static int ipw_set_rsn_capa(struct ipw_priv *priv,
6299 char *capabilities, int length)
6300{
6301 struct host_cmd cmd = {
6302 .cmd = IPW_CMD_RSN_CAPABILITIES,
6303 .len = length,
6304 };
6305
6306 IPW_DEBUG_HC("HOST_CMD_RSN_CAPABILITIES\n");
6307
6308 memcpy(cmd.param, capabilities, length);
James Ketrenos9ddf84f2005-08-16 17:07:11 -05006309 return ipw_send_cmd(priv, &cmd);
James Ketrenosafbf30a2005-08-25 00:05:33 -05006310}
6311
James Ketrenosafbf30a2005-08-25 00:05:33 -05006312/*
6313 * WE-18 support
6314 */
6315
6316/* SIOCSIWGENIE */
6317static int ipw_wx_set_genie(struct net_device *dev,
6318 struct iw_request_info *info,
6319 union iwreq_data *wrqu, char *extra)
6320{
6321 struct ipw_priv *priv = ieee80211_priv(dev);
6322 struct ieee80211_device *ieee = priv->ieee;
6323 u8 *buf;
6324 int err = 0;
6325
6326 if (wrqu->data.length > MAX_WPA_IE_LEN ||
6327 (wrqu->data.length && extra == NULL))
6328 return -EINVAL;
6329
6330 //down(&priv->sem);
6331
6332 //if (!ieee->wpa_enabled) {
6333 // err = -EOPNOTSUPP;
6334 // goto out;
6335 //}
6336
6337 if (wrqu->data.length) {
6338 buf = kmalloc(wrqu->data.length, GFP_KERNEL);
6339 if (buf == NULL) {
6340 err = -ENOMEM;
6341 goto out;
6342 }
6343
6344 memcpy(buf, extra, wrqu->data.length);
6345 kfree(ieee->wpa_ie);
6346 ieee->wpa_ie = buf;
6347 ieee->wpa_ie_len = wrqu->data.length;
6348 } else {
6349 kfree(ieee->wpa_ie);
6350 ieee->wpa_ie = NULL;
6351 ieee->wpa_ie_len = 0;
6352 }
6353
6354 ipw_wpa_assoc_frame(priv, ieee->wpa_ie, ieee->wpa_ie_len);
6355 out:
6356 //up(&priv->sem);
6357 return err;
6358}
6359
6360/* SIOCGIWGENIE */
6361static int ipw_wx_get_genie(struct net_device *dev,
6362 struct iw_request_info *info,
6363 union iwreq_data *wrqu, char *extra)
6364{
6365 struct ipw_priv *priv = ieee80211_priv(dev);
6366 struct ieee80211_device *ieee = priv->ieee;
6367 int err = 0;
6368
6369 //down(&priv->sem);
6370
6371 //if (!ieee->wpa_enabled) {
6372 // err = -EOPNOTSUPP;
6373 // goto out;
6374 //}
6375
6376 if (ieee->wpa_ie_len == 0 || ieee->wpa_ie == NULL) {
6377 wrqu->data.length = 0;
6378 goto out;
6379 }
6380
6381 if (wrqu->data.length < ieee->wpa_ie_len) {
6382 err = -E2BIG;
6383 goto out;
6384 }
6385
6386 wrqu->data.length = ieee->wpa_ie_len;
6387 memcpy(extra, ieee->wpa_ie, ieee->wpa_ie_len);
6388
6389 out:
6390 //up(&priv->sem);
6391 return err;
6392}
6393
Zhu Yi1fbfea52005-08-05 17:22:56 +08006394static int wext_cipher2level(int cipher)
6395{
6396 switch (cipher) {
6397 case IW_AUTH_CIPHER_NONE:
6398 return SEC_LEVEL_0;
6399 case IW_AUTH_CIPHER_WEP40:
6400 case IW_AUTH_CIPHER_WEP104:
6401 return SEC_LEVEL_1;
6402 case IW_AUTH_CIPHER_TKIP:
6403 return SEC_LEVEL_2;
6404 case IW_AUTH_CIPHER_CCMP:
6405 return SEC_LEVEL_3;
6406 default:
6407 return -1;
6408 }
6409}
6410
James Ketrenosafbf30a2005-08-25 00:05:33 -05006411/* SIOCSIWAUTH */
6412static int ipw_wx_set_auth(struct net_device *dev,
6413 struct iw_request_info *info,
6414 union iwreq_data *wrqu, char *extra)
6415{
6416 struct ipw_priv *priv = ieee80211_priv(dev);
6417 struct ieee80211_device *ieee = priv->ieee;
6418 struct iw_param *param = &wrqu->param;
6419 struct ieee80211_crypt_data *crypt;
6420 unsigned long flags;
6421 int ret = 0;
6422
6423 switch (param->flags & IW_AUTH_INDEX) {
6424 case IW_AUTH_WPA_VERSION:
Zhu Yi1fbfea52005-08-05 17:22:56 +08006425 break;
James Ketrenosafbf30a2005-08-25 00:05:33 -05006426 case IW_AUTH_CIPHER_PAIRWISE:
Zhu Yi1fbfea52005-08-05 17:22:56 +08006427 ipw_set_hw_decrypt_unicast(priv,
6428 wext_cipher2level(param->value));
6429 break;
James Ketrenosafbf30a2005-08-25 00:05:33 -05006430 case IW_AUTH_CIPHER_GROUP:
Zhu Yi1fbfea52005-08-05 17:22:56 +08006431 ipw_set_hw_decrypt_multicast(priv,
6432 wext_cipher2level(param->value));
6433 break;
James Ketrenosafbf30a2005-08-25 00:05:33 -05006434 case IW_AUTH_KEY_MGMT:
6435 /*
6436 * ipw2200 does not use these parameters
6437 */
6438 break;
6439
6440 case IW_AUTH_TKIP_COUNTERMEASURES:
6441 crypt = priv->ieee->crypt[priv->ieee->tx_keyidx];
James Ketrenos991d1cc2005-10-13 09:26:48 +00006442 if (!crypt || !crypt->ops->set_flags || !crypt->ops->get_flags)
James Ketrenosafbf30a2005-08-25 00:05:33 -05006443 break;
James Ketrenosafbf30a2005-08-25 00:05:33 -05006444
6445 flags = crypt->ops->get_flags(crypt->priv);
6446
6447 if (param->value)
6448 flags |= IEEE80211_CRYPTO_TKIP_COUNTERMEASURES;
6449 else
6450 flags &= ~IEEE80211_CRYPTO_TKIP_COUNTERMEASURES;
6451
6452 crypt->ops->set_flags(flags, crypt->priv);
6453
6454 break;
6455
6456 case IW_AUTH_DROP_UNENCRYPTED:{
6457 /* HACK:
6458 *
6459 * wpa_supplicant calls set_wpa_enabled when the driver
6460 * is loaded and unloaded, regardless of if WPA is being
6461 * used. No other calls are made which can be used to
6462 * determine if encryption will be used or not prior to
6463 * association being expected. If encryption is not being
6464 * used, drop_unencrypted is set to false, else true -- we
6465 * can use this to determine if the CAP_PRIVACY_ON bit should
6466 * be set.
6467 */
6468 struct ieee80211_security sec = {
6469 .flags = SEC_ENABLED,
6470 .enabled = param->value,
6471 };
6472 priv->ieee->drop_unencrypted = param->value;
6473 /* We only change SEC_LEVEL for open mode. Others
6474 * are set by ipw_wpa_set_encryption.
6475 */
6476 if (!param->value) {
6477 sec.flags |= SEC_LEVEL;
6478 sec.level = SEC_LEVEL_0;
6479 } else {
6480 sec.flags |= SEC_LEVEL;
6481 sec.level = SEC_LEVEL_1;
6482 }
6483 if (priv->ieee->set_security)
6484 priv->ieee->set_security(priv->ieee->dev, &sec);
6485 break;
6486 }
6487
6488 case IW_AUTH_80211_AUTH_ALG:
6489 ret = ipw_wpa_set_auth_algs(priv, param->value);
6490 break;
6491
6492 case IW_AUTH_WPA_ENABLED:
6493 ret = ipw_wpa_enable(priv, param->value);
6494 break;
6495
6496 case IW_AUTH_RX_UNENCRYPTED_EAPOL:
6497 ieee->ieee802_1x = param->value;
6498 break;
6499
6500 //case IW_AUTH_ROAMING_CONTROL:
6501 case IW_AUTH_PRIVACY_INVOKED:
6502 ieee->privacy_invoked = param->value;
6503 break;
6504
6505 default:
6506 return -EOPNOTSUPP;
6507 }
6508 return ret;
6509}
6510
6511/* SIOCGIWAUTH */
6512static int ipw_wx_get_auth(struct net_device *dev,
6513 struct iw_request_info *info,
6514 union iwreq_data *wrqu, char *extra)
6515{
6516 struct ipw_priv *priv = ieee80211_priv(dev);
6517 struct ieee80211_device *ieee = priv->ieee;
6518 struct ieee80211_crypt_data *crypt;
6519 struct iw_param *param = &wrqu->param;
6520 int ret = 0;
6521
6522 switch (param->flags & IW_AUTH_INDEX) {
6523 case IW_AUTH_WPA_VERSION:
6524 case IW_AUTH_CIPHER_PAIRWISE:
6525 case IW_AUTH_CIPHER_GROUP:
6526 case IW_AUTH_KEY_MGMT:
6527 /*
6528 * wpa_supplicant will control these internally
6529 */
6530 ret = -EOPNOTSUPP;
6531 break;
6532
6533 case IW_AUTH_TKIP_COUNTERMEASURES:
6534 crypt = priv->ieee->crypt[priv->ieee->tx_keyidx];
James Ketrenos991d1cc2005-10-13 09:26:48 +00006535 if (!crypt || !crypt->ops->get_flags)
James Ketrenosafbf30a2005-08-25 00:05:33 -05006536 break;
James Ketrenosafbf30a2005-08-25 00:05:33 -05006537
6538 param->value = (crypt->ops->get_flags(crypt->priv) &
6539 IEEE80211_CRYPTO_TKIP_COUNTERMEASURES) ? 1 : 0;
6540
6541 break;
6542
6543 case IW_AUTH_DROP_UNENCRYPTED:
6544 param->value = ieee->drop_unencrypted;
6545 break;
6546
6547 case IW_AUTH_80211_AUTH_ALG:
6548 param->value = ieee->sec.auth_mode;
6549 break;
6550
6551 case IW_AUTH_WPA_ENABLED:
6552 param->value = ieee->wpa_enabled;
6553 break;
6554
6555 case IW_AUTH_RX_UNENCRYPTED_EAPOL:
6556 param->value = ieee->ieee802_1x;
6557 break;
6558
6559 case IW_AUTH_ROAMING_CONTROL:
6560 case IW_AUTH_PRIVACY_INVOKED:
6561 param->value = ieee->privacy_invoked;
6562 break;
6563
6564 default:
6565 return -EOPNOTSUPP;
6566 }
6567 return 0;
6568}
6569
6570/* SIOCSIWENCODEEXT */
6571static int ipw_wx_set_encodeext(struct net_device *dev,
6572 struct iw_request_info *info,
6573 union iwreq_data *wrqu, char *extra)
6574{
6575 struct ipw_priv *priv = ieee80211_priv(dev);
6576 struct iw_encode_ext *ext = (struct iw_encode_ext *)extra;
6577
6578 if (hwcrypto) {
James Ketrenosafbf30a2005-08-25 00:05:33 -05006579 if (ext->alg == IW_ENCODE_ALG_TKIP) {
Hong Liu567deaf2005-08-31 18:07:22 +08006580 /* IPW HW can't build TKIP MIC,
6581 host decryption still needed */
6582 if (ext->ext_flags & IW_ENCODE_EXT_GROUP_KEY)
6583 priv->ieee->host_mc_decrypt = 1;
6584 else {
6585 priv->ieee->host_encrypt = 0;
6586 priv->ieee->host_encrypt_msdu = 1;
6587 priv->ieee->host_decrypt = 1;
6588 }
James Ketrenosafbf30a2005-08-25 00:05:33 -05006589 } else {
6590 priv->ieee->host_encrypt = 0;
6591 priv->ieee->host_encrypt_msdu = 0;
6592 priv->ieee->host_decrypt = 0;
Hong Liu567deaf2005-08-31 18:07:22 +08006593 priv->ieee->host_mc_decrypt = 0;
James Ketrenosafbf30a2005-08-25 00:05:33 -05006594 }
6595 }
6596
6597 return ieee80211_wx_set_encodeext(priv->ieee, info, wrqu, extra);
6598}
6599
6600/* SIOCGIWENCODEEXT */
6601static int ipw_wx_get_encodeext(struct net_device *dev,
6602 struct iw_request_info *info,
6603 union iwreq_data *wrqu, char *extra)
6604{
6605 struct ipw_priv *priv = ieee80211_priv(dev);
6606 return ieee80211_wx_get_encodeext(priv->ieee, info, wrqu, extra);
6607}
6608
6609/* SIOCSIWMLME */
6610static int ipw_wx_set_mlme(struct net_device *dev,
6611 struct iw_request_info *info,
6612 union iwreq_data *wrqu, char *extra)
6613{
6614 struct ipw_priv *priv = ieee80211_priv(dev);
6615 struct iw_mlme *mlme = (struct iw_mlme *)extra;
6616 u16 reason;
6617
6618 reason = cpu_to_le16(mlme->reason_code);
6619
6620 switch (mlme->cmd) {
6621 case IW_MLME_DEAUTH:
6622 // silently ignore
6623 break;
6624
6625 case IW_MLME_DISASSOC:
6626 ipw_disassociate(priv);
6627 break;
6628
6629 default:
6630 return -EOPNOTSUPP;
6631 }
6632 return 0;
6633}
James Ketrenosea2b26e2005-08-24 21:25:16 -05006634
James Ketrenosb095c382005-08-24 22:04:42 -05006635#ifdef CONFIG_IPW_QOS
6636
6637/* QoS */
6638/*
6639* get the modulation type of the current network or
6640* the card current mode
6641*/
6642u8 ipw_qos_current_mode(struct ipw_priv * priv)
6643{
6644 u8 mode = 0;
6645
6646 if (priv->status & STATUS_ASSOCIATED) {
6647 unsigned long flags;
6648
6649 spin_lock_irqsave(&priv->ieee->lock, flags);
6650 mode = priv->assoc_network->mode;
6651 spin_unlock_irqrestore(&priv->ieee->lock, flags);
6652 } else {
6653 mode = priv->ieee->mode;
6654 }
6655 IPW_DEBUG_QOS("QoS network/card mode %d \n", mode);
6656 return mode;
6657}
6658
6659/*
6660* Handle management frame beacon and probe response
6661*/
James Ketrenos3b9990c2005-08-19 13:18:55 -05006662static int ipw_qos_handle_probe_response(struct ipw_priv *priv,
6663 int active_network,
6664 struct ieee80211_network *network)
James Ketrenosb095c382005-08-24 22:04:42 -05006665{
6666 u32 size = sizeof(struct ieee80211_qos_parameters);
6667
James Ketrenosafbf30a2005-08-25 00:05:33 -05006668 if (network->capability & WLAN_CAPABILITY_IBSS)
James Ketrenosb095c382005-08-24 22:04:42 -05006669 network->qos_data.active = network->qos_data.supported;
6670
6671 if (network->flags & NETWORK_HAS_QOS_MASK) {
James Ketrenosafbf30a2005-08-25 00:05:33 -05006672 if (active_network &&
6673 (network->flags & NETWORK_HAS_QOS_PARAMETERS))
James Ketrenosb095c382005-08-24 22:04:42 -05006674 network->qos_data.active = network->qos_data.supported;
6675
6676 if ((network->qos_data.active == 1) && (active_network == 1) &&
6677 (network->flags & NETWORK_HAS_QOS_PARAMETERS) &&
6678 (network->qos_data.old_param_count !=
6679 network->qos_data.param_count)) {
6680 network->qos_data.old_param_count =
6681 network->qos_data.param_count;
6682 schedule_work(&priv->qos_activate);
James Ketrenosafbf30a2005-08-25 00:05:33 -05006683 IPW_DEBUG_QOS("QoS parameters change call "
6684 "qos_activate\n");
James Ketrenosb095c382005-08-24 22:04:42 -05006685 }
6686 } else {
James Ketrenosafbf30a2005-08-25 00:05:33 -05006687 if ((priv->ieee->mode == IEEE_B) || (network->mode == IEEE_B))
6688 memcpy(&network->qos_data.parameters,
James Ketrenosb095c382005-08-24 22:04:42 -05006689 &def_parameters_CCK, size);
James Ketrenosafbf30a2005-08-25 00:05:33 -05006690 else
6691 memcpy(&network->qos_data.parameters,
James Ketrenosb095c382005-08-24 22:04:42 -05006692 &def_parameters_OFDM, size);
James Ketrenosafbf30a2005-08-25 00:05:33 -05006693
James Ketrenosb095c382005-08-24 22:04:42 -05006694 if ((network->qos_data.active == 1) && (active_network == 1)) {
6695 IPW_DEBUG_QOS("QoS was disabled call qos_activate \n");
6696 schedule_work(&priv->qos_activate);
6697 }
6698
6699 network->qos_data.active = 0;
6700 network->qos_data.supported = 0;
6701 }
James Ketrenosafbf30a2005-08-25 00:05:33 -05006702 if ((priv->status & STATUS_ASSOCIATED) &&
6703 (priv->ieee->iw_mode == IW_MODE_ADHOC) && (active_network == 0)) {
6704 if (memcmp(network->bssid, priv->bssid, ETH_ALEN))
6705 if ((network->capability & WLAN_CAPABILITY_IBSS) &&
6706 !(network->flags & NETWORK_EMPTY_ESSID))
James Ketrenosb095c382005-08-24 22:04:42 -05006707 if ((network->ssid_len ==
James Ketrenosafbf30a2005-08-25 00:05:33 -05006708 priv->assoc_network->ssid_len) &&
6709 !memcmp(network->ssid,
6710 priv->assoc_network->ssid,
6711 network->ssid_len)) {
James Ketrenosb095c382005-08-24 22:04:42 -05006712 queue_work(priv->workqueue,
6713 &priv->merge_networks);
6714 }
James Ketrenosb095c382005-08-24 22:04:42 -05006715 }
6716
6717 return 0;
6718}
6719
6720/*
6721* This function set up the firmware to support QoS. It sends
6722* IPW_CMD_QOS_PARAMETERS and IPW_CMD_WME_INFO
6723*/
6724static int ipw_qos_activate(struct ipw_priv *priv,
6725 struct ieee80211_qos_data *qos_network_data)
6726{
6727 int err;
6728 struct ieee80211_qos_parameters qos_parameters[QOS_QOS_SETS];
6729 struct ieee80211_qos_parameters *active_one = NULL;
6730 u32 size = sizeof(struct ieee80211_qos_parameters);
6731 u32 burst_duration;
6732 int i;
6733 u8 type;
6734
6735 type = ipw_qos_current_mode(priv);
6736
6737 active_one = &(qos_parameters[QOS_PARAM_SET_DEF_CCK]);
6738 memcpy(active_one, priv->qos_data.def_qos_parm_CCK, size);
6739 active_one = &(qos_parameters[QOS_PARAM_SET_DEF_OFDM]);
6740 memcpy(active_one, priv->qos_data.def_qos_parm_OFDM, size);
6741
6742 if (qos_network_data == NULL) {
6743 if (type == IEEE_B) {
6744 IPW_DEBUG_QOS("QoS activate network mode %d\n", type);
6745 active_one = &def_parameters_CCK;
6746 } else
6747 active_one = &def_parameters_OFDM;
6748
James Ketrenosafbf30a2005-08-25 00:05:33 -05006749 memcpy(&qos_parameters[QOS_PARAM_SET_ACTIVE], active_one, size);
James Ketrenosb095c382005-08-24 22:04:42 -05006750 burst_duration = ipw_qos_get_burst_duration(priv);
6751 for (i = 0; i < QOS_QUEUE_NUM; i++)
James Ketrenosafbf30a2005-08-25 00:05:33 -05006752 qos_parameters[QOS_PARAM_SET_ACTIVE].tx_op_limit[i] =
6753 (u16) burst_duration;
6754 } else if (priv->ieee->iw_mode == IW_MODE_ADHOC) {
James Ketrenosb095c382005-08-24 22:04:42 -05006755 if (type == IEEE_B) {
6756 IPW_DEBUG_QOS("QoS activate IBSS nework mode %d\n",
6757 type);
6758 if (priv->qos_data.qos_enable == 0)
6759 active_one = &def_parameters_CCK;
6760 else
6761 active_one = priv->qos_data.def_qos_parm_CCK;
6762 } else {
6763 if (priv->qos_data.qos_enable == 0)
6764 active_one = &def_parameters_OFDM;
6765 else
6766 active_one = priv->qos_data.def_qos_parm_OFDM;
6767 }
James Ketrenosafbf30a2005-08-25 00:05:33 -05006768 memcpy(&qos_parameters[QOS_PARAM_SET_ACTIVE], active_one, size);
James Ketrenosb095c382005-08-24 22:04:42 -05006769 } else {
6770 unsigned long flags;
6771 int active;
6772
6773 spin_lock_irqsave(&priv->ieee->lock, flags);
6774 active_one = &(qos_network_data->parameters);
6775 qos_network_data->old_param_count =
6776 qos_network_data->param_count;
James Ketrenosafbf30a2005-08-25 00:05:33 -05006777 memcpy(&qos_parameters[QOS_PARAM_SET_ACTIVE], active_one, size);
James Ketrenosb095c382005-08-24 22:04:42 -05006778 active = qos_network_data->supported;
6779 spin_unlock_irqrestore(&priv->ieee->lock, flags);
6780
6781 if (active == 0) {
6782 burst_duration = ipw_qos_get_burst_duration(priv);
6783 for (i = 0; i < QOS_QUEUE_NUM; i++)
6784 qos_parameters[QOS_PARAM_SET_ACTIVE].
6785 tx_op_limit[i] = (u16) burst_duration;
6786 }
6787 }
6788
6789 IPW_DEBUG_QOS("QoS sending IPW_CMD_QOS_PARAMETERS\n");
James Ketrenosafbf30a2005-08-25 00:05:33 -05006790 err = ipw_send_qos_params_command(priv,
6791 (struct ieee80211_qos_parameters *)
6792 &(qos_parameters[0]));
James Ketrenosb095c382005-08-24 22:04:42 -05006793 if (err)
6794 IPW_DEBUG_QOS("QoS IPW_CMD_QOS_PARAMETERS failed\n");
6795
6796 return err;
6797}
6798
6799/*
6800* send IPW_CMD_WME_INFO to the firmware
6801*/
6802static int ipw_qos_set_info_element(struct ipw_priv *priv)
6803{
6804 int ret = 0;
6805 struct ieee80211_qos_information_element qos_info;
6806
6807 if (priv == NULL)
6808 return -1;
6809
6810 qos_info.elementID = QOS_ELEMENT_ID;
6811 qos_info.length = sizeof(struct ieee80211_qos_information_element) - 2;
6812
6813 qos_info.version = QOS_VERSION_1;
6814 qos_info.ac_info = 0;
6815
6816 memcpy(qos_info.qui, qos_oui, QOS_OUI_LEN);
6817 qos_info.qui_type = QOS_OUI_TYPE;
6818 qos_info.qui_subtype = QOS_OUI_INFO_SUB_TYPE;
6819
6820 ret = ipw_send_qos_info_command(priv, &qos_info);
6821 if (ret != 0) {
6822 IPW_DEBUG_QOS("QoS error calling ipw_send_qos_info_command\n");
6823 }
6824 return ret;
6825}
6826
6827/*
6828* Set the QoS parameter with the association request structure
6829*/
6830static int ipw_qos_association(struct ipw_priv *priv,
6831 struct ieee80211_network *network)
6832{
6833 int err = 0;
6834 struct ieee80211_qos_data *qos_data = NULL;
6835 struct ieee80211_qos_data ibss_data = {
6836 .supported = 1,
6837 .active = 1,
6838 };
6839
6840 switch (priv->ieee->iw_mode) {
6841 case IW_MODE_ADHOC:
6842 if (!(network->capability & WLAN_CAPABILITY_IBSS))
6843 BUG();
6844
6845 qos_data = &ibss_data;
6846 break;
6847
6848 case IW_MODE_INFRA:
6849 qos_data = &network->qos_data;
6850 break;
6851
6852 default:
6853 BUG();
6854 break;
6855 }
6856
6857 err = ipw_qos_activate(priv, qos_data);
6858 if (err) {
6859 priv->assoc_request.policy_support &= ~HC_QOS_SUPPORT_ASSOC;
6860 return err;
6861 }
6862
6863 if (priv->qos_data.qos_enable && qos_data->supported) {
6864 IPW_DEBUG_QOS("QoS will be enabled for this association\n");
6865 priv->assoc_request.policy_support |= HC_QOS_SUPPORT_ASSOC;
6866 return ipw_qos_set_info_element(priv);
6867 }
6868
6869 return 0;
6870}
6871
6872/*
6873* handling the beaconing responces. if we get different QoS setting
6874* of the network from the the associated setting adjust the QoS
6875* setting
6876*/
6877static int ipw_qos_association_resp(struct ipw_priv *priv,
6878 struct ieee80211_network *network)
6879{
6880 int ret = 0;
6881 unsigned long flags;
6882 u32 size = sizeof(struct ieee80211_qos_parameters);
6883 int set_qos_param = 0;
6884
James Ketrenosafbf30a2005-08-25 00:05:33 -05006885 if ((priv == NULL) || (network == NULL) ||
6886 (priv->assoc_network == NULL))
James Ketrenosb095c382005-08-24 22:04:42 -05006887 return ret;
6888
6889 if (!(priv->status & STATUS_ASSOCIATED))
6890 return ret;
6891
James Ketrenosafbf30a2005-08-25 00:05:33 -05006892 if ((priv->ieee->iw_mode != IW_MODE_INFRA))
James Ketrenosb095c382005-08-24 22:04:42 -05006893 return ret;
James Ketrenosb095c382005-08-24 22:04:42 -05006894
6895 spin_lock_irqsave(&priv->ieee->lock, flags);
6896 if (network->flags & NETWORK_HAS_QOS_PARAMETERS) {
James Ketrenosafbf30a2005-08-25 00:05:33 -05006897 memcpy(&priv->assoc_network->qos_data, &network->qos_data,
James Ketrenosb095c382005-08-24 22:04:42 -05006898 sizeof(struct ieee80211_qos_data));
6899 priv->assoc_network->qos_data.active = 1;
6900 if ((network->qos_data.old_param_count !=
6901 network->qos_data.param_count)) {
6902 set_qos_param = 1;
6903 network->qos_data.old_param_count =
6904 network->qos_data.param_count;
6905 }
6906
6907 } else {
James Ketrenosafbf30a2005-08-25 00:05:33 -05006908 if ((network->mode == IEEE_B) || (priv->ieee->mode == IEEE_B))
6909 memcpy(&priv->assoc_network->qos_data.parameters,
James Ketrenosb095c382005-08-24 22:04:42 -05006910 &def_parameters_CCK, size);
James Ketrenosafbf30a2005-08-25 00:05:33 -05006911 else
6912 memcpy(&priv->assoc_network->qos_data.parameters,
James Ketrenosb095c382005-08-24 22:04:42 -05006913 &def_parameters_OFDM, size);
James Ketrenosb095c382005-08-24 22:04:42 -05006914 priv->assoc_network->qos_data.active = 0;
6915 priv->assoc_network->qos_data.supported = 0;
6916 set_qos_param = 1;
6917 }
6918
6919 spin_unlock_irqrestore(&priv->ieee->lock, flags);
6920
6921 if (set_qos_param == 1)
6922 schedule_work(&priv->qos_activate);
6923
6924 return ret;
6925}
6926
6927static u32 ipw_qos_get_burst_duration(struct ipw_priv *priv)
6928{
6929 u32 ret = 0;
6930
6931 if ((priv == NULL))
6932 return 0;
6933
James Ketrenosafbf30a2005-08-25 00:05:33 -05006934 if (!(priv->ieee->modulation & IEEE80211_OFDM_MODULATION))
James Ketrenosb095c382005-08-24 22:04:42 -05006935 ret = priv->qos_data.burst_duration_CCK;
James Ketrenosafbf30a2005-08-25 00:05:33 -05006936 else
James Ketrenosb095c382005-08-24 22:04:42 -05006937 ret = priv->qos_data.burst_duration_OFDM;
James Ketrenosafbf30a2005-08-25 00:05:33 -05006938
James Ketrenosb095c382005-08-24 22:04:42 -05006939 return ret;
6940}
6941
6942/*
6943* Initialize the setting of QoS global
6944*/
6945static void ipw_qos_init(struct ipw_priv *priv, int enable,
6946 int burst_enable, u32 burst_duration_CCK,
6947 u32 burst_duration_OFDM)
6948{
6949 priv->qos_data.qos_enable = enable;
6950
6951 if (priv->qos_data.qos_enable) {
6952 priv->qos_data.def_qos_parm_CCK = &def_qos_parameters_CCK;
6953 priv->qos_data.def_qos_parm_OFDM = &def_qos_parameters_OFDM;
6954 IPW_DEBUG_QOS("QoS is enabled\n");
6955 } else {
6956 priv->qos_data.def_qos_parm_CCK = &def_parameters_CCK;
6957 priv->qos_data.def_qos_parm_OFDM = &def_parameters_OFDM;
6958 IPW_DEBUG_QOS("QoS is not enabled\n");
6959 }
6960
6961 priv->qos_data.burst_enable = burst_enable;
6962
6963 if (burst_enable) {
6964 priv->qos_data.burst_duration_CCK = burst_duration_CCK;
6965 priv->qos_data.burst_duration_OFDM = burst_duration_OFDM;
6966 } else {
6967 priv->qos_data.burst_duration_CCK = 0;
6968 priv->qos_data.burst_duration_OFDM = 0;
6969 }
6970}
6971
6972/*
6973* map the packet priority to the right TX Queue
6974*/
6975static int ipw_get_tx_queue_number(struct ipw_priv *priv, u16 priority)
6976{
6977 if (priority > 7 || !priv->qos_data.qos_enable)
6978 priority = 0;
6979
6980 return from_priority_to_tx_queue[priority] - 1;
6981}
6982
6983/*
6984* add QoS parameter to the TX command
6985*/
6986static int ipw_qos_set_tx_queue_command(struct ipw_priv *priv,
6987 u16 priority,
6988 struct tfd_data *tfd, u8 unicast)
6989{
6990 int ret = 0;
6991 int tx_queue_id = 0;
6992 struct ieee80211_qos_data *qos_data = NULL;
6993 int active, supported;
6994 unsigned long flags;
6995
6996 if (!(priv->status & STATUS_ASSOCIATED))
6997 return 0;
6998
6999 qos_data = &priv->assoc_network->qos_data;
7000
7001 spin_lock_irqsave(&priv->ieee->lock, flags);
7002
7003 if (priv->ieee->iw_mode == IW_MODE_ADHOC) {
7004 if (unicast == 0)
7005 qos_data->active = 0;
7006 else
7007 qos_data->active = qos_data->supported;
7008 }
7009
7010 active = qos_data->active;
7011 supported = qos_data->supported;
7012
7013 spin_unlock_irqrestore(&priv->ieee->lock, flags);
7014
James Ketrenosafbf30a2005-08-25 00:05:33 -05007015 IPW_DEBUG_QOS("QoS %d network is QoS active %d supported %d "
7016 "unicast %d\n",
7017 priv->qos_data.qos_enable, active, supported, unicast);
James Ketrenosb095c382005-08-24 22:04:42 -05007018 if (active && priv->qos_data.qos_enable) {
7019 ret = from_priority_to_tx_queue[priority];
7020 tx_queue_id = ret - 1;
7021 IPW_DEBUG_QOS("QoS packet priority is %d \n", priority);
7022 if (priority <= 7) {
7023 tfd->tx_flags_ext |= DCT_FLAG_EXT_QOS_ENABLED;
7024 tfd->tfd.tfd_26.mchdr.qos_ctrl = priority;
7025 tfd->tfd.tfd_26.mchdr.frame_ctl |=
7026 IEEE80211_STYPE_QOS_DATA;
7027
7028 if (priv->qos_data.qos_no_ack_mask &
7029 (1UL << tx_queue_id)) {
7030 tfd->tx_flags &= ~DCT_FLAG_ACK_REQD;
7031 tfd->tfd.tfd_26.mchdr.qos_ctrl |=
7032 CTRL_QOS_NO_ACK;
7033 }
7034 }
7035 }
7036
7037 return ret;
7038}
7039
7040/*
7041* background support to run QoS activate functionality
7042*/
7043static void ipw_bg_qos_activate(void *data)
7044{
7045 struct ipw_priv *priv = data;
7046
7047 if (priv == NULL)
7048 return;
7049
7050 down(&priv->sem);
7051
7052 if (priv->status & STATUS_ASSOCIATED)
7053 ipw_qos_activate(priv, &(priv->assoc_network->qos_data));
7054
7055 up(&priv->sem);
7056}
7057
James Ketrenos3b9990c2005-08-19 13:18:55 -05007058static int ipw_handle_probe_response(struct net_device *dev,
7059 struct ieee80211_probe_response *resp,
7060 struct ieee80211_network *network)
James Ketrenosb095c382005-08-24 22:04:42 -05007061{
7062 struct ipw_priv *priv = ieee80211_priv(dev);
James Ketrenos3b9990c2005-08-19 13:18:55 -05007063 int active_network = ((priv->status & STATUS_ASSOCIATED) &&
7064 (network == priv->assoc_network));
James Ketrenosb095c382005-08-24 22:04:42 -05007065
James Ketrenos3b9990c2005-08-19 13:18:55 -05007066 ipw_qos_handle_probe_response(priv, active_network, network);
James Ketrenosb095c382005-08-24 22:04:42 -05007067
James Ketrenos3b9990c2005-08-19 13:18:55 -05007068 return 0;
7069}
James Ketrenosb095c382005-08-24 22:04:42 -05007070
James Ketrenos3b9990c2005-08-19 13:18:55 -05007071static int ipw_handle_beacon(struct net_device *dev,
7072 struct ieee80211_beacon *resp,
7073 struct ieee80211_network *network)
7074{
7075 struct ipw_priv *priv = ieee80211_priv(dev);
7076 int active_network = ((priv->status & STATUS_ASSOCIATED) &&
7077 (network == priv->assoc_network));
7078
7079 ipw_qos_handle_probe_response(priv, active_network, network);
7080
7081 return 0;
7082}
7083
7084static int ipw_handle_assoc_response(struct net_device *dev,
7085 struct ieee80211_assoc_response *resp,
7086 struct ieee80211_network *network)
7087{
7088 struct ipw_priv *priv = ieee80211_priv(dev);
7089 ipw_qos_association_resp(priv, network);
James Ketrenosb095c382005-08-24 22:04:42 -05007090 return 0;
7091}
7092
7093static int ipw_send_qos_params_command(struct ipw_priv *priv, struct ieee80211_qos_parameters
7094 *qos_param)
7095{
7096 struct host_cmd cmd = {
7097 .cmd = IPW_CMD_QOS_PARAMETERS,
7098 .len = (sizeof(struct ieee80211_qos_parameters) * 3)
7099 };
7100
James Ketrenosafbf30a2005-08-25 00:05:33 -05007101 memcpy(cmd.param, qos_param, sizeof(*qos_param) * 3);
James Ketrenos9ddf84f2005-08-16 17:07:11 -05007102 return ipw_send_cmd(priv, &cmd);
James Ketrenosb095c382005-08-24 22:04:42 -05007103}
7104
7105static int ipw_send_qos_info_command(struct ipw_priv *priv, struct ieee80211_qos_information_element
7106 *qos_param)
7107{
7108 struct host_cmd cmd = {
7109 .cmd = IPW_CMD_WME_INFO,
7110 .len = sizeof(*qos_param)
7111 };
7112
James Ketrenosafbf30a2005-08-25 00:05:33 -05007113 memcpy(cmd.param, qos_param, sizeof(*qos_param));
James Ketrenos9ddf84f2005-08-16 17:07:11 -05007114 return ipw_send_cmd(priv, &cmd);
James Ketrenosb095c382005-08-24 22:04:42 -05007115}
7116
7117#endif /* CONFIG_IPW_QOS */
7118
James Ketrenos43f66a62005-03-25 12:31:53 -06007119static int ipw_associate_network(struct ipw_priv *priv,
7120 struct ieee80211_network *network,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04007121 struct ipw_supported_rates *rates, int roaming)
James Ketrenos43f66a62005-03-25 12:31:53 -06007122{
7123 int err;
7124
7125 if (priv->config & CFG_FIXED_RATE)
James Ketrenosb095c382005-08-24 22:04:42 -05007126 ipw_set_fixed_rate(priv, network->mode);
James Ketrenos43f66a62005-03-25 12:31:53 -06007127
7128 if (!(priv->config & CFG_STATIC_ESSID)) {
Jeff Garzikbf794512005-07-31 13:07:26 -04007129 priv->essid_len = min(network->ssid_len,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04007130 (u8) IW_ESSID_MAX_SIZE);
James Ketrenos43f66a62005-03-25 12:31:53 -06007131 memcpy(priv->essid, network->ssid, priv->essid_len);
7132 }
7133
7134 network->last_associate = jiffies;
7135
7136 memset(&priv->assoc_request, 0, sizeof(priv->assoc_request));
7137 priv->assoc_request.channel = network->channel;
Zhu Yi3e234b42006-01-24 16:36:52 +08007138 priv->assoc_request.auth_key = 0;
7139
James Ketrenos43f66a62005-03-25 12:31:53 -06007140 if ((priv->capability & CAP_PRIVACY_ON) &&
Zhu Yi3e234b42006-01-24 16:36:52 +08007141 (priv->ieee->sec.auth_mode == WLAN_AUTH_SHARED_KEY)) {
James Ketrenos43f66a62005-03-25 12:31:53 -06007142 priv->assoc_request.auth_type = AUTH_SHARED_KEY;
James Ketrenosb095c382005-08-24 22:04:42 -05007143 priv->assoc_request.auth_key = priv->ieee->sec.active_key;
7144
Zhu Yi3e234b42006-01-24 16:36:52 +08007145 if ((priv->ieee->sec.level == SEC_LEVEL_1) &&
James Ketrenosb095c382005-08-24 22:04:42 -05007146 !(priv->ieee->host_encrypt || priv->ieee->host_decrypt))
7147 ipw_send_wep_keys(priv, DCW_WEP_KEY_SEC_TYPE_WEP);
Zhu Yi3e234b42006-01-24 16:36:52 +08007148
7149 } else if ((priv->capability & CAP_PRIVACY_ON) &&
7150 (priv->ieee->sec.auth_mode == WLAN_AUTH_LEAP))
7151 priv->assoc_request.auth_type = AUTH_LEAP;
7152 else
James Ketrenos43f66a62005-03-25 12:31:53 -06007153 priv->assoc_request.auth_type = AUTH_OPEN;
James Ketrenos43f66a62005-03-25 12:31:53 -06007154
James Ketrenosa613bff2005-08-24 21:43:11 -05007155 if (priv->ieee->wpa_ie_len) {
James Ketrenosea2b26e2005-08-24 21:25:16 -05007156 priv->assoc_request.policy_support = 0x02; /* RSN active */
7157 ipw_set_rsn_capa(priv, priv->ieee->wpa_ie,
7158 priv->ieee->wpa_ie_len);
7159 }
James Ketrenos43f66a62005-03-25 12:31:53 -06007160
Jeff Garzikbf794512005-07-31 13:07:26 -04007161 /*
7162 * It is valid for our ieee device to support multiple modes, but
7163 * when it comes to associating to a given network we have to choose
James Ketrenos43f66a62005-03-25 12:31:53 -06007164 * just one mode.
7165 */
7166 if (network->mode & priv->ieee->mode & IEEE_A)
7167 priv->assoc_request.ieee_mode = IPW_A_MODE;
7168 else if (network->mode & priv->ieee->mode & IEEE_G)
7169 priv->assoc_request.ieee_mode = IPW_G_MODE;
7170 else if (network->mode & priv->ieee->mode & IEEE_B)
7171 priv->assoc_request.ieee_mode = IPW_B_MODE;
7172
James Ketrenosea2b26e2005-08-24 21:25:16 -05007173 priv->assoc_request.capability = network->capability;
7174 if ((network->capability & WLAN_CAPABILITY_SHORT_PREAMBLE)
7175 && !(priv->config & CFG_PREAMBLE_LONG)) {
7176 priv->assoc_request.preamble_length = DCT_FLAG_SHORT_PREAMBLE;
7177 } else {
7178 priv->assoc_request.preamble_length = DCT_FLAG_LONG_PREAMBLE;
7179
7180 /* Clear the short preamble if we won't be supporting it */
7181 priv->assoc_request.capability &=
7182 ~WLAN_CAPABILITY_SHORT_PREAMBLE;
7183 }
7184
James Ketrenosafbf30a2005-08-25 00:05:33 -05007185 /* Clear capability bits that aren't used in Ad Hoc */
7186 if (priv->ieee->iw_mode == IW_MODE_ADHOC)
7187 priv->assoc_request.capability &=
7188 ~WLAN_CAPABILITY_SHORT_SLOT_TIME;
7189
James Ketrenos43f66a62005-03-25 12:31:53 -06007190 IPW_DEBUG_ASSOC("%sssocation attempt: '%s', channel %d, "
James Ketrenosea2b26e2005-08-24 21:25:16 -05007191 "802.11%c [%d], %s[:%s], enc=%s%s%s%c%c\n",
James Ketrenos43f66a62005-03-25 12:31:53 -06007192 roaming ? "Rea" : "A",
Jeff Garzikbf794512005-07-31 13:07:26 -04007193 escape_essid(priv->essid, priv->essid_len),
7194 network->channel,
7195 ipw_modes[priv->assoc_request.ieee_mode],
7196 rates->num_rates,
James Ketrenosea2b26e2005-08-24 21:25:16 -05007197 (priv->assoc_request.preamble_length ==
7198 DCT_FLAG_LONG_PREAMBLE) ? "long" : "short",
7199 network->capability &
7200 WLAN_CAPABILITY_SHORT_PREAMBLE ? "short" : "long",
James Ketrenos43f66a62005-03-25 12:31:53 -06007201 priv->capability & CAP_PRIVACY_ON ? "on " : "off",
Jeff Garzikbf794512005-07-31 13:07:26 -04007202 priv->capability & CAP_PRIVACY_ON ?
7203 (priv->capability & CAP_SHARED_KEY ? "(shared)" :
James Ketrenos43f66a62005-03-25 12:31:53 -06007204 "(open)") : "",
7205 priv->capability & CAP_PRIVACY_ON ? " key=" : "",
Jeff Garzikbf794512005-07-31 13:07:26 -04007206 priv->capability & CAP_PRIVACY_ON ?
James Ketrenosb095c382005-08-24 22:04:42 -05007207 '1' + priv->ieee->sec.active_key : '.',
Jeff Garzik0edd5b42005-09-07 00:48:31 -04007208 priv->capability & CAP_PRIVACY_ON ? '.' : ' ');
James Ketrenos43f66a62005-03-25 12:31:53 -06007209
7210 priv->assoc_request.beacon_interval = network->beacon_interval;
7211 if ((priv->ieee->iw_mode == IW_MODE_ADHOC) &&
Jeff Garzik0edd5b42005-09-07 00:48:31 -04007212 (network->time_stamp[0] == 0) && (network->time_stamp[1] == 0)) {
James Ketrenos43f66a62005-03-25 12:31:53 -06007213 priv->assoc_request.assoc_type = HC_IBSS_START;
7214 priv->assoc_request.assoc_tsf_msw = 0;
7215 priv->assoc_request.assoc_tsf_lsw = 0;
7216 } else {
7217 if (unlikely(roaming))
7218 priv->assoc_request.assoc_type = HC_REASSOCIATE;
7219 else
7220 priv->assoc_request.assoc_type = HC_ASSOCIATE;
7221 priv->assoc_request.assoc_tsf_msw = network->time_stamp[1];
7222 priv->assoc_request.assoc_tsf_lsw = network->time_stamp[0];
7223 }
7224
James Ketrenosafbf30a2005-08-25 00:05:33 -05007225 memcpy(priv->assoc_request.bssid, network->bssid, ETH_ALEN);
James Ketrenos43f66a62005-03-25 12:31:53 -06007226
7227 if (priv->ieee->iw_mode == IW_MODE_ADHOC) {
7228 memset(&priv->assoc_request.dest, 0xFF, ETH_ALEN);
7229 priv->assoc_request.atim_window = network->atim_window;
7230 } else {
James Ketrenosafbf30a2005-08-25 00:05:33 -05007231 memcpy(priv->assoc_request.dest, network->bssid, ETH_ALEN);
James Ketrenos43f66a62005-03-25 12:31:53 -06007232 priv->assoc_request.atim_window = 0;
7233 }
7234
James Ketrenos43f66a62005-03-25 12:31:53 -06007235 priv->assoc_request.listen_interval = network->listen_interval;
Jeff Garzikbf794512005-07-31 13:07:26 -04007236
James Ketrenos43f66a62005-03-25 12:31:53 -06007237 err = ipw_send_ssid(priv, priv->essid, priv->essid_len);
7238 if (err) {
7239 IPW_DEBUG_HC("Attempt to send SSID command failed.\n");
7240 return err;
7241 }
7242
7243 rates->ieee_mode = priv->assoc_request.ieee_mode;
7244 rates->purpose = IPW_RATE_CONNECT;
7245 ipw_send_supported_rates(priv, rates);
Jeff Garzikbf794512005-07-31 13:07:26 -04007246
James Ketrenos43f66a62005-03-25 12:31:53 -06007247 if (priv->assoc_request.ieee_mode == IPW_G_MODE)
7248 priv->sys_config.dot11g_auto_detection = 1;
7249 else
7250 priv->sys_config.dot11g_auto_detection = 0;
James Ketrenosc848d0a2005-08-24 21:56:24 -05007251
7252 if (priv->ieee->iw_mode == IW_MODE_ADHOC)
7253 priv->sys_config.answer_broadcast_ssid_probe = 1;
7254 else
7255 priv->sys_config.answer_broadcast_ssid_probe = 0;
7256
James Ketrenos43f66a62005-03-25 12:31:53 -06007257 err = ipw_send_system_config(priv, &priv->sys_config);
7258 if (err) {
7259 IPW_DEBUG_HC("Attempt to send sys config command failed.\n");
7260 return err;
7261 }
Jeff Garzikbf794512005-07-31 13:07:26 -04007262
James Ketrenos43f66a62005-03-25 12:31:53 -06007263 IPW_DEBUG_ASSOC("Association sensitivity: %d\n", network->stats.rssi);
James Ketrenosea2b26e2005-08-24 21:25:16 -05007264 err = ipw_set_sensitivity(priv, network->stats.rssi + IPW_RSSI_TO_DBM);
James Ketrenos43f66a62005-03-25 12:31:53 -06007265 if (err) {
7266 IPW_DEBUG_HC("Attempt to send associate command failed.\n");
7267 return err;
7268 }
7269
7270 /*
7271 * If preemption is enabled, it is possible for the association
7272 * to complete before we return from ipw_send_associate. Therefore
7273 * we have to be sure and update our priviate data first.
7274 */
7275 priv->channel = network->channel;
7276 memcpy(priv->bssid, network->bssid, ETH_ALEN);
Jeff Garzikbf794512005-07-31 13:07:26 -04007277 priv->status |= STATUS_ASSOCIATING;
James Ketrenos43f66a62005-03-25 12:31:53 -06007278 priv->status &= ~STATUS_SECURITY_UPDATED;
7279
7280 priv->assoc_network = network;
7281
James Ketrenosb095c382005-08-24 22:04:42 -05007282#ifdef CONFIG_IPW_QOS
7283 ipw_qos_association(priv, network);
7284#endif
7285
James Ketrenos43f66a62005-03-25 12:31:53 -06007286 err = ipw_send_associate(priv, &priv->assoc_request);
7287 if (err) {
7288 IPW_DEBUG_HC("Attempt to send associate command failed.\n");
7289 return err;
7290 }
Jeff Garzikbf794512005-07-31 13:07:26 -04007291
7292 IPW_DEBUG(IPW_DL_STATE, "associating: '%s' " MAC_FMT " \n",
James Ketrenos43f66a62005-03-25 12:31:53 -06007293 escape_essid(priv->essid, priv->essid_len),
7294 MAC_ARG(priv->bssid));
7295
7296 return 0;
7297}
7298
7299static void ipw_roam(void *data)
7300{
7301 struct ipw_priv *priv = data;
7302 struct ieee80211_network *network = NULL;
7303 struct ipw_network_match match = {
7304 .network = priv->assoc_network
7305 };
7306
7307 /* The roaming process is as follows:
Jeff Garzikbf794512005-07-31 13:07:26 -04007308 *
7309 * 1. Missed beacon threshold triggers the roaming process by
James Ketrenos43f66a62005-03-25 12:31:53 -06007310 * setting the status ROAM bit and requesting a scan.
7311 * 2. When the scan completes, it schedules the ROAM work
7312 * 3. The ROAM work looks at all of the known networks for one that
7313 * is a better network than the currently associated. If none
7314 * found, the ROAM process is over (ROAM bit cleared)
7315 * 4. If a better network is found, a disassociation request is
7316 * sent.
7317 * 5. When the disassociation completes, the roam work is again
7318 * scheduled. The second time through, the driver is no longer
7319 * associated, and the newly selected network is sent an
Jeff Garzikbf794512005-07-31 13:07:26 -04007320 * association request.
James Ketrenos43f66a62005-03-25 12:31:53 -06007321 * 6. At this point ,the roaming process is complete and the ROAM
7322 * status bit is cleared.
7323 */
7324
7325 /* If we are no longer associated, and the roaming bit is no longer
7326 * set, then we are not actively roaming, so just return */
7327 if (!(priv->status & (STATUS_ASSOCIATED | STATUS_ROAMING)))
7328 return;
Jeff Garzikbf794512005-07-31 13:07:26 -04007329
James Ketrenos43f66a62005-03-25 12:31:53 -06007330 if (priv->status & STATUS_ASSOCIATED) {
Jeff Garzikbf794512005-07-31 13:07:26 -04007331 /* First pass through ROAM process -- look for a better
James Ketrenos43f66a62005-03-25 12:31:53 -06007332 * network */
James Ketrenosa613bff2005-08-24 21:43:11 -05007333 unsigned long flags;
James Ketrenos43f66a62005-03-25 12:31:53 -06007334 u8 rssi = priv->assoc_network->stats.rssi;
7335 priv->assoc_network->stats.rssi = -128;
James Ketrenosa613bff2005-08-24 21:43:11 -05007336 spin_lock_irqsave(&priv->ieee->lock, flags);
James Ketrenos43f66a62005-03-25 12:31:53 -06007337 list_for_each_entry(network, &priv->ieee->network_list, list) {
7338 if (network != priv->assoc_network)
7339 ipw_best_network(priv, &match, network, 1);
7340 }
James Ketrenosa613bff2005-08-24 21:43:11 -05007341 spin_unlock_irqrestore(&priv->ieee->lock, flags);
James Ketrenos43f66a62005-03-25 12:31:53 -06007342 priv->assoc_network->stats.rssi = rssi;
Jeff Garzikbf794512005-07-31 13:07:26 -04007343
James Ketrenos43f66a62005-03-25 12:31:53 -06007344 if (match.network == priv->assoc_network) {
7345 IPW_DEBUG_ASSOC("No better APs in this network to "
7346 "roam to.\n");
7347 priv->status &= ~STATUS_ROAMING;
7348 ipw_debug_config(priv);
7349 return;
7350 }
Jeff Garzikbf794512005-07-31 13:07:26 -04007351
James Ketrenos43f66a62005-03-25 12:31:53 -06007352 ipw_send_disassociate(priv, 1);
7353 priv->assoc_network = match.network;
7354
7355 return;
Jeff Garzikbf794512005-07-31 13:07:26 -04007356 }
James Ketrenos43f66a62005-03-25 12:31:53 -06007357
7358 /* Second pass through ROAM process -- request association */
7359 ipw_compatible_rates(priv, priv->assoc_network, &match.rates);
7360 ipw_associate_network(priv, priv->assoc_network, &match.rates, 1);
7361 priv->status &= ~STATUS_ROAMING;
7362}
7363
James Ketrenosc848d0a2005-08-24 21:56:24 -05007364static void ipw_bg_roam(void *data)
7365{
7366 struct ipw_priv *priv = data;
7367 down(&priv->sem);
7368 ipw_roam(data);
7369 up(&priv->sem);
7370}
7371
7372static int ipw_associate(void *data)
James Ketrenos43f66a62005-03-25 12:31:53 -06007373{
7374 struct ipw_priv *priv = data;
7375
7376 struct ieee80211_network *network = NULL;
7377 struct ipw_network_match match = {
7378 .network = NULL
7379 };
7380 struct ipw_supported_rates *rates;
7381 struct list_head *element;
James Ketrenosa613bff2005-08-24 21:43:11 -05007382 unsigned long flags;
James Ketrenos43f66a62005-03-25 12:31:53 -06007383
James Ketrenosb095c382005-08-24 22:04:42 -05007384 if (priv->ieee->iw_mode == IW_MODE_MONITOR) {
7385 IPW_DEBUG_ASSOC("Not attempting association (monitor mode)\n");
7386 return 0;
7387 }
7388
James Ketrenosc848d0a2005-08-24 21:56:24 -05007389 if (priv->status & (STATUS_ASSOCIATED | STATUS_ASSOCIATING)) {
James Ketrenosafbf30a2005-08-25 00:05:33 -05007390 IPW_DEBUG_ASSOC("Not attempting association (already in "
7391 "progress)\n");
James Ketrenosc848d0a2005-08-24 21:56:24 -05007392 return 0;
7393 }
7394
Hong Liue6324722005-09-14 21:04:15 -05007395 if (priv->status & STATUS_DISASSOCIATING) {
7396 IPW_DEBUG_ASSOC("Not attempting association (in "
7397 "disassociating)\n ");
7398 queue_work(priv->workqueue, &priv->associate);
7399 return 0;
7400 }
7401
James Ketrenosc848d0a2005-08-24 21:56:24 -05007402 if (!ipw_is_init(priv) || (priv->status & STATUS_SCANNING)) {
James Ketrenosafbf30a2005-08-25 00:05:33 -05007403 IPW_DEBUG_ASSOC("Not attempting association (scanning or not "
7404 "initialized)\n");
James Ketrenosc848d0a2005-08-24 21:56:24 -05007405 return 0;
7406 }
James Ketrenos43f66a62005-03-25 12:31:53 -06007407
7408 if (!(priv->config & CFG_ASSOCIATE) &&
7409 !(priv->config & (CFG_STATIC_ESSID |
Jeff Garzik0edd5b42005-09-07 00:48:31 -04007410 CFG_STATIC_CHANNEL | CFG_STATIC_BSSID))) {
James Ketrenos43f66a62005-03-25 12:31:53 -06007411 IPW_DEBUG_ASSOC("Not attempting association (associate=0)\n");
James Ketrenosc848d0a2005-08-24 21:56:24 -05007412 return 0;
James Ketrenos43f66a62005-03-25 12:31:53 -06007413 }
7414
James Ketrenosa613bff2005-08-24 21:43:11 -05007415 /* Protect our use of the network_list */
7416 spin_lock_irqsave(&priv->ieee->lock, flags);
Jeff Garzikbf794512005-07-31 13:07:26 -04007417 list_for_each_entry(network, &priv->ieee->network_list, list)
Jeff Garzik0edd5b42005-09-07 00:48:31 -04007418 ipw_best_network(priv, &match, network, 0);
James Ketrenos43f66a62005-03-25 12:31:53 -06007419
7420 network = match.network;
7421 rates = &match.rates;
7422
7423 if (network == NULL &&
7424 priv->ieee->iw_mode == IW_MODE_ADHOC &&
7425 priv->config & CFG_ADHOC_CREATE &&
7426 priv->config & CFG_STATIC_ESSID &&
James Ketrenosa613bff2005-08-24 21:43:11 -05007427 priv->config & CFG_STATIC_CHANNEL &&
James Ketrenos43f66a62005-03-25 12:31:53 -06007428 !list_empty(&priv->ieee->network_free_list)) {
7429 element = priv->ieee->network_free_list.next;
Jeff Garzik0edd5b42005-09-07 00:48:31 -04007430 network = list_entry(element, struct ieee80211_network, list);
James Ketrenos43f66a62005-03-25 12:31:53 -06007431 ipw_adhoc_create(priv, network);
7432 rates = &priv->rates;
7433 list_del(element);
7434 list_add_tail(&network->list, &priv->ieee->network_list);
7435 }
James Ketrenosa613bff2005-08-24 21:43:11 -05007436 spin_unlock_irqrestore(&priv->ieee->lock, flags);
Jeff Garzikbf794512005-07-31 13:07:26 -04007437
James Ketrenos43f66a62005-03-25 12:31:53 -06007438 /* If we reached the end of the list, then we don't have any valid
7439 * matching APs */
7440 if (!network) {
7441 ipw_debug_config(priv);
7442
James Ketrenosb095c382005-08-24 22:04:42 -05007443 if (!(priv->status & STATUS_SCANNING)) {
7444 if (!(priv->config & CFG_SPEED_SCAN))
7445 queue_delayed_work(priv->workqueue,
7446 &priv->request_scan,
7447 SCAN_INTERVAL);
7448 else
7449 queue_work(priv->workqueue,
7450 &priv->request_scan);
7451 }
Jeff Garzikbf794512005-07-31 13:07:26 -04007452
James Ketrenosc848d0a2005-08-24 21:56:24 -05007453 return 0;
James Ketrenos43f66a62005-03-25 12:31:53 -06007454 }
7455
7456 ipw_associate_network(priv, network, rates, 0);
James Ketrenosc848d0a2005-08-24 21:56:24 -05007457
7458 return 1;
James Ketrenos43f66a62005-03-25 12:31:53 -06007459}
Jeff Garzikbf794512005-07-31 13:07:26 -04007460
James Ketrenosc848d0a2005-08-24 21:56:24 -05007461static void ipw_bg_associate(void *data)
James Ketrenos43f66a62005-03-25 12:31:53 -06007462{
James Ketrenosc848d0a2005-08-24 21:56:24 -05007463 struct ipw_priv *priv = data;
7464 down(&priv->sem);
7465 ipw_associate(data);
7466 up(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06007467}
7468
James Ketrenosb095c382005-08-24 22:04:42 -05007469static void ipw_rebuild_decrypted_skb(struct ipw_priv *priv,
7470 struct sk_buff *skb)
7471{
7472 struct ieee80211_hdr *hdr;
7473 u16 fc;
7474
7475 hdr = (struct ieee80211_hdr *)skb->data;
7476 fc = le16_to_cpu(hdr->frame_ctl);
7477 if (!(fc & IEEE80211_FCTL_PROTECTED))
7478 return;
7479
7480 fc &= ~IEEE80211_FCTL_PROTECTED;
7481 hdr->frame_ctl = cpu_to_le16(fc);
7482 switch (priv->ieee->sec.level) {
7483 case SEC_LEVEL_3:
7484 /* Remove CCMP HDR */
7485 memmove(skb->data + IEEE80211_3ADDR_LEN,
7486 skb->data + IEEE80211_3ADDR_LEN + 8,
7487 skb->len - IEEE80211_3ADDR_LEN - 8);
Zhu Yif4ff4972005-09-12 10:48:48 -05007488 skb_trim(skb, skb->len - 16); /* CCMP_HDR_LEN + CCMP_MIC_LEN */
James Ketrenosb095c382005-08-24 22:04:42 -05007489 break;
7490 case SEC_LEVEL_2:
7491 break;
7492 case SEC_LEVEL_1:
7493 /* Remove IV */
7494 memmove(skb->data + IEEE80211_3ADDR_LEN,
7495 skb->data + IEEE80211_3ADDR_LEN + 4,
7496 skb->len - IEEE80211_3ADDR_LEN - 4);
Zhu Yif4ff4972005-09-12 10:48:48 -05007497 skb_trim(skb, skb->len - 8); /* IV + ICV */
James Ketrenosb095c382005-08-24 22:04:42 -05007498 break;
7499 case SEC_LEVEL_0:
7500 break;
7501 default:
7502 printk(KERN_ERR "Unknow security level %d\n",
7503 priv->ieee->sec.level);
7504 break;
7505 }
7506}
7507
7508static void ipw_handle_data_packet(struct ipw_priv *priv,
7509 struct ipw_rx_mem_buffer *rxb,
7510 struct ieee80211_rx_stats *stats)
James Ketrenos43f66a62005-03-25 12:31:53 -06007511{
Hong Liu567deaf2005-08-31 18:07:22 +08007512 struct ieee80211_hdr_4addr *hdr;
James Ketrenos43f66a62005-03-25 12:31:53 -06007513 struct ipw_rx_packet *pkt = (struct ipw_rx_packet *)rxb->skb->data;
7514
7515 /* We received data from the HW, so stop the watchdog */
7516 priv->net_dev->trans_start = jiffies;
7517
Jeff Garzikbf794512005-07-31 13:07:26 -04007518 /* We only process data packets if the
James Ketrenos43f66a62005-03-25 12:31:53 -06007519 * interface is open */
James Ketrenosa613bff2005-08-24 21:43:11 -05007520 if (unlikely((le16_to_cpu(pkt->u.frame.length) + IPW_RX_FRAME_SIZE) >
James Ketrenos43f66a62005-03-25 12:31:53 -06007521 skb_tailroom(rxb->skb))) {
7522 priv->ieee->stats.rx_errors++;
7523 priv->wstats.discard.misc++;
7524 IPW_DEBUG_DROP("Corruption detected! Oh no!\n");
7525 return;
7526 } else if (unlikely(!netif_running(priv->net_dev))) {
7527 priv->ieee->stats.rx_dropped++;
7528 priv->wstats.discard.misc++;
7529 IPW_DEBUG_DROP("Dropping packet while interface is not up.\n");
7530 return;
7531 }
7532
7533 /* Advance skb->data to the start of the actual payload */
Jiri Bencaaa4d302005-06-07 14:58:41 +02007534 skb_reserve(rxb->skb, offsetof(struct ipw_rx_packet, u.frame.data));
James Ketrenos43f66a62005-03-25 12:31:53 -06007535
7536 /* Set the size of the skb to the size of the frame */
James Ketrenosa613bff2005-08-24 21:43:11 -05007537 skb_put(rxb->skb, le16_to_cpu(pkt->u.frame.length));
James Ketrenos43f66a62005-03-25 12:31:53 -06007538
7539 IPW_DEBUG_RX("Rx packet of %d bytes.\n", rxb->skb->len);
7540
James Ketrenosb095c382005-08-24 22:04:42 -05007541 /* HW decrypt will not clear the WEP bit, MIC, PN, etc. */
Hong Liu567deaf2005-08-31 18:07:22 +08007542 hdr = (struct ieee80211_hdr_4addr *)rxb->skb->data;
7543 if (priv->ieee->iw_mode != IW_MODE_MONITOR &&
Stephen Hemminger3c190652006-01-03 15:27:38 -08007544 (is_multicast_ether_addr(hdr->addr1) ?
Hong Liu567deaf2005-08-31 18:07:22 +08007545 !priv->ieee->host_mc_decrypt : !priv->ieee->host_decrypt))
James Ketrenosb095c382005-08-24 22:04:42 -05007546 ipw_rebuild_decrypted_skb(priv, rxb->skb);
7547
Jeff Garzikbf794512005-07-31 13:07:26 -04007548 if (!ieee80211_rx(priv->ieee, rxb->skb, stats))
James Ketrenos43f66a62005-03-25 12:31:53 -06007549 priv->ieee->stats.rx_errors++;
James Ketrenosa613bff2005-08-24 21:43:11 -05007550 else { /* ieee80211_rx succeeded, so it now owns the SKB */
James Ketrenos43f66a62005-03-25 12:31:53 -06007551 rxb->skb = NULL;
James Ketrenosb095c382005-08-24 22:04:42 -05007552 __ipw_led_activity_on(priv);
James Ketrenosa613bff2005-08-24 21:43:11 -05007553 }
James Ketrenos43f66a62005-03-25 12:31:53 -06007554}
7555
Mike Kershaw24a47db2005-08-26 00:41:54 -05007556#ifdef CONFIG_IEEE80211_RADIOTAP
7557static void ipw_handle_data_packet_monitor(struct ipw_priv *priv,
7558 struct ipw_rx_mem_buffer *rxb,
7559 struct ieee80211_rx_stats *stats)
7560{
7561 struct ipw_rx_packet *pkt = (struct ipw_rx_packet *)rxb->skb->data;
7562 struct ipw_rx_frame *frame = &pkt->u.frame;
7563
7564 /* initial pull of some data */
7565 u16 received_channel = frame->received_channel;
7566 u8 antennaAndPhy = frame->antennaAndPhy;
7567 s8 antsignal = frame->rssi_dbm - IPW_RSSI_TO_DBM; /* call it signed anyhow */
7568 u16 pktrate = frame->rate;
7569
7570 /* Magic struct that slots into the radiotap header -- no reason
7571 * to build this manually element by element, we can write it much
7572 * more efficiently than we can parse it. ORDER MATTERS HERE */
7573 struct ipw_rt_hdr {
7574 struct ieee80211_radiotap_header rt_hdr;
7575 u8 rt_flags; /* radiotap packet flags */
7576 u8 rt_rate; /* rate in 500kb/s */
7577 u16 rt_channel; /* channel in mhz */
7578 u16 rt_chbitmask; /* channel bitfield */
7579 s8 rt_dbmsignal; /* signal in dbM, kluged to signed */
7580 u8 rt_antenna; /* antenna number */
7581 } *ipw_rt;
7582
7583 short len = le16_to_cpu(pkt->u.frame.length);
7584
7585 /* We received data from the HW, so stop the watchdog */
7586 priv->net_dev->trans_start = jiffies;
7587
7588 /* We only process data packets if the
7589 * interface is open */
7590 if (unlikely((le16_to_cpu(pkt->u.frame.length) + IPW_RX_FRAME_SIZE) >
7591 skb_tailroom(rxb->skb))) {
7592 priv->ieee->stats.rx_errors++;
7593 priv->wstats.discard.misc++;
7594 IPW_DEBUG_DROP("Corruption detected! Oh no!\n");
7595 return;
7596 } else if (unlikely(!netif_running(priv->net_dev))) {
7597 priv->ieee->stats.rx_dropped++;
7598 priv->wstats.discard.misc++;
7599 IPW_DEBUG_DROP("Dropping packet while interface is not up.\n");
7600 return;
7601 }
7602
7603 /* Libpcap 0.9.3+ can handle variable length radiotap, so we'll use
7604 * that now */
7605 if (len > IPW_RX_BUF_SIZE - sizeof(struct ipw_rt_hdr)) {
7606 /* FIXME: Should alloc bigger skb instead */
7607 priv->ieee->stats.rx_dropped++;
7608 priv->wstats.discard.misc++;
7609 IPW_DEBUG_DROP("Dropping too large packet in monitor\n");
7610 return;
7611 }
7612
7613 /* copy the frame itself */
7614 memmove(rxb->skb->data + sizeof(struct ipw_rt_hdr),
7615 rxb->skb->data + IPW_RX_FRAME_SIZE, len);
7616
7617 /* Zero the radiotap static buffer ... We only need to zero the bytes NOT
7618 * part of our real header, saves a little time.
7619 *
7620 * No longer necessary since we fill in all our data. Purge before merging
7621 * patch officially.
7622 * memset(rxb->skb->data + sizeof(struct ipw_rt_hdr), 0,
7623 * IEEE80211_RADIOTAP_HDRLEN - sizeof(struct ipw_rt_hdr));
7624 */
7625
7626 ipw_rt = (struct ipw_rt_hdr *)rxb->skb->data;
7627
7628 ipw_rt->rt_hdr.it_version = PKTHDR_RADIOTAP_VERSION;
7629 ipw_rt->rt_hdr.it_pad = 0; /* always good to zero */
7630 ipw_rt->rt_hdr.it_len = sizeof(struct ipw_rt_hdr); /* total header+data */
7631
7632 /* Big bitfield of all the fields we provide in radiotap */
7633 ipw_rt->rt_hdr.it_present =
7634 ((1 << IEEE80211_RADIOTAP_FLAGS) |
7635 (1 << IEEE80211_RADIOTAP_RATE) |
7636 (1 << IEEE80211_RADIOTAP_CHANNEL) |
7637 (1 << IEEE80211_RADIOTAP_DBM_ANTSIGNAL) |
7638 (1 << IEEE80211_RADIOTAP_ANTENNA));
7639
7640 /* Zero the flags, we'll add to them as we go */
7641 ipw_rt->rt_flags = 0;
7642
7643 /* Convert signal to DBM */
7644 ipw_rt->rt_dbmsignal = antsignal;
7645
7646 /* Convert the channel data and set the flags */
7647 ipw_rt->rt_channel = cpu_to_le16(ieee80211chan2mhz(received_channel));
7648 if (received_channel > 14) { /* 802.11a */
7649 ipw_rt->rt_chbitmask =
7650 cpu_to_le16((IEEE80211_CHAN_OFDM | IEEE80211_CHAN_5GHZ));
7651 } else if (antennaAndPhy & 32) { /* 802.11b */
7652 ipw_rt->rt_chbitmask =
7653 cpu_to_le16((IEEE80211_CHAN_CCK | IEEE80211_CHAN_2GHZ));
7654 } else { /* 802.11g */
7655 ipw_rt->rt_chbitmask =
7656 (IEEE80211_CHAN_OFDM | IEEE80211_CHAN_2GHZ);
7657 }
7658
7659 /* set the rate in multiples of 500k/s */
7660 switch (pktrate) {
7661 case IPW_TX_RATE_1MB:
7662 ipw_rt->rt_rate = 2;
7663 break;
7664 case IPW_TX_RATE_2MB:
7665 ipw_rt->rt_rate = 4;
7666 break;
7667 case IPW_TX_RATE_5MB:
7668 ipw_rt->rt_rate = 10;
7669 break;
7670 case IPW_TX_RATE_6MB:
7671 ipw_rt->rt_rate = 12;
7672 break;
7673 case IPW_TX_RATE_9MB:
7674 ipw_rt->rt_rate = 18;
7675 break;
7676 case IPW_TX_RATE_11MB:
7677 ipw_rt->rt_rate = 22;
7678 break;
7679 case IPW_TX_RATE_12MB:
7680 ipw_rt->rt_rate = 24;
7681 break;
7682 case IPW_TX_RATE_18MB:
7683 ipw_rt->rt_rate = 36;
7684 break;
7685 case IPW_TX_RATE_24MB:
7686 ipw_rt->rt_rate = 48;
7687 break;
7688 case IPW_TX_RATE_36MB:
7689 ipw_rt->rt_rate = 72;
7690 break;
7691 case IPW_TX_RATE_48MB:
7692 ipw_rt->rt_rate = 96;
7693 break;
7694 case IPW_TX_RATE_54MB:
7695 ipw_rt->rt_rate = 108;
7696 break;
7697 default:
7698 ipw_rt->rt_rate = 0;
7699 break;
7700 }
7701
7702 /* antenna number */
7703 ipw_rt->rt_antenna = (antennaAndPhy & 3); /* Is this right? */
7704
7705 /* set the preamble flag if we have it */
7706 if ((antennaAndPhy & 64))
7707 ipw_rt->rt_flags |= IEEE80211_RADIOTAP_F_SHORTPRE;
7708
7709 /* Set the size of the skb to the size of the frame */
7710 skb_put(rxb->skb, len + sizeof(struct ipw_rt_hdr));
James Ketrenos43f66a62005-03-25 12:31:53 -06007711
7712 IPW_DEBUG_RX("Rx packet of %d bytes.\n", rxb->skb->len);
7713
7714 if (!ieee80211_rx(priv->ieee, rxb->skb, stats))
7715 priv->ieee->stats.rx_errors++;
Mike Kershaw24a47db2005-08-26 00:41:54 -05007716 else { /* ieee80211_rx succeeded, so it now owns the SKB */
James Ketrenos43f66a62005-03-25 12:31:53 -06007717 rxb->skb = NULL;
Mike Kershaw24a47db2005-08-26 00:41:54 -05007718 /* no LED during capture */
7719 }
7720}
7721#endif
7722
Arjan van de Ven858119e2006-01-14 13:20:43 -08007723static int is_network_packet(struct ipw_priv *priv,
James Ketrenosea2b26e2005-08-24 21:25:16 -05007724 struct ieee80211_hdr_4addr *header)
7725{
7726 /* Filter incoming packets to determine if they are targetted toward
7727 * this network, discarding packets coming from ourselves */
7728 switch (priv->ieee->iw_mode) {
James Ketrenosa613bff2005-08-24 21:43:11 -05007729 case IW_MODE_ADHOC: /* Header: Dest. | Source | BSSID */
James Ketrenosc848d0a2005-08-24 21:56:24 -05007730 /* packets from our adapter are dropped (echo) */
7731 if (!memcmp(header->addr2, priv->net_dev->dev_addr, ETH_ALEN))
7732 return 0;
7733
Peter Jones90700fd2005-08-26 16:51:06 -05007734 /* {broad,multi}cast packets to our BSSID go through */
Stephen Hemminger3c190652006-01-03 15:27:38 -08007735 if (is_multicast_ether_addr(header->addr1))
James Ketrenosea2b26e2005-08-24 21:25:16 -05007736 return !memcmp(header->addr3, priv->bssid, ETH_ALEN);
James Ketrenosa613bff2005-08-24 21:43:11 -05007737
7738 /* packets to our adapter go through */
7739 return !memcmp(header->addr1, priv->net_dev->dev_addr,
7740 ETH_ALEN);
James Ketrenosa613bff2005-08-24 21:43:11 -05007741
Peter Jones90700fd2005-08-26 16:51:06 -05007742 case IW_MODE_INFRA: /* Header: Dest. | BSSID | Source */
James Ketrenosc848d0a2005-08-24 21:56:24 -05007743 /* packets from our adapter are dropped (echo) */
7744 if (!memcmp(header->addr3, priv->net_dev->dev_addr, ETH_ALEN))
7745 return 0;
7746
Peter Jones90700fd2005-08-26 16:51:06 -05007747 /* {broad,multi}cast packets to our BSS go through */
Stephen Hemminger3c190652006-01-03 15:27:38 -08007748 if (is_multicast_ether_addr(header->addr1))
James Ketrenosa613bff2005-08-24 21:43:11 -05007749 return !memcmp(header->addr2, priv->bssid, ETH_ALEN);
7750
7751 /* packets to our adapter go through */
7752 return !memcmp(header->addr1, priv->net_dev->dev_addr,
7753 ETH_ALEN);
James Ketrenosea2b26e2005-08-24 21:25:16 -05007754 }
James Ketrenosa613bff2005-08-24 21:43:11 -05007755
James Ketrenosea2b26e2005-08-24 21:25:16 -05007756 return 1;
7757}
7758
James Ketrenosafbf30a2005-08-25 00:05:33 -05007759#define IPW_PACKET_RETRY_TIME HZ
7760
Arjan van de Ven858119e2006-01-14 13:20:43 -08007761static int is_duplicate_packet(struct ipw_priv *priv,
James Ketrenosafbf30a2005-08-25 00:05:33 -05007762 struct ieee80211_hdr_4addr *header)
7763{
James Ketrenosafbf30a2005-08-25 00:05:33 -05007764 u16 sc = le16_to_cpu(header->seq_ctl);
7765 u16 seq = WLAN_GET_SEQ_SEQ(sc);
7766 u16 frag = WLAN_GET_SEQ_FRAG(sc);
7767 u16 *last_seq, *last_frag;
7768 unsigned long *last_time;
7769
7770 switch (priv->ieee->iw_mode) {
7771 case IW_MODE_ADHOC:
7772 {
7773 struct list_head *p;
7774 struct ipw_ibss_seq *entry = NULL;
7775 u8 *mac = header->addr2;
7776 int index = mac[5] % IPW_IBSS_MAC_HASH_SIZE;
7777
7778 __list_for_each(p, &priv->ibss_mac_hash[index]) {
7779 entry =
7780 list_entry(p, struct ipw_ibss_seq, list);
7781 if (!memcmp(entry->mac, mac, ETH_ALEN))
7782 break;
7783 }
7784 if (p == &priv->ibss_mac_hash[index]) {
7785 entry = kmalloc(sizeof(*entry), GFP_ATOMIC);
7786 if (!entry) {
7787 IPW_ERROR
7788 ("Cannot malloc new mac entry\n");
7789 return 0;
7790 }
7791 memcpy(entry->mac, mac, ETH_ALEN);
7792 entry->seq_num = seq;
7793 entry->frag_num = frag;
7794 entry->packet_time = jiffies;
7795 list_add(&entry->list,
7796 &priv->ibss_mac_hash[index]);
7797 return 0;
7798 }
7799 last_seq = &entry->seq_num;
7800 last_frag = &entry->frag_num;
7801 last_time = &entry->packet_time;
7802 break;
7803 }
7804 case IW_MODE_INFRA:
7805 last_seq = &priv->last_seq_num;
7806 last_frag = &priv->last_frag_num;
7807 last_time = &priv->last_packet_time;
7808 break;
7809 default:
7810 return 0;
7811 }
7812 if ((*last_seq == seq) &&
7813 time_after(*last_time + IPW_PACKET_RETRY_TIME, jiffies)) {
7814 if (*last_frag == frag)
7815 goto drop;
7816 if (*last_frag + 1 != frag)
7817 /* out-of-order fragment */
7818 goto drop;
James Ketrenosafbf30a2005-08-25 00:05:33 -05007819 } else
7820 *last_seq = seq;
7821
Zhu Yif57ce7c2005-07-13 12:22:15 -05007822 *last_frag = frag;
James Ketrenosafbf30a2005-08-25 00:05:33 -05007823 *last_time = jiffies;
7824 return 0;
7825
7826 drop:
Zhu Yi87b016c2005-08-05 17:17:35 +08007827 /* Comment this line now since we observed the card receives
7828 * duplicate packets but the FCTL_RETRY bit is not set in the
7829 * IBSS mode with fragmentation enabled.
7830 BUG_ON(!(le16_to_cpu(header->frame_ctl) & IEEE80211_FCTL_RETRY)); */
James Ketrenosafbf30a2005-08-25 00:05:33 -05007831 return 1;
7832}
7833
James Ketrenosb095c382005-08-24 22:04:42 -05007834static void ipw_handle_mgmt_packet(struct ipw_priv *priv,
7835 struct ipw_rx_mem_buffer *rxb,
7836 struct ieee80211_rx_stats *stats)
7837{
7838 struct sk_buff *skb = rxb->skb;
7839 struct ipw_rx_packet *pkt = (struct ipw_rx_packet *)skb->data;
7840 struct ieee80211_hdr_4addr *header = (struct ieee80211_hdr_4addr *)
7841 (skb->data + IPW_RX_FRAME_SIZE);
7842
7843 ieee80211_rx_mgt(priv->ieee, header, stats);
7844
7845 if (priv->ieee->iw_mode == IW_MODE_ADHOC &&
7846 ((WLAN_FC_GET_STYPE(le16_to_cpu(header->frame_ctl)) ==
7847 IEEE80211_STYPE_PROBE_RESP) ||
7848 (WLAN_FC_GET_STYPE(le16_to_cpu(header->frame_ctl)) ==
7849 IEEE80211_STYPE_BEACON))) {
7850 if (!memcmp(header->addr3, priv->bssid, ETH_ALEN))
7851 ipw_add_station(priv, header->addr2);
7852 }
7853
7854 if (priv->config & CFG_NET_STATS) {
7855 IPW_DEBUG_HC("sending stat packet\n");
7856
7857 /* Set the size of the skb to the size of the full
7858 * ipw header and 802.11 frame */
7859 skb_put(skb, le16_to_cpu(pkt->u.frame.length) +
7860 IPW_RX_FRAME_SIZE);
7861
7862 /* Advance past the ipw packet header to the 802.11 frame */
7863 skb_pull(skb, IPW_RX_FRAME_SIZE);
7864
7865 /* Push the ieee80211_rx_stats before the 802.11 frame */
7866 memcpy(skb_push(skb, sizeof(*stats)), stats, sizeof(*stats));
7867
7868 skb->dev = priv->ieee->dev;
7869
7870 /* Point raw at the ieee80211_stats */
7871 skb->mac.raw = skb->data;
7872
7873 skb->pkt_type = PACKET_OTHERHOST;
7874 skb->protocol = __constant_htons(ETH_P_80211_STATS);
7875 memset(skb->cb, 0, sizeof(rxb->skb->cb));
7876 netif_rx(skb);
7877 rxb->skb = NULL;
7878 }
James Ketrenos43f66a62005-03-25 12:31:53 -06007879}
7880
James Ketrenos43f66a62005-03-25 12:31:53 -06007881/*
7882 * Main entry function for recieving a packet with 80211 headers. This
7883 * should be called when ever the FW has notified us that there is a new
7884 * skb in the recieve queue.
7885 */
7886static void ipw_rx(struct ipw_priv *priv)
7887{
7888 struct ipw_rx_mem_buffer *rxb;
7889 struct ipw_rx_packet *pkt;
James Ketrenos0dacca12005-09-21 12:23:41 -05007890 struct ieee80211_hdr_4addr *header;
James Ketrenos43f66a62005-03-25 12:31:53 -06007891 u32 r, w, i;
7892 u8 network_packet;
7893
James Ketrenosb095c382005-08-24 22:04:42 -05007894 r = ipw_read32(priv, IPW_RX_READ_INDEX);
7895 w = ipw_read32(priv, IPW_RX_WRITE_INDEX);
James Ketrenos43f66a62005-03-25 12:31:53 -06007896 i = (priv->rxq->processed + 1) % RX_QUEUE_SIZE;
7897
7898 while (i != r) {
7899 rxb = priv->rxq->queue[i];
Brice Goglin0f52bf92005-12-01 01:41:46 -08007900#ifdef CONFIG_IPW2200_DEBUG
James Ketrenos43f66a62005-03-25 12:31:53 -06007901 if (unlikely(rxb == NULL)) {
7902 printk(KERN_CRIT "Queue not allocated!\n");
7903 break;
7904 }
7905#endif
7906 priv->rxq->queue[i] = NULL;
7907
7908 pci_dma_sync_single_for_cpu(priv->pci_dev, rxb->dma_addr,
James Ketrenosb095c382005-08-24 22:04:42 -05007909 IPW_RX_BUF_SIZE,
James Ketrenos43f66a62005-03-25 12:31:53 -06007910 PCI_DMA_FROMDEVICE);
7911
7912 pkt = (struct ipw_rx_packet *)rxb->skb->data;
7913 IPW_DEBUG_RX("Packet: type=%02X seq=%02X bits=%02X\n",
7914 pkt->header.message_type,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04007915 pkt->header.rx_seq_num, pkt->header.control_bits);
James Ketrenos43f66a62005-03-25 12:31:53 -06007916
7917 switch (pkt->header.message_type) {
Jeff Garzik0edd5b42005-09-07 00:48:31 -04007918 case RX_FRAME_TYPE: /* 802.11 frame */ {
7919 struct ieee80211_rx_stats stats = {
James Ketrenosc848d0a2005-08-24 21:56:24 -05007920 .rssi =
7921 le16_to_cpu(pkt->u.frame.rssi_dbm) -
Jeff Garzik0edd5b42005-09-07 00:48:31 -04007922 IPW_RSSI_TO_DBM,
James Ketrenosc848d0a2005-08-24 21:56:24 -05007923 .signal =
7924 le16_to_cpu(pkt->u.frame.signal),
7925 .noise =
7926 le16_to_cpu(pkt->u.frame.noise),
Jeff Garzik0edd5b42005-09-07 00:48:31 -04007927 .rate = pkt->u.frame.rate,
7928 .mac_time = jiffies,
7929 .received_channel =
7930 pkt->u.frame.received_channel,
7931 .freq =
7932 (pkt->u.frame.
7933 control & (1 << 0)) ?
7934 IEEE80211_24GHZ_BAND :
7935 IEEE80211_52GHZ_BAND,
James Ketrenosa613bff2005-08-24 21:43:11 -05007936 .len = le16_to_cpu(pkt->u.frame.length),
Jeff Garzik0edd5b42005-09-07 00:48:31 -04007937 };
James Ketrenos43f66a62005-03-25 12:31:53 -06007938
Jeff Garzik0edd5b42005-09-07 00:48:31 -04007939 if (stats.rssi != 0)
7940 stats.mask |= IEEE80211_STATMASK_RSSI;
7941 if (stats.signal != 0)
7942 stats.mask |= IEEE80211_STATMASK_SIGNAL;
James Ketrenosc848d0a2005-08-24 21:56:24 -05007943 if (stats.noise != 0)
7944 stats.mask |= IEEE80211_STATMASK_NOISE;
Jeff Garzik0edd5b42005-09-07 00:48:31 -04007945 if (stats.rate != 0)
7946 stats.mask |= IEEE80211_STATMASK_RATE;
James Ketrenos43f66a62005-03-25 12:31:53 -06007947
Jeff Garzik0edd5b42005-09-07 00:48:31 -04007948 priv->rx_packets++;
James Ketrenos43f66a62005-03-25 12:31:53 -06007949
James Ketrenosb095c382005-08-24 22:04:42 -05007950#ifdef CONFIG_IPW2200_MONITOR
Jeff Garzik0edd5b42005-09-07 00:48:31 -04007951 if (priv->ieee->iw_mode == IW_MODE_MONITOR) {
Mike Kershaw24a47db2005-08-26 00:41:54 -05007952#ifdef CONFIG_IEEE80211_RADIOTAP
7953 ipw_handle_data_packet_monitor(priv,
7954 rxb,
7955 &stats);
7956#else
Jeff Garzik0edd5b42005-09-07 00:48:31 -04007957 ipw_handle_data_packet(priv, rxb,
7958 &stats);
Mike Kershaw24a47db2005-08-26 00:41:54 -05007959#endif
Jeff Garzik0edd5b42005-09-07 00:48:31 -04007960 break;
7961 }
James Ketrenos43f66a62005-03-25 12:31:53 -06007962#endif
Jeff Garzikbf794512005-07-31 13:07:26 -04007963
Jeff Garzik0edd5b42005-09-07 00:48:31 -04007964 header =
James Ketrenos0dacca12005-09-21 12:23:41 -05007965 (struct ieee80211_hdr_4addr *)(rxb->skb->
7966 data +
7967 IPW_RX_FRAME_SIZE);
James Ketrenos43f66a62005-03-25 12:31:53 -06007968 /* TODO: Check Ad-Hoc dest/source and make sure
7969 * that we are actually parsing these packets
Jeff Garzikbf794512005-07-31 13:07:26 -04007970 * correctly -- we should probably use the
James Ketrenos43f66a62005-03-25 12:31:53 -06007971 * frame control of the packet and disregard
7972 * the current iw_mode */
James Ketrenos43f66a62005-03-25 12:31:53 -06007973
James Ketrenosea2b26e2005-08-24 21:25:16 -05007974 network_packet =
7975 is_network_packet(priv, header);
Jeff Garzik0edd5b42005-09-07 00:48:31 -04007976 if (network_packet && priv->assoc_network) {
7977 priv->assoc_network->stats.rssi =
7978 stats.rssi;
7979 average_add(&priv->average_rssi,
7980 stats.rssi);
7981 priv->last_rx_rssi = stats.rssi;
7982 }
7983
7984 IPW_DEBUG_RX("Frame: len=%u\n",
James Ketrenosa613bff2005-08-24 21:43:11 -05007985 le16_to_cpu(pkt->u.frame.length));
Jeff Garzik0edd5b42005-09-07 00:48:31 -04007986
James Ketrenosa613bff2005-08-24 21:43:11 -05007987 if (le16_to_cpu(pkt->u.frame.length) <
7988 frame_hdr_len(header)) {
Jeff Garzik0edd5b42005-09-07 00:48:31 -04007989 IPW_DEBUG_DROP
7990 ("Received packet is too small. "
7991 "Dropping.\n");
7992 priv->ieee->stats.rx_errors++;
7993 priv->wstats.discard.misc++;
7994 break;
7995 }
7996
James Ketrenosa613bff2005-08-24 21:43:11 -05007997 switch (WLAN_FC_GET_TYPE
7998 (le16_to_cpu(header->frame_ctl))) {
James Ketrenosb095c382005-08-24 22:04:42 -05007999
Jeff Garzik0edd5b42005-09-07 00:48:31 -04008000 case IEEE80211_FTYPE_MGMT:
James Ketrenosb095c382005-08-24 22:04:42 -05008001 ipw_handle_mgmt_packet(priv, rxb,
8002 &stats);
Jeff Garzik0edd5b42005-09-07 00:48:31 -04008003 break;
8004
8005 case IEEE80211_FTYPE_CTL:
8006 break;
8007
8008 case IEEE80211_FTYPE_DATA:
James Ketrenosafbf30a2005-08-25 00:05:33 -05008009 if (unlikely(!network_packet ||
8010 is_duplicate_packet(priv,
8011 header)))
8012 {
Jeff Garzik0edd5b42005-09-07 00:48:31 -04008013 IPW_DEBUG_DROP("Dropping: "
8014 MAC_FMT ", "
8015 MAC_FMT ", "
8016 MAC_FMT "\n",
8017 MAC_ARG(header->
8018 addr1),
8019 MAC_ARG(header->
8020 addr2),
8021 MAC_ARG(header->
8022 addr3));
James Ketrenosb095c382005-08-24 22:04:42 -05008023 break;
8024 }
8025
8026 ipw_handle_data_packet(priv, rxb,
8027 &stats);
8028
Jeff Garzik0edd5b42005-09-07 00:48:31 -04008029 break;
8030 }
James Ketrenos43f66a62005-03-25 12:31:53 -06008031 break;
8032 }
Jeff Garzikbf794512005-07-31 13:07:26 -04008033
Jeff Garzik0edd5b42005-09-07 00:48:31 -04008034 case RX_HOST_NOTIFICATION_TYPE:{
8035 IPW_DEBUG_RX
8036 ("Notification: subtype=%02X flags=%02X size=%d\n",
James Ketrenos43f66a62005-03-25 12:31:53 -06008037 pkt->u.notification.subtype,
8038 pkt->u.notification.flags,
8039 pkt->u.notification.size);
Jeff Garzik0edd5b42005-09-07 00:48:31 -04008040 ipw_rx_notification(priv, &pkt->u.notification);
8041 break;
8042 }
James Ketrenos43f66a62005-03-25 12:31:53 -06008043
8044 default:
8045 IPW_DEBUG_RX("Bad Rx packet of type %d\n",
8046 pkt->header.message_type);
8047 break;
8048 }
Jeff Garzikbf794512005-07-31 13:07:26 -04008049
8050 /* For now we just don't re-use anything. We can tweak this
8051 * later to try and re-use notification packets and SKBs that
James Ketrenos43f66a62005-03-25 12:31:53 -06008052 * fail to Rx correctly */
8053 if (rxb->skb != NULL) {
8054 dev_kfree_skb_any(rxb->skb);
8055 rxb->skb = NULL;
8056 }
Jeff Garzikbf794512005-07-31 13:07:26 -04008057
James Ketrenos43f66a62005-03-25 12:31:53 -06008058 pci_unmap_single(priv->pci_dev, rxb->dma_addr,
James Ketrenosb095c382005-08-24 22:04:42 -05008059 IPW_RX_BUF_SIZE, PCI_DMA_FROMDEVICE);
James Ketrenos43f66a62005-03-25 12:31:53 -06008060 list_add_tail(&rxb->list, &priv->rxq->rx_used);
Jeff Garzikbf794512005-07-31 13:07:26 -04008061
James Ketrenos43f66a62005-03-25 12:31:53 -06008062 i = (i + 1) % RX_QUEUE_SIZE;
8063 }
8064
8065 /* Backtrack one entry */
8066 priv->rxq->processed = (i ? i : RX_QUEUE_SIZE) - 1;
8067
8068 ipw_rx_queue_restock(priv);
8069}
8070
James Ketrenosafbf30a2005-08-25 00:05:33 -05008071#define DEFAULT_RTS_THRESHOLD 2304U
8072#define MIN_RTS_THRESHOLD 1U
8073#define MAX_RTS_THRESHOLD 2304U
8074#define DEFAULT_BEACON_INTERVAL 100U
8075#define DEFAULT_SHORT_RETRY_LIMIT 7U
8076#define DEFAULT_LONG_RETRY_LIMIT 4U
8077
8078static int ipw_sw_reset(struct ipw_priv *priv, int init)
James Ketrenos43f66a62005-03-25 12:31:53 -06008079{
James Ketrenosafbf30a2005-08-25 00:05:33 -05008080 int band, modulation;
8081 int old_mode = priv->ieee->iw_mode;
James Ketrenos43f66a62005-03-25 12:31:53 -06008082
James Ketrenosafbf30a2005-08-25 00:05:33 -05008083 /* Initialize module parameter values here */
8084 priv->config = 0;
James Ketrenos43f66a62005-03-25 12:31:53 -06008085
James Ketrenosafbf30a2005-08-25 00:05:33 -05008086 /* We default to disabling the LED code as right now it causes
8087 * too many systems to lock up... */
8088 if (!led)
8089 priv->config |= CFG_NO_LED;
James Ketrenos43f66a62005-03-25 12:31:53 -06008090
James Ketrenosafbf30a2005-08-25 00:05:33 -05008091 if (associate)
8092 priv->config |= CFG_ASSOCIATE;
8093 else
8094 IPW_DEBUG_INFO("Auto associate disabled.\n");
Jeff Garzikbf794512005-07-31 13:07:26 -04008095
James Ketrenosafbf30a2005-08-25 00:05:33 -05008096 if (auto_create)
8097 priv->config |= CFG_ADHOC_CREATE;
8098 else
8099 IPW_DEBUG_INFO("Auto adhoc creation disabled.\n");
8100
Zhu Yi17ed0812006-01-24 16:36:31 +08008101 priv->config &= ~CFG_STATIC_ESSID;
8102 priv->essid_len = 0;
8103 memset(priv->essid, 0, IW_ESSID_MAX_SIZE);
8104
James Ketrenosafbf30a2005-08-25 00:05:33 -05008105 if (disable) {
8106 priv->status |= STATUS_RF_KILL_SW;
8107 IPW_DEBUG_INFO("Radio disabled.\n");
James Ketrenos43f66a62005-03-25 12:31:53 -06008108 }
8109
James Ketrenosafbf30a2005-08-25 00:05:33 -05008110 if (channel != 0) {
8111 priv->config |= CFG_STATIC_CHANNEL;
8112 priv->channel = channel;
8113 IPW_DEBUG_INFO("Bind to static channel %d\n", channel);
8114 /* TODO: Validate that provided channel is in range */
8115 }
8116#ifdef CONFIG_IPW_QOS
8117 ipw_qos_init(priv, qos_enable, qos_burst_enable,
8118 burst_duration_CCK, burst_duration_OFDM);
8119#endif /* CONFIG_IPW_QOS */
8120
8121 switch (mode) {
8122 case 1:
8123 priv->ieee->iw_mode = IW_MODE_ADHOC;
8124 priv->net_dev->type = ARPHRD_ETHER;
8125
8126 break;
8127#ifdef CONFIG_IPW2200_MONITOR
8128 case 2:
8129 priv->ieee->iw_mode = IW_MODE_MONITOR;
Mike Kershaw24a47db2005-08-26 00:41:54 -05008130#ifdef CONFIG_IEEE80211_RADIOTAP
8131 priv->net_dev->type = ARPHRD_IEEE80211_RADIOTAP;
8132#else
James Ketrenosafbf30a2005-08-25 00:05:33 -05008133 priv->net_dev->type = ARPHRD_IEEE80211;
Mike Kershaw24a47db2005-08-26 00:41:54 -05008134#endif
James Ketrenosafbf30a2005-08-25 00:05:33 -05008135 break;
8136#endif
8137 default:
8138 case 0:
8139 priv->net_dev->type = ARPHRD_ETHER;
8140 priv->ieee->iw_mode = IW_MODE_INFRA;
8141 break;
James Ketrenos43f66a62005-03-25 12:31:53 -06008142 }
Jeff Garzikbf794512005-07-31 13:07:26 -04008143
James Ketrenosafbf30a2005-08-25 00:05:33 -05008144 if (hwcrypto) {
8145 priv->ieee->host_encrypt = 0;
8146 priv->ieee->host_encrypt_msdu = 0;
8147 priv->ieee->host_decrypt = 0;
Hong Liu567deaf2005-08-31 18:07:22 +08008148 priv->ieee->host_mc_decrypt = 0;
James Ketrenos43f66a62005-03-25 12:31:53 -06008149 }
James Ketrenosafbf30a2005-08-25 00:05:33 -05008150 IPW_DEBUG_INFO("Hardware crypto [%s]\n", hwcrypto ? "on" : "off");
James Ketrenos43f66a62005-03-25 12:31:53 -06008151
Zhu Yie402c932005-08-05 17:20:40 +08008152 /* IPW2200/2915 is abled to do hardware fragmentation. */
8153 priv->ieee->host_open_frag = 0;
James Ketrenos43f66a62005-03-25 12:31:53 -06008154
James Ketrenosafbf30a2005-08-25 00:05:33 -05008155 if ((priv->pci_dev->device == 0x4223) ||
8156 (priv->pci_dev->device == 0x4224)) {
8157 if (init)
8158 printk(KERN_INFO DRV_NAME
8159 ": Detected Intel PRO/Wireless 2915ABG Network "
8160 "Connection\n");
8161 priv->ieee->abg_true = 1;
8162 band = IEEE80211_52GHZ_BAND | IEEE80211_24GHZ_BAND;
8163 modulation = IEEE80211_OFDM_MODULATION |
8164 IEEE80211_CCK_MODULATION;
8165 priv->adapter = IPW_2915ABG;
8166 priv->ieee->mode = IEEE_A | IEEE_G | IEEE_B;
James Ketrenos43f66a62005-03-25 12:31:53 -06008167 } else {
James Ketrenosafbf30a2005-08-25 00:05:33 -05008168 if (init)
8169 printk(KERN_INFO DRV_NAME
8170 ": Detected Intel PRO/Wireless 2200BG Network "
8171 "Connection\n");
8172
8173 priv->ieee->abg_true = 0;
8174 band = IEEE80211_24GHZ_BAND;
8175 modulation = IEEE80211_OFDM_MODULATION |
8176 IEEE80211_CCK_MODULATION;
8177 priv->adapter = IPW_2200BG;
8178 priv->ieee->mode = IEEE_G | IEEE_B;
James Ketrenos43f66a62005-03-25 12:31:53 -06008179 }
Jeff Garzikbf794512005-07-31 13:07:26 -04008180
James Ketrenosafbf30a2005-08-25 00:05:33 -05008181 priv->ieee->freq_band = band;
8182 priv->ieee->modulation = modulation;
Jeff Garzikbf794512005-07-31 13:07:26 -04008183
James Ketrenosafbf30a2005-08-25 00:05:33 -05008184 priv->rates_mask = IEEE80211_DEFAULT_RATES_MASK;
James Ketrenos43f66a62005-03-25 12:31:53 -06008185
James Ketrenosafbf30a2005-08-25 00:05:33 -05008186 priv->disassociate_threshold = IPW_MB_DISASSOCIATE_THRESHOLD_DEFAULT;
8187 priv->roaming_threshold = IPW_MB_ROAMING_THRESHOLD_DEFAULT;
James Ketrenos43f66a62005-03-25 12:31:53 -06008188
James Ketrenosafbf30a2005-08-25 00:05:33 -05008189 priv->rts_threshold = DEFAULT_RTS_THRESHOLD;
8190 priv->short_retry_limit = DEFAULT_SHORT_RETRY_LIMIT;
8191 priv->long_retry_limit = DEFAULT_LONG_RETRY_LIMIT;
Jeff Garzikbf794512005-07-31 13:07:26 -04008192
James Ketrenosafbf30a2005-08-25 00:05:33 -05008193 /* If power management is turned on, default to AC mode */
8194 priv->power_mode = IPW_POWER_AC;
8195 priv->tx_power = IPW_TX_POWER_DEFAULT;
James Ketrenos43f66a62005-03-25 12:31:53 -06008196
Zhu Yi0ece35b2005-08-05 17:26:51 +08008197 return old_mode == priv->ieee->iw_mode;
James Ketrenos43f66a62005-03-25 12:31:53 -06008198}
8199
8200/*
8201 * This file defines the Wireless Extension handlers. It does not
8202 * define any methods of hardware manipulation and relies on the
8203 * functions defined in ipw_main to provide the HW interaction.
Jeff Garzikbf794512005-07-31 13:07:26 -04008204 *
8205 * The exception to this is the use of the ipw_get_ordinal()
James Ketrenos43f66a62005-03-25 12:31:53 -06008206 * function used to poll the hardware vs. making unecessary calls.
8207 *
8208 */
8209
Jeff Garzikbf794512005-07-31 13:07:26 -04008210static int ipw_wx_get_name(struct net_device *dev,
8211 struct iw_request_info *info,
James Ketrenos43f66a62005-03-25 12:31:53 -06008212 union iwreq_data *wrqu, char *extra)
8213{
8214 struct ipw_priv *priv = ieee80211_priv(dev);
James Ketrenosc848d0a2005-08-24 21:56:24 -05008215 down(&priv->sem);
8216 if (priv->status & STATUS_RF_KILL_MASK)
James Ketrenosa613bff2005-08-24 21:43:11 -05008217 strcpy(wrqu->name, "radio off");
James Ketrenosc848d0a2005-08-24 21:56:24 -05008218 else if (!(priv->status & STATUS_ASSOCIATED))
James Ketrenos43f66a62005-03-25 12:31:53 -06008219 strcpy(wrqu->name, "unassociated");
Jeff Garzikbf794512005-07-31 13:07:26 -04008220 else
James Ketrenos43f66a62005-03-25 12:31:53 -06008221 snprintf(wrqu->name, IFNAMSIZ, "IEEE 802.11%c",
8222 ipw_modes[priv->assoc_request.ieee_mode]);
8223 IPW_DEBUG_WX("Name: %s\n", wrqu->name);
James Ketrenosc848d0a2005-08-24 21:56:24 -05008224 up(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06008225 return 0;
8226}
8227
8228static int ipw_set_channel(struct ipw_priv *priv, u8 channel)
8229{
8230 if (channel == 0) {
8231 IPW_DEBUG_INFO("Setting channel to ANY (0)\n");
8232 priv->config &= ~CFG_STATIC_CHANNEL;
James Ketrenosc848d0a2005-08-24 21:56:24 -05008233 IPW_DEBUG_ASSOC("Attempting to associate with new "
8234 "parameters.\n");
8235 ipw_associate(priv);
James Ketrenos43f66a62005-03-25 12:31:53 -06008236 return 0;
8237 }
8238
8239 priv->config |= CFG_STATIC_CHANNEL;
8240
8241 if (priv->channel == channel) {
Jeff Garzik0edd5b42005-09-07 00:48:31 -04008242 IPW_DEBUG_INFO("Request to set channel to current value (%d)\n",
8243 channel);
James Ketrenos43f66a62005-03-25 12:31:53 -06008244 return 0;
8245 }
8246
8247 IPW_DEBUG_INFO("Setting channel to %i\n", (int)channel);
8248 priv->channel = channel;
8249
James Ketrenosb095c382005-08-24 22:04:42 -05008250#ifdef CONFIG_IPW2200_MONITOR
8251 if (priv->ieee->iw_mode == IW_MODE_MONITOR) {
James Ketrenosafbf30a2005-08-25 00:05:33 -05008252 int i;
James Ketrenosb095c382005-08-24 22:04:42 -05008253 if (priv->status & STATUS_SCANNING) {
James Ketrenosafbf30a2005-08-25 00:05:33 -05008254 IPW_DEBUG_SCAN("Scan abort triggered due to "
James Ketrenosb095c382005-08-24 22:04:42 -05008255 "channel change.\n");
James Ketrenosafbf30a2005-08-25 00:05:33 -05008256 ipw_abort_scan(priv);
James Ketrenosb095c382005-08-24 22:04:42 -05008257 }
8258
8259 for (i = 1000; i && (priv->status & STATUS_SCANNING); i--)
8260 udelay(10);
8261
8262 if (priv->status & STATUS_SCANNING)
8263 IPW_DEBUG_SCAN("Still scanning...\n");
8264 else
8265 IPW_DEBUG_SCAN("Took %dms to abort current scan\n",
8266 1000 - i);
8267
8268 return 0;
James Ketrenos43f66a62005-03-25 12:31:53 -06008269 }
James Ketrenosb095c382005-08-24 22:04:42 -05008270#endif /* CONFIG_IPW2200_MONITOR */
8271
James Ketrenosc848d0a2005-08-24 21:56:24 -05008272 /* Network configuration changed -- force [re]association */
8273 IPW_DEBUG_ASSOC("[re]association triggered due to channel change.\n");
8274 if (!ipw_disassociate(priv))
James Ketrenos43f66a62005-03-25 12:31:53 -06008275 ipw_associate(priv);
James Ketrenos43f66a62005-03-25 12:31:53 -06008276
8277 return 0;
8278}
8279
Jeff Garzikbf794512005-07-31 13:07:26 -04008280static int ipw_wx_set_freq(struct net_device *dev,
8281 struct iw_request_info *info,
8282 union iwreq_data *wrqu, char *extra)
James Ketrenos43f66a62005-03-25 12:31:53 -06008283{
8284 struct ipw_priv *priv = ieee80211_priv(dev);
Liu Hong1fe0adb2005-08-19 09:33:10 -05008285 const struct ieee80211_geo *geo = ipw_get_geo(priv->ieee);
James Ketrenos43f66a62005-03-25 12:31:53 -06008286 struct iw_freq *fwrq = &wrqu->freq;
James Ketrenosafbf30a2005-08-25 00:05:33 -05008287 int ret = 0, i;
Liu Hong1fe0adb2005-08-19 09:33:10 -05008288 u8 channel, flags;
8289 int band;
Jeff Garzikbf794512005-07-31 13:07:26 -04008290
James Ketrenosb095c382005-08-24 22:04:42 -05008291 if (fwrq->m == 0) {
8292 IPW_DEBUG_WX("SET Freq/Channel -> any\n");
8293 down(&priv->sem);
8294 ret = ipw_set_channel(priv, 0);
8295 up(&priv->sem);
8296 return ret;
8297 }
James Ketrenos43f66a62005-03-25 12:31:53 -06008298 /* if setting by freq convert to channel */
8299 if (fwrq->e == 1) {
Liu Hong1fe0adb2005-08-19 09:33:10 -05008300 channel = ipw_freq_to_channel(priv->ieee, fwrq->m);
James Ketrenosb095c382005-08-24 22:04:42 -05008301 if (channel == 0)
8302 return -EINVAL;
8303 } else
8304 channel = fwrq->m;
Jeff Garzikbf794512005-07-31 13:07:26 -04008305
Liu Hong1fe0adb2005-08-19 09:33:10 -05008306 if (!(band = ipw_is_valid_channel(priv->ieee, channel)))
James Ketrenosb095c382005-08-24 22:04:42 -05008307 return -EINVAL;
Jeff Garzikbf794512005-07-31 13:07:26 -04008308
Liu Hong1fe0adb2005-08-19 09:33:10 -05008309 if (priv->ieee->iw_mode == IW_MODE_ADHOC) {
8310 i = ipw_channel_to_index(priv->ieee, channel);
James Ketrenosafbf30a2005-08-25 00:05:33 -05008311 if (i == -1)
8312 return -EINVAL;
Liu Hong1fe0adb2005-08-19 09:33:10 -05008313
8314 flags = (band == IEEE80211_24GHZ_BAND) ?
8315 geo->bg[i].flags : geo->a[i].flags;
8316 if (flags & IEEE80211_CH_PASSIVE_ONLY) {
James Ketrenosafbf30a2005-08-25 00:05:33 -05008317 IPW_DEBUG_WX("Invalid Ad-Hoc channel for 802.11a\n");
8318 return -EINVAL;
James Ketrenos43f66a62005-03-25 12:31:53 -06008319 }
8320 }
Jeff Garzikbf794512005-07-31 13:07:26 -04008321
James Ketrenos43f66a62005-03-25 12:31:53 -06008322 IPW_DEBUG_WX("SET Freq/Channel -> %d \n", fwrq->m);
James Ketrenosc848d0a2005-08-24 21:56:24 -05008323 down(&priv->sem);
James Ketrenosb095c382005-08-24 22:04:42 -05008324 ret = ipw_set_channel(priv, channel);
James Ketrenosc848d0a2005-08-24 21:56:24 -05008325 up(&priv->sem);
8326 return ret;
James Ketrenos43f66a62005-03-25 12:31:53 -06008327}
8328
Jeff Garzikbf794512005-07-31 13:07:26 -04008329static int ipw_wx_get_freq(struct net_device *dev,
8330 struct iw_request_info *info,
James Ketrenos43f66a62005-03-25 12:31:53 -06008331 union iwreq_data *wrqu, char *extra)
8332{
8333 struct ipw_priv *priv = ieee80211_priv(dev);
8334
8335 wrqu->freq.e = 0;
8336
8337 /* If we are associated, trying to associate, or have a statically
8338 * configured CHANNEL then return that; otherwise return ANY */
James Ketrenosc848d0a2005-08-24 21:56:24 -05008339 down(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06008340 if (priv->config & CFG_STATIC_CHANNEL ||
8341 priv->status & (STATUS_ASSOCIATING | STATUS_ASSOCIATED))
8342 wrqu->freq.m = priv->channel;
Jeff Garzikbf794512005-07-31 13:07:26 -04008343 else
James Ketrenos43f66a62005-03-25 12:31:53 -06008344 wrqu->freq.m = 0;
8345
James Ketrenosc848d0a2005-08-24 21:56:24 -05008346 up(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06008347 IPW_DEBUG_WX("GET Freq/Channel -> %d \n", priv->channel);
8348 return 0;
8349}
8350
Jeff Garzikbf794512005-07-31 13:07:26 -04008351static int ipw_wx_set_mode(struct net_device *dev,
8352 struct iw_request_info *info,
James Ketrenos43f66a62005-03-25 12:31:53 -06008353 union iwreq_data *wrqu, char *extra)
8354{
8355 struct ipw_priv *priv = ieee80211_priv(dev);
8356 int err = 0;
8357
8358 IPW_DEBUG_WX("Set MODE: %d\n", wrqu->mode);
8359
James Ketrenos43f66a62005-03-25 12:31:53 -06008360 switch (wrqu->mode) {
James Ketrenosb095c382005-08-24 22:04:42 -05008361#ifdef CONFIG_IPW2200_MONITOR
James Ketrenos43f66a62005-03-25 12:31:53 -06008362 case IW_MODE_MONITOR:
8363#endif
8364 case IW_MODE_ADHOC:
8365 case IW_MODE_INFRA:
8366 break;
8367 case IW_MODE_AUTO:
8368 wrqu->mode = IW_MODE_INFRA;
8369 break;
8370 default:
8371 return -EINVAL;
8372 }
James Ketrenosb095c382005-08-24 22:04:42 -05008373 if (wrqu->mode == priv->ieee->iw_mode)
8374 return 0;
James Ketrenos43f66a62005-03-25 12:31:53 -06008375
James Ketrenosb095c382005-08-24 22:04:42 -05008376 down(&priv->sem);
James Ketrenosafbf30a2005-08-25 00:05:33 -05008377
8378 ipw_sw_reset(priv, 0);
8379
James Ketrenosb095c382005-08-24 22:04:42 -05008380#ifdef CONFIG_IPW2200_MONITOR
Jeff Garzikbf794512005-07-31 13:07:26 -04008381 if (priv->ieee->iw_mode == IW_MODE_MONITOR)
James Ketrenos43f66a62005-03-25 12:31:53 -06008382 priv->net_dev->type = ARPHRD_ETHER;
Jeff Garzikbf794512005-07-31 13:07:26 -04008383
8384 if (wrqu->mode == IW_MODE_MONITOR)
Mike Kershaw24a47db2005-08-26 00:41:54 -05008385#ifdef CONFIG_IEEE80211_RADIOTAP
8386 priv->net_dev->type = ARPHRD_IEEE80211_RADIOTAP;
8387#else
James Ketrenos43f66a62005-03-25 12:31:53 -06008388 priv->net_dev->type = ARPHRD_IEEE80211;
Mike Kershaw24a47db2005-08-26 00:41:54 -05008389#endif
James Ketrenosb095c382005-08-24 22:04:42 -05008390#endif /* CONFIG_IPW2200_MONITOR */
Jeff Garzikbf794512005-07-31 13:07:26 -04008391
Jeff Garzikbf794512005-07-31 13:07:26 -04008392 /* Free the existing firmware and reset the fw_loaded
James Ketrenos43f66a62005-03-25 12:31:53 -06008393 * flag so ipw_load() will bring in the new firmawre */
James Ketrenosafbf30a2005-08-25 00:05:33 -05008394 free_firmware();
James Ketrenos43f66a62005-03-25 12:31:53 -06008395
8396 priv->ieee->iw_mode = wrqu->mode;
Jeff Garzikbf794512005-07-31 13:07:26 -04008397
James Ketrenosc848d0a2005-08-24 21:56:24 -05008398 queue_work(priv->workqueue, &priv->adapter_restart);
8399 up(&priv->sem);
Jeff Garzik0edd5b42005-09-07 00:48:31 -04008400 return err;
James Ketrenos43f66a62005-03-25 12:31:53 -06008401}
8402
Jeff Garzikbf794512005-07-31 13:07:26 -04008403static int ipw_wx_get_mode(struct net_device *dev,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04008404 struct iw_request_info *info,
8405 union iwreq_data *wrqu, char *extra)
James Ketrenos43f66a62005-03-25 12:31:53 -06008406{
8407 struct ipw_priv *priv = ieee80211_priv(dev);
James Ketrenosc848d0a2005-08-24 21:56:24 -05008408 down(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06008409 wrqu->mode = priv->ieee->iw_mode;
8410 IPW_DEBUG_WX("Get MODE -> %d\n", wrqu->mode);
James Ketrenosc848d0a2005-08-24 21:56:24 -05008411 up(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06008412 return 0;
8413}
8414
James Ketrenos43f66a62005-03-25 12:31:53 -06008415/* Values are in microsecond */
8416static const s32 timeout_duration[] = {
8417 350000,
8418 250000,
8419 75000,
8420 37000,
8421 25000,
8422};
8423
8424static const s32 period_duration[] = {
8425 400000,
8426 700000,
8427 1000000,
8428 1000000,
8429 1000000
8430};
8431
Jeff Garzikbf794512005-07-31 13:07:26 -04008432static int ipw_wx_get_range(struct net_device *dev,
8433 struct iw_request_info *info,
James Ketrenos43f66a62005-03-25 12:31:53 -06008434 union iwreq_data *wrqu, char *extra)
8435{
8436 struct ipw_priv *priv = ieee80211_priv(dev);
8437 struct iw_range *range = (struct iw_range *)extra;
Liu Hong1fe0adb2005-08-19 09:33:10 -05008438 const struct ieee80211_geo *geo = ipw_get_geo(priv->ieee);
James Ketrenosb095c382005-08-24 22:04:42 -05008439 int i = 0, j;
James Ketrenos43f66a62005-03-25 12:31:53 -06008440
8441 wrqu->data.length = sizeof(*range);
8442 memset(range, 0, sizeof(*range));
8443
8444 /* 54Mbs == ~27 Mb/s real (802.11g) */
Jeff Garzikbf794512005-07-31 13:07:26 -04008445 range->throughput = 27 * 1000 * 1000;
James Ketrenos43f66a62005-03-25 12:31:53 -06008446
8447 range->max_qual.qual = 100;
8448 /* TODO: Find real max RSSI and stick here */
8449 range->max_qual.level = 0;
James Ketrenosc848d0a2005-08-24 21:56:24 -05008450 range->max_qual.noise = priv->ieee->worst_rssi + 0x100;
Jeff Garzik0edd5b42005-09-07 00:48:31 -04008451 range->max_qual.updated = 7; /* Updated all three */
James Ketrenos43f66a62005-03-25 12:31:53 -06008452
8453 range->avg_qual.qual = 70;
8454 /* TODO: Find real 'good' to 'bad' threshol value for RSSI */
Jeff Garzik0edd5b42005-09-07 00:48:31 -04008455 range->avg_qual.level = 0; /* FIXME to real average level */
James Ketrenos43f66a62005-03-25 12:31:53 -06008456 range->avg_qual.noise = 0;
Jeff Garzik0edd5b42005-09-07 00:48:31 -04008457 range->avg_qual.updated = 7; /* Updated all three */
James Ketrenosc848d0a2005-08-24 21:56:24 -05008458 down(&priv->sem);
Jeff Garzik0edd5b42005-09-07 00:48:31 -04008459 range->num_bitrates = min(priv->rates.num_rates, (u8) IW_MAX_BITRATES);
James Ketrenos43f66a62005-03-25 12:31:53 -06008460
Jeff Garzikbf794512005-07-31 13:07:26 -04008461 for (i = 0; i < range->num_bitrates; i++)
8462 range->bitrate[i] = (priv->rates.supported_rates[i] & 0x7F) *
Jeff Garzik0edd5b42005-09-07 00:48:31 -04008463 500000;
Jeff Garzikbf794512005-07-31 13:07:26 -04008464
James Ketrenos43f66a62005-03-25 12:31:53 -06008465 range->max_rts = DEFAULT_RTS_THRESHOLD;
8466 range->min_frag = MIN_FRAG_THRESHOLD;
8467 range->max_frag = MAX_FRAG_THRESHOLD;
8468
8469 range->encoding_size[0] = 5;
Jeff Garzikbf794512005-07-31 13:07:26 -04008470 range->encoding_size[1] = 13;
James Ketrenos43f66a62005-03-25 12:31:53 -06008471 range->num_encoding_sizes = 2;
8472 range->max_encoding_tokens = WEP_KEYS;
8473
8474 /* Set the Wireless Extension versions */
8475 range->we_version_compiled = WIRELESS_EXT;
8476 range->we_version_source = 16;
8477
James Ketrenosb095c382005-08-24 22:04:42 -05008478 i = 0;
8479 if (priv->ieee->mode & (IEEE_B | IEEE_G)) {
8480 for (j = 0; j < geo->bg_channels && i < IW_MAX_FREQUENCIES;
8481 i++, j++) {
8482 range->freq[i].i = geo->bg[j].channel;
8483 range->freq[i].m = geo->bg[j].freq * 100000;
8484 range->freq[i].e = 1;
8485 }
James Ketrenos43f66a62005-03-25 12:31:53 -06008486 }
James Ketrenosb095c382005-08-24 22:04:42 -05008487
8488 if (priv->ieee->mode & IEEE_A) {
8489 for (j = 0; j < geo->a_channels && i < IW_MAX_FREQUENCIES;
8490 i++, j++) {
8491 range->freq[i].i = geo->a[j].channel;
8492 range->freq[i].m = geo->a[j].freq * 100000;
8493 range->freq[i].e = 1;
8494 }
8495 }
8496
8497 range->num_channels = i;
8498 range->num_frequency = i;
8499
James Ketrenosc848d0a2005-08-24 21:56:24 -05008500 up(&priv->sem);
Benoit Boissinot97a78ca2005-09-15 17:30:28 +00008501
8502 /* Event capability (kernel + driver) */
8503 range->event_capa[0] = (IW_EVENT_CAPA_K_0 |
8504 IW_EVENT_CAPA_MASK(SIOCGIWTHRSPY) |
8505 IW_EVENT_CAPA_MASK(SIOCGIWAP));
8506 range->event_capa[1] = IW_EVENT_CAPA_K_1;
James Ketrenos43f66a62005-03-25 12:31:53 -06008507
8508 IPW_DEBUG_WX("GET Range\n");
8509 return 0;
8510}
8511
Jeff Garzikbf794512005-07-31 13:07:26 -04008512static int ipw_wx_set_wap(struct net_device *dev,
8513 struct iw_request_info *info,
James Ketrenos43f66a62005-03-25 12:31:53 -06008514 union iwreq_data *wrqu, char *extra)
8515{
8516 struct ipw_priv *priv = ieee80211_priv(dev);
8517
8518 static const unsigned char any[] = {
8519 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
8520 };
8521 static const unsigned char off[] = {
8522 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
8523 };
8524
Jeff Garzikbf794512005-07-31 13:07:26 -04008525 if (wrqu->ap_addr.sa_family != ARPHRD_ETHER)
James Ketrenos43f66a62005-03-25 12:31:53 -06008526 return -EINVAL;
James Ketrenosc848d0a2005-08-24 21:56:24 -05008527 down(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06008528 if (!memcmp(any, wrqu->ap_addr.sa_data, ETH_ALEN) ||
8529 !memcmp(off, wrqu->ap_addr.sa_data, ETH_ALEN)) {
8530 /* we disable mandatory BSSID association */
8531 IPW_DEBUG_WX("Setting AP BSSID to ANY\n");
8532 priv->config &= ~CFG_STATIC_BSSID;
James Ketrenosc848d0a2005-08-24 21:56:24 -05008533 IPW_DEBUG_ASSOC("Attempting to associate with new "
8534 "parameters.\n");
8535 ipw_associate(priv);
8536 up(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06008537 return 0;
8538 }
8539
8540 priv->config |= CFG_STATIC_BSSID;
8541 if (!memcmp(priv->bssid, wrqu->ap_addr.sa_data, ETH_ALEN)) {
8542 IPW_DEBUG_WX("BSSID set to current BSSID.\n");
James Ketrenosc848d0a2005-08-24 21:56:24 -05008543 up(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06008544 return 0;
8545 }
8546
8547 IPW_DEBUG_WX("Setting mandatory BSSID to " MAC_FMT "\n",
8548 MAC_ARG(wrqu->ap_addr.sa_data));
8549
8550 memcpy(priv->bssid, wrqu->ap_addr.sa_data, ETH_ALEN);
8551
James Ketrenosc848d0a2005-08-24 21:56:24 -05008552 /* Network configuration changed -- force [re]association */
8553 IPW_DEBUG_ASSOC("[re]association triggered due to BSSID change.\n");
8554 if (!ipw_disassociate(priv))
James Ketrenos43f66a62005-03-25 12:31:53 -06008555 ipw_associate(priv);
James Ketrenos43f66a62005-03-25 12:31:53 -06008556
James Ketrenosc848d0a2005-08-24 21:56:24 -05008557 up(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06008558 return 0;
8559}
8560
Jeff Garzikbf794512005-07-31 13:07:26 -04008561static int ipw_wx_get_wap(struct net_device *dev,
8562 struct iw_request_info *info,
James Ketrenos43f66a62005-03-25 12:31:53 -06008563 union iwreq_data *wrqu, char *extra)
8564{
8565 struct ipw_priv *priv = ieee80211_priv(dev);
8566 /* If we are associated, trying to associate, or have a statically
8567 * configured BSSID then return that; otherwise return ANY */
James Ketrenosc848d0a2005-08-24 21:56:24 -05008568 down(&priv->sem);
Jeff Garzikbf794512005-07-31 13:07:26 -04008569 if (priv->config & CFG_STATIC_BSSID ||
James Ketrenos43f66a62005-03-25 12:31:53 -06008570 priv->status & (STATUS_ASSOCIATED | STATUS_ASSOCIATING)) {
8571 wrqu->ap_addr.sa_family = ARPHRD_ETHER;
James Ketrenosafbf30a2005-08-25 00:05:33 -05008572 memcpy(wrqu->ap_addr.sa_data, priv->bssid, ETH_ALEN);
James Ketrenos43f66a62005-03-25 12:31:53 -06008573 } else
8574 memset(wrqu->ap_addr.sa_data, 0, ETH_ALEN);
8575
8576 IPW_DEBUG_WX("Getting WAP BSSID: " MAC_FMT "\n",
8577 MAC_ARG(wrqu->ap_addr.sa_data));
James Ketrenosc848d0a2005-08-24 21:56:24 -05008578 up(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06008579 return 0;
8580}
8581
Jeff Garzikbf794512005-07-31 13:07:26 -04008582static int ipw_wx_set_essid(struct net_device *dev,
8583 struct iw_request_info *info,
James Ketrenos43f66a62005-03-25 12:31:53 -06008584 union iwreq_data *wrqu, char *extra)
8585{
8586 struct ipw_priv *priv = ieee80211_priv(dev);
Jeff Garzik0edd5b42005-09-07 00:48:31 -04008587 char *essid = ""; /* ANY */
James Ketrenos43f66a62005-03-25 12:31:53 -06008588 int length = 0;
James Ketrenosc848d0a2005-08-24 21:56:24 -05008589 down(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06008590 if (wrqu->essid.flags && wrqu->essid.length) {
8591 length = wrqu->essid.length - 1;
8592 essid = extra;
8593 }
8594 if (length == 0) {
8595 IPW_DEBUG_WX("Setting ESSID to ANY\n");
James Ketrenosafbf30a2005-08-25 00:05:33 -05008596 if ((priv->config & CFG_STATIC_ESSID) &&
8597 !(priv->status & (STATUS_ASSOCIATED |
James Ketrenos43f66a62005-03-25 12:31:53 -06008598 STATUS_ASSOCIATING))) {
8599 IPW_DEBUG_ASSOC("Attempting to associate with new "
8600 "parameters.\n");
James Ketrenosafbf30a2005-08-25 00:05:33 -05008601 priv->config &= ~CFG_STATIC_ESSID;
James Ketrenos43f66a62005-03-25 12:31:53 -06008602 ipw_associate(priv);
8603 }
James Ketrenosc848d0a2005-08-24 21:56:24 -05008604 up(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06008605 return 0;
8606 }
8607
8608 length = min(length, IW_ESSID_MAX_SIZE);
8609
8610 priv->config |= CFG_STATIC_ESSID;
8611
8612 if (priv->essid_len == length && !memcmp(priv->essid, extra, length)) {
8613 IPW_DEBUG_WX("ESSID set to current ESSID.\n");
James Ketrenosc848d0a2005-08-24 21:56:24 -05008614 up(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06008615 return 0;
8616 }
8617
8618 IPW_DEBUG_WX("Setting ESSID: '%s' (%d)\n", escape_essid(essid, length),
8619 length);
8620
8621 priv->essid_len = length;
8622 memcpy(priv->essid, essid, priv->essid_len);
Jeff Garzikbf794512005-07-31 13:07:26 -04008623
James Ketrenosc848d0a2005-08-24 21:56:24 -05008624 /* Network configuration changed -- force [re]association */
8625 IPW_DEBUG_ASSOC("[re]association triggered due to ESSID change.\n");
8626 if (!ipw_disassociate(priv))
James Ketrenos43f66a62005-03-25 12:31:53 -06008627 ipw_associate(priv);
James Ketrenos43f66a62005-03-25 12:31:53 -06008628
James Ketrenosc848d0a2005-08-24 21:56:24 -05008629 up(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06008630 return 0;
8631}
8632
Jeff Garzikbf794512005-07-31 13:07:26 -04008633static int ipw_wx_get_essid(struct net_device *dev,
8634 struct iw_request_info *info,
James Ketrenos43f66a62005-03-25 12:31:53 -06008635 union iwreq_data *wrqu, char *extra)
8636{
8637 struct ipw_priv *priv = ieee80211_priv(dev);
8638
8639 /* If we are associated, trying to associate, or have a statically
8640 * configured ESSID then return that; otherwise return ANY */
James Ketrenosc848d0a2005-08-24 21:56:24 -05008641 down(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06008642 if (priv->config & CFG_STATIC_ESSID ||
Jeff Garzikbf794512005-07-31 13:07:26 -04008643 priv->status & (STATUS_ASSOCIATED | STATUS_ASSOCIATING)) {
8644 IPW_DEBUG_WX("Getting essid: '%s'\n",
James Ketrenos43f66a62005-03-25 12:31:53 -06008645 escape_essid(priv->essid, priv->essid_len));
Jeff Garzikbf794512005-07-31 13:07:26 -04008646 memcpy(extra, priv->essid, priv->essid_len);
James Ketrenos43f66a62005-03-25 12:31:53 -06008647 wrqu->essid.length = priv->essid_len;
Jeff Garzik0edd5b42005-09-07 00:48:31 -04008648 wrqu->essid.flags = 1; /* active */
James Ketrenos43f66a62005-03-25 12:31:53 -06008649 } else {
8650 IPW_DEBUG_WX("Getting essid: ANY\n");
8651 wrqu->essid.length = 0;
Jeff Garzik0edd5b42005-09-07 00:48:31 -04008652 wrqu->essid.flags = 0; /* active */
James Ketrenos43f66a62005-03-25 12:31:53 -06008653 }
James Ketrenosc848d0a2005-08-24 21:56:24 -05008654 up(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06008655 return 0;
8656}
8657
Jeff Garzikbf794512005-07-31 13:07:26 -04008658static int ipw_wx_set_nick(struct net_device *dev,
8659 struct iw_request_info *info,
James Ketrenos43f66a62005-03-25 12:31:53 -06008660 union iwreq_data *wrqu, char *extra)
Jeff Garzikbf794512005-07-31 13:07:26 -04008661{
James Ketrenos43f66a62005-03-25 12:31:53 -06008662 struct ipw_priv *priv = ieee80211_priv(dev);
8663
8664 IPW_DEBUG_WX("Setting nick to '%s'\n", extra);
8665 if (wrqu->data.length > IW_ESSID_MAX_SIZE)
8666 return -E2BIG;
James Ketrenosc848d0a2005-08-24 21:56:24 -05008667 down(&priv->sem);
Jeff Garzik0edd5b42005-09-07 00:48:31 -04008668 wrqu->data.length = min((size_t) wrqu->data.length, sizeof(priv->nick));
James Ketrenos43f66a62005-03-25 12:31:53 -06008669 memset(priv->nick, 0, sizeof(priv->nick));
Jeff Garzik0edd5b42005-09-07 00:48:31 -04008670 memcpy(priv->nick, extra, wrqu->data.length);
James Ketrenos43f66a62005-03-25 12:31:53 -06008671 IPW_DEBUG_TRACE("<<\n");
James Ketrenosc848d0a2005-08-24 21:56:24 -05008672 up(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06008673 return 0;
8674
8675}
8676
Jeff Garzikbf794512005-07-31 13:07:26 -04008677static int ipw_wx_get_nick(struct net_device *dev,
8678 struct iw_request_info *info,
James Ketrenos43f66a62005-03-25 12:31:53 -06008679 union iwreq_data *wrqu, char *extra)
Jeff Garzikbf794512005-07-31 13:07:26 -04008680{
James Ketrenos43f66a62005-03-25 12:31:53 -06008681 struct ipw_priv *priv = ieee80211_priv(dev);
8682 IPW_DEBUG_WX("Getting nick\n");
James Ketrenosc848d0a2005-08-24 21:56:24 -05008683 down(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06008684 wrqu->data.length = strlen(priv->nick) + 1;
8685 memcpy(extra, priv->nick, wrqu->data.length);
Jeff Garzik0edd5b42005-09-07 00:48:31 -04008686 wrqu->data.flags = 1; /* active */
James Ketrenosc848d0a2005-08-24 21:56:24 -05008687 up(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06008688 return 0;
8689}
8690
James Ketrenos43f66a62005-03-25 12:31:53 -06008691static int ipw_wx_set_rate(struct net_device *dev,
8692 struct iw_request_info *info,
8693 union iwreq_data *wrqu, char *extra)
Jeff Garzikbf794512005-07-31 13:07:26 -04008694{
James Ketrenosea2b26e2005-08-24 21:25:16 -05008695 /* TODO: We should use semaphores or locks for access to priv */
8696 struct ipw_priv *priv = ieee80211_priv(dev);
8697 u32 target_rate = wrqu->bitrate.value;
8698 u32 fixed, mask;
8699
8700 /* value = -1, fixed = 0 means auto only, so we should use all rates offered by AP */
8701 /* value = X, fixed = 1 means only rate X */
8702 /* value = X, fixed = 0 means all rates lower equal X */
8703
8704 if (target_rate == -1) {
8705 fixed = 0;
8706 mask = IEEE80211_DEFAULT_RATES_MASK;
8707 /* Now we should reassociate */
8708 goto apply;
8709 }
8710
8711 mask = 0;
8712 fixed = wrqu->bitrate.fixed;
8713
8714 if (target_rate == 1000000 || !fixed)
8715 mask |= IEEE80211_CCK_RATE_1MB_MASK;
8716 if (target_rate == 1000000)
8717 goto apply;
8718
8719 if (target_rate == 2000000 || !fixed)
8720 mask |= IEEE80211_CCK_RATE_2MB_MASK;
8721 if (target_rate == 2000000)
8722 goto apply;
8723
8724 if (target_rate == 5500000 || !fixed)
8725 mask |= IEEE80211_CCK_RATE_5MB_MASK;
8726 if (target_rate == 5500000)
8727 goto apply;
8728
8729 if (target_rate == 6000000 || !fixed)
8730 mask |= IEEE80211_OFDM_RATE_6MB_MASK;
8731 if (target_rate == 6000000)
8732 goto apply;
8733
8734 if (target_rate == 9000000 || !fixed)
8735 mask |= IEEE80211_OFDM_RATE_9MB_MASK;
8736 if (target_rate == 9000000)
8737 goto apply;
8738
8739 if (target_rate == 11000000 || !fixed)
8740 mask |= IEEE80211_CCK_RATE_11MB_MASK;
8741 if (target_rate == 11000000)
8742 goto apply;
8743
8744 if (target_rate == 12000000 || !fixed)
8745 mask |= IEEE80211_OFDM_RATE_12MB_MASK;
8746 if (target_rate == 12000000)
8747 goto apply;
8748
8749 if (target_rate == 18000000 || !fixed)
8750 mask |= IEEE80211_OFDM_RATE_18MB_MASK;
8751 if (target_rate == 18000000)
8752 goto apply;
8753
8754 if (target_rate == 24000000 || !fixed)
8755 mask |= IEEE80211_OFDM_RATE_24MB_MASK;
8756 if (target_rate == 24000000)
8757 goto apply;
8758
8759 if (target_rate == 36000000 || !fixed)
8760 mask |= IEEE80211_OFDM_RATE_36MB_MASK;
8761 if (target_rate == 36000000)
8762 goto apply;
8763
8764 if (target_rate == 48000000 || !fixed)
8765 mask |= IEEE80211_OFDM_RATE_48MB_MASK;
8766 if (target_rate == 48000000)
8767 goto apply;
8768
8769 if (target_rate == 54000000 || !fixed)
8770 mask |= IEEE80211_OFDM_RATE_54MB_MASK;
8771 if (target_rate == 54000000)
8772 goto apply;
8773
8774 IPW_DEBUG_WX("invalid rate specified, returning error\n");
8775 return -EINVAL;
8776
8777 apply:
8778 IPW_DEBUG_WX("Setting rate mask to 0x%08X [%s]\n",
8779 mask, fixed ? "fixed" : "sub-rates");
James Ketrenosc848d0a2005-08-24 21:56:24 -05008780 down(&priv->sem);
James Ketrenosb095c382005-08-24 22:04:42 -05008781 if (mask == IEEE80211_DEFAULT_RATES_MASK) {
James Ketrenosea2b26e2005-08-24 21:25:16 -05008782 priv->config &= ~CFG_FIXED_RATE;
James Ketrenosb095c382005-08-24 22:04:42 -05008783 ipw_set_fixed_rate(priv, priv->ieee->mode);
8784 } else
James Ketrenosea2b26e2005-08-24 21:25:16 -05008785 priv->config |= CFG_FIXED_RATE;
8786
James Ketrenosc848d0a2005-08-24 21:56:24 -05008787 if (priv->rates_mask == mask) {
8788 IPW_DEBUG_WX("Mask set to current mask.\n");
8789 up(&priv->sem);
8790 return 0;
James Ketrenosea2b26e2005-08-24 21:25:16 -05008791 }
8792
James Ketrenosc848d0a2005-08-24 21:56:24 -05008793 priv->rates_mask = mask;
8794
8795 /* Network configuration changed -- force [re]association */
8796 IPW_DEBUG_ASSOC("[re]association triggered due to rates change.\n");
8797 if (!ipw_disassociate(priv))
8798 ipw_associate(priv);
8799
8800 up(&priv->sem);
James Ketrenosea2b26e2005-08-24 21:25:16 -05008801 return 0;
James Ketrenos43f66a62005-03-25 12:31:53 -06008802}
8803
Jeff Garzikbf794512005-07-31 13:07:26 -04008804static int ipw_wx_get_rate(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{
Jeff Garzik0edd5b42005-09-07 00:48:31 -04008808 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 wrqu->bitrate.value = priv->last_rate;
James Ketrenosc848d0a2005-08-24 21:56:24 -05008811 up(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06008812 IPW_DEBUG_WX("GET Rate -> %d \n", wrqu->bitrate.value);
8813 return 0;
8814}
8815
Jeff Garzikbf794512005-07-31 13:07:26 -04008816static int ipw_wx_set_rts(struct net_device *dev,
8817 struct iw_request_info *info,
James Ketrenos43f66a62005-03-25 12:31:53 -06008818 union iwreq_data *wrqu, char *extra)
Jeff Garzikbf794512005-07-31 13:07:26 -04008819{
James Ketrenos43f66a62005-03-25 12:31:53 -06008820 struct ipw_priv *priv = ieee80211_priv(dev);
James Ketrenosc848d0a2005-08-24 21:56:24 -05008821 down(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06008822 if (wrqu->rts.disabled)
8823 priv->rts_threshold = DEFAULT_RTS_THRESHOLD;
8824 else {
8825 if (wrqu->rts.value < MIN_RTS_THRESHOLD ||
James Ketrenosc848d0a2005-08-24 21:56:24 -05008826 wrqu->rts.value > MAX_RTS_THRESHOLD) {
8827 up(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06008828 return -EINVAL;
James Ketrenosc848d0a2005-08-24 21:56:24 -05008829 }
James Ketrenos43f66a62005-03-25 12:31:53 -06008830 priv->rts_threshold = wrqu->rts.value;
8831 }
8832
8833 ipw_send_rts_threshold(priv, priv->rts_threshold);
James Ketrenosc848d0a2005-08-24 21:56:24 -05008834 up(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06008835 IPW_DEBUG_WX("SET RTS Threshold -> %d \n", priv->rts_threshold);
8836 return 0;
8837}
8838
Jeff Garzikbf794512005-07-31 13:07:26 -04008839static int ipw_wx_get_rts(struct net_device *dev,
8840 struct iw_request_info *info,
James Ketrenos43f66a62005-03-25 12:31:53 -06008841 union iwreq_data *wrqu, char *extra)
Jeff Garzikbf794512005-07-31 13:07:26 -04008842{
James Ketrenos43f66a62005-03-25 12:31:53 -06008843 struct ipw_priv *priv = ieee80211_priv(dev);
James Ketrenosc848d0a2005-08-24 21:56:24 -05008844 down(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06008845 wrqu->rts.value = priv->rts_threshold;
8846 wrqu->rts.fixed = 0; /* no auto select */
Jeff Garzik0edd5b42005-09-07 00:48:31 -04008847 wrqu->rts.disabled = (wrqu->rts.value == DEFAULT_RTS_THRESHOLD);
James Ketrenosc848d0a2005-08-24 21:56:24 -05008848 up(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06008849 IPW_DEBUG_WX("GET RTS Threshold -> %d \n", wrqu->rts.value);
8850 return 0;
8851}
8852
Jeff Garzikbf794512005-07-31 13:07:26 -04008853static int ipw_wx_set_txpow(struct net_device *dev,
8854 struct iw_request_info *info,
James Ketrenos43f66a62005-03-25 12:31:53 -06008855 union iwreq_data *wrqu, char *extra)
Jeff Garzikbf794512005-07-31 13:07:26 -04008856{
James Ketrenos43f66a62005-03-25 12:31:53 -06008857 struct ipw_priv *priv = ieee80211_priv(dev);
Zhu Yi6de9f7f2005-08-11 14:39:33 +08008858 int err = 0;
James Ketrenos43f66a62005-03-25 12:31:53 -06008859
James Ketrenosc848d0a2005-08-24 21:56:24 -05008860 down(&priv->sem);
8861 if (ipw_radio_kill_sw(priv, wrqu->power.disabled)) {
Zhu Yi6de9f7f2005-08-11 14:39:33 +08008862 err = -EINPROGRESS;
8863 goto out;
James Ketrenosc848d0a2005-08-24 21:56:24 -05008864 }
James Ketrenos43f66a62005-03-25 12:31:53 -06008865
James Ketrenosb095c382005-08-24 22:04:42 -05008866 if (!wrqu->power.fixed)
8867 wrqu->power.value = IPW_TX_POWER_DEFAULT;
James Ketrenos43f66a62005-03-25 12:31:53 -06008868
James Ketrenosc848d0a2005-08-24 21:56:24 -05008869 if (wrqu->power.flags != IW_TXPOW_DBM) {
Zhu Yi6de9f7f2005-08-11 14:39:33 +08008870 err = -EINVAL;
8871 goto out;
James Ketrenosc848d0a2005-08-24 21:56:24 -05008872 }
James Ketrenos43f66a62005-03-25 12:31:53 -06008873
James Ketrenosb095c382005-08-24 22:04:42 -05008874 if ((wrqu->power.value > IPW_TX_POWER_MAX) ||
James Ketrenosafbf30a2005-08-25 00:05:33 -05008875 (wrqu->power.value < IPW_TX_POWER_MIN)) {
Zhu Yi6de9f7f2005-08-11 14:39:33 +08008876 err = -EINVAL;
8877 goto out;
James Ketrenosc848d0a2005-08-24 21:56:24 -05008878 }
James Ketrenos43f66a62005-03-25 12:31:53 -06008879
8880 priv->tx_power = wrqu->power.value;
Zhu Yi6de9f7f2005-08-11 14:39:33 +08008881 err = ipw_set_tx_power(priv);
8882 out:
James Ketrenosc848d0a2005-08-24 21:56:24 -05008883 up(&priv->sem);
Zhu Yi6de9f7f2005-08-11 14:39:33 +08008884 return err;
James Ketrenos43f66a62005-03-25 12:31:53 -06008885}
8886
Jeff Garzikbf794512005-07-31 13:07:26 -04008887static int ipw_wx_get_txpow(struct net_device *dev,
8888 struct iw_request_info *info,
James Ketrenos43f66a62005-03-25 12:31:53 -06008889 union iwreq_data *wrqu, char *extra)
Jeff Garzikbf794512005-07-31 13:07:26 -04008890{
James Ketrenos43f66a62005-03-25 12:31:53 -06008891 struct ipw_priv *priv = ieee80211_priv(dev);
James Ketrenosc848d0a2005-08-24 21:56:24 -05008892 down(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06008893 wrqu->power.value = priv->tx_power;
8894 wrqu->power.fixed = 1;
8895 wrqu->power.flags = IW_TXPOW_DBM;
8896 wrqu->power.disabled = (priv->status & STATUS_RF_KILL_MASK) ? 1 : 0;
James Ketrenosc848d0a2005-08-24 21:56:24 -05008897 up(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06008898
Jeff Garzikbf794512005-07-31 13:07:26 -04008899 IPW_DEBUG_WX("GET TX Power -> %s %d \n",
Zhu Yi22501c82005-08-11 10:49:17 +08008900 wrqu->power.disabled ? "OFF" : "ON", wrqu->power.value);
James Ketrenos43f66a62005-03-25 12:31:53 -06008901
8902 return 0;
8903}
8904
Jeff Garzikbf794512005-07-31 13:07:26 -04008905static int ipw_wx_set_frag(struct net_device *dev,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04008906 struct iw_request_info *info,
8907 union iwreq_data *wrqu, char *extra)
James Ketrenos43f66a62005-03-25 12:31:53 -06008908{
8909 struct ipw_priv *priv = ieee80211_priv(dev);
James Ketrenosc848d0a2005-08-24 21:56:24 -05008910 down(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06008911 if (wrqu->frag.disabled)
8912 priv->ieee->fts = DEFAULT_FTS;
8913 else {
8914 if (wrqu->frag.value < MIN_FRAG_THRESHOLD ||
James Ketrenosb095c382005-08-24 22:04:42 -05008915 wrqu->frag.value > MAX_FRAG_THRESHOLD) {
8916 up(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06008917 return -EINVAL;
James Ketrenosb095c382005-08-24 22:04:42 -05008918 }
Jeff Garzikbf794512005-07-31 13:07:26 -04008919
James Ketrenos43f66a62005-03-25 12:31:53 -06008920 priv->ieee->fts = wrqu->frag.value & ~0x1;
8921 }
8922
8923 ipw_send_frag_threshold(priv, wrqu->frag.value);
James Ketrenosc848d0a2005-08-24 21:56:24 -05008924 up(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06008925 IPW_DEBUG_WX("SET Frag Threshold -> %d \n", wrqu->frag.value);
8926 return 0;
8927}
8928
Jeff Garzikbf794512005-07-31 13:07:26 -04008929static int ipw_wx_get_frag(struct net_device *dev,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04008930 struct iw_request_info *info,
8931 union iwreq_data *wrqu, char *extra)
James Ketrenos43f66a62005-03-25 12:31:53 -06008932{
8933 struct ipw_priv *priv = ieee80211_priv(dev);
James Ketrenosc848d0a2005-08-24 21:56:24 -05008934 down(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06008935 wrqu->frag.value = priv->ieee->fts;
8936 wrqu->frag.fixed = 0; /* no auto select */
Jeff Garzik0edd5b42005-09-07 00:48:31 -04008937 wrqu->frag.disabled = (wrqu->frag.value == DEFAULT_FTS);
James Ketrenosc848d0a2005-08-24 21:56:24 -05008938 up(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06008939 IPW_DEBUG_WX("GET Frag Threshold -> %d \n", wrqu->frag.value);
8940
8941 return 0;
8942}
8943
Jeff Garzikbf794512005-07-31 13:07:26 -04008944static int ipw_wx_set_retry(struct net_device *dev,
8945 struct iw_request_info *info,
James Ketrenos43f66a62005-03-25 12:31:53 -06008946 union iwreq_data *wrqu, char *extra)
Jeff Garzikbf794512005-07-31 13:07:26 -04008947{
James Ketrenosafbf30a2005-08-25 00:05:33 -05008948 struct ipw_priv *priv = ieee80211_priv(dev);
8949
8950 if (wrqu->retry.flags & IW_RETRY_LIFETIME || wrqu->retry.disabled)
8951 return -EINVAL;
8952
8953 if (!(wrqu->retry.flags & IW_RETRY_LIMIT))
8954 return 0;
8955
8956 if (wrqu->retry.value < 0 || wrqu->retry.value > 255)
8957 return -EINVAL;
8958
8959 down(&priv->sem);
8960 if (wrqu->retry.flags & IW_RETRY_MIN)
8961 priv->short_retry_limit = (u8) wrqu->retry.value;
8962 else if (wrqu->retry.flags & IW_RETRY_MAX)
8963 priv->long_retry_limit = (u8) wrqu->retry.value;
8964 else {
8965 priv->short_retry_limit = (u8) wrqu->retry.value;
8966 priv->long_retry_limit = (u8) wrqu->retry.value;
8967 }
8968
8969 ipw_send_retry_limit(priv, priv->short_retry_limit,
8970 priv->long_retry_limit);
8971 up(&priv->sem);
8972 IPW_DEBUG_WX("SET retry limit -> short:%d long:%d\n",
8973 priv->short_retry_limit, priv->long_retry_limit);
8974 return 0;
James Ketrenos43f66a62005-03-25 12:31:53 -06008975}
8976
Jeff Garzikbf794512005-07-31 13:07:26 -04008977static int ipw_wx_get_retry(struct net_device *dev,
8978 struct iw_request_info *info,
James Ketrenos43f66a62005-03-25 12:31:53 -06008979 union iwreq_data *wrqu, char *extra)
Jeff Garzikbf794512005-07-31 13:07:26 -04008980{
James Ketrenosafbf30a2005-08-25 00:05:33 -05008981 struct ipw_priv *priv = ieee80211_priv(dev);
8982
8983 down(&priv->sem);
8984 wrqu->retry.disabled = 0;
8985
8986 if ((wrqu->retry.flags & IW_RETRY_TYPE) == IW_RETRY_LIFETIME) {
8987 up(&priv->sem);
8988 return -EINVAL;
8989 }
8990
8991 if (wrqu->retry.flags & IW_RETRY_MAX) {
8992 wrqu->retry.flags = IW_RETRY_LIMIT | IW_RETRY_MAX;
8993 wrqu->retry.value = priv->long_retry_limit;
8994 } else if (wrqu->retry.flags & IW_RETRY_MIN) {
8995 wrqu->retry.flags = IW_RETRY_LIMIT | IW_RETRY_MIN;
8996 wrqu->retry.value = priv->short_retry_limit;
8997 } else {
8998 wrqu->retry.flags = IW_RETRY_LIMIT;
8999 wrqu->retry.value = priv->short_retry_limit;
9000 }
9001 up(&priv->sem);
9002
9003 IPW_DEBUG_WX("GET retry -> %d \n", wrqu->retry.value);
9004
9005 return 0;
James Ketrenos43f66a62005-03-25 12:31:53 -06009006}
9007
James Ketrenosafbf30a2005-08-25 00:05:33 -05009008static int ipw_request_direct_scan(struct ipw_priv *priv, char *essid,
9009 int essid_len)
9010{
9011 struct ipw_scan_request_ext scan;
9012 int err = 0, scan_type;
9013
Pekka Enbergefb34422005-11-16 21:55:05 +02009014 if (!(priv->status & STATUS_INIT) ||
9015 (priv->status & STATUS_EXIT_PENDING))
9016 return 0;
9017
James Ketrenosafbf30a2005-08-25 00:05:33 -05009018 down(&priv->sem);
9019
9020 if (priv->status & STATUS_RF_KILL_MASK) {
9021 IPW_DEBUG_HC("Aborting scan due to RF kill activation\n");
9022 priv->status |= STATUS_SCAN_PENDING;
9023 goto done;
9024 }
9025
9026 IPW_DEBUG_HC("starting request direct scan!\n");
9027
9028 if (priv->status & (STATUS_SCANNING | STATUS_SCAN_ABORTING)) {
Olaf Kirchd834a412006-01-09 17:00:37 +01009029 /* We should not sleep here; otherwise we will block most
9030 * of the system (for instance, we hold rtnl_lock when we
9031 * get here).
9032 */
9033 err = -EAGAIN;
9034 goto done;
James Ketrenosafbf30a2005-08-25 00:05:33 -05009035 }
9036 memset(&scan, 0, sizeof(scan));
9037
9038 if (priv->config & CFG_SPEED_SCAN)
9039 scan.dwell_time[IPW_SCAN_ACTIVE_BROADCAST_SCAN] =
9040 cpu_to_le16(30);
9041 else
9042 scan.dwell_time[IPW_SCAN_ACTIVE_BROADCAST_SCAN] =
9043 cpu_to_le16(20);
9044
9045 scan.dwell_time[IPW_SCAN_ACTIVE_BROADCAST_AND_DIRECT_SCAN] =
9046 cpu_to_le16(20);
Liu Hong1fe0adb2005-08-19 09:33:10 -05009047 scan.dwell_time[IPW_SCAN_PASSIVE_FULL_DWELL_SCAN] = cpu_to_le16(120);
James Ketrenosafbf30a2005-08-25 00:05:33 -05009048 scan.dwell_time[IPW_SCAN_ACTIVE_DIRECT_SCAN] = cpu_to_le16(20);
9049
9050 scan.full_scan_index = cpu_to_le32(ieee80211_get_scans(priv->ieee));
9051
9052 err = ipw_send_ssid(priv, essid, essid_len);
9053 if (err) {
9054 IPW_DEBUG_HC("Attempt to send SSID command failed\n");
9055 goto done;
9056 }
9057 scan_type = IPW_SCAN_ACTIVE_BROADCAST_AND_DIRECT_SCAN;
9058
9059 ipw_add_scan_channels(priv, &scan, scan_type);
9060
9061 err = ipw_send_scan_request_ext(priv, &scan);
9062 if (err) {
9063 IPW_DEBUG_HC("Sending scan command failed: %08X\n", err);
9064 goto done;
9065 }
9066
9067 priv->status |= STATUS_SCANNING;
9068
9069 done:
9070 up(&priv->sem);
9071 return err;
James Ketrenos43f66a62005-03-25 12:31:53 -06009072}
9073
Jeff Garzikbf794512005-07-31 13:07:26 -04009074static int ipw_wx_set_scan(struct net_device *dev,
9075 struct iw_request_info *info,
James Ketrenos43f66a62005-03-25 12:31:53 -06009076 union iwreq_data *wrqu, char *extra)
9077{
9078 struct ipw_priv *priv = ieee80211_priv(dev);
James Ketrenosafbf30a2005-08-25 00:05:33 -05009079 struct iw_scan_req *req = NULL;
9080 if (wrqu->data.length
9081 && wrqu->data.length == sizeof(struct iw_scan_req)) {
9082 req = (struct iw_scan_req *)extra;
9083 if (wrqu->data.flags & IW_SCAN_THIS_ESSID) {
9084 ipw_request_direct_scan(priv, req->essid,
9085 req->essid_len);
9086 return 0;
9087 }
9088 }
James Ketrenos8935f392005-10-05 15:59:08 -05009089
James Ketrenos43f66a62005-03-25 12:31:53 -06009090 IPW_DEBUG_WX("Start scan\n");
James Ketrenosb095c382005-08-24 22:04:42 -05009091
9092 queue_work(priv->workqueue, &priv->request_scan);
9093
James Ketrenos43f66a62005-03-25 12:31:53 -06009094 return 0;
9095}
9096
Jeff Garzikbf794512005-07-31 13:07:26 -04009097static int ipw_wx_get_scan(struct net_device *dev,
9098 struct iw_request_info *info,
James Ketrenos43f66a62005-03-25 12:31:53 -06009099 union iwreq_data *wrqu, char *extra)
Jeff Garzikbf794512005-07-31 13:07:26 -04009100{
James Ketrenos43f66a62005-03-25 12:31:53 -06009101 struct ipw_priv *priv = ieee80211_priv(dev);
9102 return ieee80211_wx_get_scan(priv->ieee, info, wrqu, extra);
9103}
9104
Jeff Garzikbf794512005-07-31 13:07:26 -04009105static int ipw_wx_set_encode(struct net_device *dev,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04009106 struct iw_request_info *info,
9107 union iwreq_data *wrqu, char *key)
James Ketrenos43f66a62005-03-25 12:31:53 -06009108{
9109 struct ipw_priv *priv = ieee80211_priv(dev);
James Ketrenosafbf30a2005-08-25 00:05:33 -05009110 int ret;
Hong Liucaeff812005-08-05 17:25:50 +08009111 u32 cap = priv->capability;
James Ketrenosafbf30a2005-08-25 00:05:33 -05009112
9113 down(&priv->sem);
9114 ret = ieee80211_wx_set_encode(priv->ieee, info, wrqu, key);
James Ketrenosafbf30a2005-08-25 00:05:33 -05009115
Hong Liucaeff812005-08-05 17:25:50 +08009116 /* In IBSS mode, we need to notify the firmware to update
9117 * the beacon info after we changed the capability. */
9118 if (cap != priv->capability &&
9119 priv->ieee->iw_mode == IW_MODE_ADHOC &&
9120 priv->status & STATUS_ASSOCIATED)
9121 ipw_disassociate(priv);
9122
9123 up(&priv->sem);
James Ketrenosafbf30a2005-08-25 00:05:33 -05009124 return ret;
James Ketrenos43f66a62005-03-25 12:31:53 -06009125}
9126
Jeff Garzikbf794512005-07-31 13:07:26 -04009127static int ipw_wx_get_encode(struct net_device *dev,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04009128 struct iw_request_info *info,
9129 union iwreq_data *wrqu, char *key)
James Ketrenos43f66a62005-03-25 12:31:53 -06009130{
9131 struct ipw_priv *priv = ieee80211_priv(dev);
9132 return ieee80211_wx_get_encode(priv->ieee, info, wrqu, key);
9133}
9134
Jeff Garzikbf794512005-07-31 13:07:26 -04009135static int ipw_wx_set_power(struct net_device *dev,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04009136 struct iw_request_info *info,
9137 union iwreq_data *wrqu, char *extra)
James Ketrenos43f66a62005-03-25 12:31:53 -06009138{
9139 struct ipw_priv *priv = ieee80211_priv(dev);
9140 int err;
James Ketrenosc848d0a2005-08-24 21:56:24 -05009141 down(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06009142 if (wrqu->power.disabled) {
9143 priv->power_mode = IPW_POWER_LEVEL(priv->power_mode);
9144 err = ipw_send_power_mode(priv, IPW_POWER_MODE_CAM);
9145 if (err) {
9146 IPW_DEBUG_WX("failed setting power mode.\n");
James Ketrenosc848d0a2005-08-24 21:56:24 -05009147 up(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06009148 return err;
9149 }
James Ketrenos43f66a62005-03-25 12:31:53 -06009150 IPW_DEBUG_WX("SET Power Management Mode -> off\n");
James Ketrenosc848d0a2005-08-24 21:56:24 -05009151 up(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06009152 return 0;
Jeff Garzikbf794512005-07-31 13:07:26 -04009153 }
James Ketrenos43f66a62005-03-25 12:31:53 -06009154
9155 switch (wrqu->power.flags & IW_POWER_MODE) {
Jeff Garzik0edd5b42005-09-07 00:48:31 -04009156 case IW_POWER_ON: /* If not specified */
9157 case IW_POWER_MODE: /* If set all mask */
9158 case IW_POWER_ALL_R: /* If explicitely state all */
James Ketrenos43f66a62005-03-25 12:31:53 -06009159 break;
Jeff Garzik0edd5b42005-09-07 00:48:31 -04009160 default: /* Otherwise we don't support it */
James Ketrenos43f66a62005-03-25 12:31:53 -06009161 IPW_DEBUG_WX("SET PM Mode: %X not supported.\n",
9162 wrqu->power.flags);
James Ketrenosc848d0a2005-08-24 21:56:24 -05009163 up(&priv->sem);
Jeff Garzikbf794512005-07-31 13:07:26 -04009164 return -EOPNOTSUPP;
James Ketrenos43f66a62005-03-25 12:31:53 -06009165 }
Jeff Garzikbf794512005-07-31 13:07:26 -04009166
James Ketrenos43f66a62005-03-25 12:31:53 -06009167 /* If the user hasn't specified a power management mode yet, default
9168 * to BATTERY */
Jeff Garzik0edd5b42005-09-07 00:48:31 -04009169 if (IPW_POWER_LEVEL(priv->power_mode) == IPW_POWER_AC)
James Ketrenos43f66a62005-03-25 12:31:53 -06009170 priv->power_mode = IPW_POWER_ENABLED | IPW_POWER_BATTERY;
Jeff Garzikbf794512005-07-31 13:07:26 -04009171 else
James Ketrenos43f66a62005-03-25 12:31:53 -06009172 priv->power_mode = IPW_POWER_ENABLED | priv->power_mode;
9173 err = ipw_send_power_mode(priv, IPW_POWER_LEVEL(priv->power_mode));
9174 if (err) {
9175 IPW_DEBUG_WX("failed setting power mode.\n");
James Ketrenosc848d0a2005-08-24 21:56:24 -05009176 up(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06009177 return err;
9178 }
9179
Jeff Garzik0edd5b42005-09-07 00:48:31 -04009180 IPW_DEBUG_WX("SET Power Management Mode -> 0x%02X\n", priv->power_mode);
James Ketrenosc848d0a2005-08-24 21:56:24 -05009181 up(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06009182 return 0;
9183}
9184
Jeff Garzikbf794512005-07-31 13:07:26 -04009185static int ipw_wx_get_power(struct net_device *dev,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04009186 struct iw_request_info *info,
9187 union iwreq_data *wrqu, char *extra)
James Ketrenos43f66a62005-03-25 12:31:53 -06009188{
9189 struct ipw_priv *priv = ieee80211_priv(dev);
James Ketrenosc848d0a2005-08-24 21:56:24 -05009190 down(&priv->sem);
James Ketrenosa613bff2005-08-24 21:43:11 -05009191 if (!(priv->power_mode & IPW_POWER_ENABLED))
James Ketrenos43f66a62005-03-25 12:31:53 -06009192 wrqu->power.disabled = 1;
James Ketrenosa613bff2005-08-24 21:43:11 -05009193 else
James Ketrenos43f66a62005-03-25 12:31:53 -06009194 wrqu->power.disabled = 0;
James Ketrenos43f66a62005-03-25 12:31:53 -06009195
James Ketrenosc848d0a2005-08-24 21:56:24 -05009196 up(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06009197 IPW_DEBUG_WX("GET Power Management Mode -> %02X\n", priv->power_mode);
Jeff Garzikbf794512005-07-31 13:07:26 -04009198
James Ketrenos43f66a62005-03-25 12:31:53 -06009199 return 0;
9200}
9201
Jeff Garzikbf794512005-07-31 13:07:26 -04009202static int ipw_wx_set_powermode(struct net_device *dev,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04009203 struct iw_request_info *info,
9204 union iwreq_data *wrqu, char *extra)
James Ketrenos43f66a62005-03-25 12:31:53 -06009205{
9206 struct ipw_priv *priv = ieee80211_priv(dev);
9207 int mode = *(int *)extra;
9208 int err;
James Ketrenosc848d0a2005-08-24 21:56:24 -05009209 down(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06009210 if ((mode < 1) || (mode > IPW_POWER_LIMIT)) {
9211 mode = IPW_POWER_AC;
9212 priv->power_mode = mode;
9213 } else {
9214 priv->power_mode = IPW_POWER_ENABLED | mode;
9215 }
Jeff Garzikbf794512005-07-31 13:07:26 -04009216
James Ketrenos43f66a62005-03-25 12:31:53 -06009217 if (priv->power_mode != mode) {
9218 err = ipw_send_power_mode(priv, mode);
Jeff Garzikbf794512005-07-31 13:07:26 -04009219
James Ketrenos43f66a62005-03-25 12:31:53 -06009220 if (err) {
9221 IPW_DEBUG_WX("failed setting power mode.\n");
James Ketrenosc848d0a2005-08-24 21:56:24 -05009222 up(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06009223 return err;
9224 }
9225 }
James Ketrenosc848d0a2005-08-24 21:56:24 -05009226 up(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06009227 return 0;
9228}
9229
9230#define MAX_WX_STRING 80
Jeff Garzikbf794512005-07-31 13:07:26 -04009231static int ipw_wx_get_powermode(struct net_device *dev,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04009232 struct iw_request_info *info,
9233 union iwreq_data *wrqu, char *extra)
James Ketrenos43f66a62005-03-25 12:31:53 -06009234{
9235 struct ipw_priv *priv = ieee80211_priv(dev);
9236 int level = IPW_POWER_LEVEL(priv->power_mode);
9237 char *p = extra;
9238
9239 p += snprintf(p, MAX_WX_STRING, "Power save level: %d ", level);
9240
9241 switch (level) {
9242 case IPW_POWER_AC:
9243 p += snprintf(p, MAX_WX_STRING - (p - extra), "(AC)");
9244 break;
9245 case IPW_POWER_BATTERY:
9246 p += snprintf(p, MAX_WX_STRING - (p - extra), "(BATTERY)");
9247 break;
9248 default:
9249 p += snprintf(p, MAX_WX_STRING - (p - extra),
Jeff Garzikbf794512005-07-31 13:07:26 -04009250 "(Timeout %dms, Period %dms)",
James Ketrenos43f66a62005-03-25 12:31:53 -06009251 timeout_duration[level - 1] / 1000,
9252 period_duration[level - 1] / 1000);
9253 }
9254
9255 if (!(priv->power_mode & IPW_POWER_ENABLED))
Jeff Garzik0edd5b42005-09-07 00:48:31 -04009256 p += snprintf(p, MAX_WX_STRING - (p - extra), " OFF");
James Ketrenos43f66a62005-03-25 12:31:53 -06009257
9258 wrqu->data.length = p - extra + 1;
9259
9260 return 0;
9261}
9262
9263static int ipw_wx_set_wireless_mode(struct net_device *dev,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04009264 struct iw_request_info *info,
9265 union iwreq_data *wrqu, char *extra)
James Ketrenos43f66a62005-03-25 12:31:53 -06009266{
Jeff Garzik0edd5b42005-09-07 00:48:31 -04009267 struct ipw_priv *priv = ieee80211_priv(dev);
James Ketrenos43f66a62005-03-25 12:31:53 -06009268 int mode = *(int *)extra;
9269 u8 band = 0, modulation = 0;
9270
9271 if (mode == 0 || mode & ~IEEE_MODE_MASK) {
Jeff Garzik0edd5b42005-09-07 00:48:31 -04009272 IPW_WARNING("Attempt to set invalid wireless mode: %d\n", mode);
James Ketrenos43f66a62005-03-25 12:31:53 -06009273 return -EINVAL;
9274 }
James Ketrenosc848d0a2005-08-24 21:56:24 -05009275 down(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06009276 if (priv->adapter == IPW_2915ABG) {
James Ketrenosa33a1982005-09-14 14:28:59 -05009277 priv->ieee->abg_true = 1;
James Ketrenos43f66a62005-03-25 12:31:53 -06009278 if (mode & IEEE_A) {
9279 band |= IEEE80211_52GHZ_BAND;
9280 modulation |= IEEE80211_OFDM_MODULATION;
9281 } else
James Ketrenosa33a1982005-09-14 14:28:59 -05009282 priv->ieee->abg_true = 0;
James Ketrenos43f66a62005-03-25 12:31:53 -06009283 } else {
9284 if (mode & IEEE_A) {
9285 IPW_WARNING("Attempt to set 2200BG into "
9286 "802.11a mode\n");
James Ketrenosc848d0a2005-08-24 21:56:24 -05009287 up(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06009288 return -EINVAL;
9289 }
9290
James Ketrenosa33a1982005-09-14 14:28:59 -05009291 priv->ieee->abg_true = 0;
James Ketrenos43f66a62005-03-25 12:31:53 -06009292 }
9293
9294 if (mode & IEEE_B) {
9295 band |= IEEE80211_24GHZ_BAND;
9296 modulation |= IEEE80211_CCK_MODULATION;
9297 } else
James Ketrenosa33a1982005-09-14 14:28:59 -05009298 priv->ieee->abg_true = 0;
Jeff Garzikbf794512005-07-31 13:07:26 -04009299
James Ketrenos43f66a62005-03-25 12:31:53 -06009300 if (mode & IEEE_G) {
9301 band |= IEEE80211_24GHZ_BAND;
9302 modulation |= IEEE80211_OFDM_MODULATION;
9303 } else
James Ketrenosa33a1982005-09-14 14:28:59 -05009304 priv->ieee->abg_true = 0;
James Ketrenos43f66a62005-03-25 12:31:53 -06009305
9306 priv->ieee->mode = mode;
9307 priv->ieee->freq_band = band;
9308 priv->ieee->modulation = modulation;
Jeff Garzik0edd5b42005-09-07 00:48:31 -04009309 init_supported_rates(priv, &priv->rates);
James Ketrenos43f66a62005-03-25 12:31:53 -06009310
James Ketrenosc848d0a2005-08-24 21:56:24 -05009311 /* Network configuration changed -- force [re]association */
9312 IPW_DEBUG_ASSOC("[re]association triggered due to mode change.\n");
9313 if (!ipw_disassociate(priv)) {
James Ketrenos43f66a62005-03-25 12:31:53 -06009314 ipw_send_supported_rates(priv, &priv->rates);
James Ketrenosc848d0a2005-08-24 21:56:24 -05009315 ipw_associate(priv);
9316 }
James Ketrenos43f66a62005-03-25 12:31:53 -06009317
James Ketrenosa613bff2005-08-24 21:43:11 -05009318 /* Update the band LEDs */
9319 ipw_led_band_on(priv);
James Ketrenos43f66a62005-03-25 12:31:53 -06009320
Jeff Garzikbf794512005-07-31 13:07:26 -04009321 IPW_DEBUG_WX("PRIV SET MODE: %c%c%c\n",
James Ketrenos43f66a62005-03-25 12:31:53 -06009322 mode & IEEE_A ? 'a' : '.',
Jeff Garzik0edd5b42005-09-07 00:48:31 -04009323 mode & IEEE_B ? 'b' : '.', mode & IEEE_G ? 'g' : '.');
James Ketrenosc848d0a2005-08-24 21:56:24 -05009324 up(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06009325 return 0;
9326}
9327
9328static int ipw_wx_get_wireless_mode(struct net_device *dev,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04009329 struct iw_request_info *info,
9330 union iwreq_data *wrqu, char *extra)
James Ketrenos43f66a62005-03-25 12:31:53 -06009331{
Jeff Garzik0edd5b42005-09-07 00:48:31 -04009332 struct ipw_priv *priv = ieee80211_priv(dev);
James Ketrenosc848d0a2005-08-24 21:56:24 -05009333 down(&priv->sem);
James Ketrenosea2b26e2005-08-24 21:25:16 -05009334 switch (priv->ieee->mode) {
9335 case IEEE_A:
James Ketrenos43f66a62005-03-25 12:31:53 -06009336 strncpy(extra, "802.11a (1)", MAX_WX_STRING);
9337 break;
James Ketrenosea2b26e2005-08-24 21:25:16 -05009338 case IEEE_B:
9339 strncpy(extra, "802.11b (2)", MAX_WX_STRING);
9340 break;
9341 case IEEE_A | IEEE_B:
9342 strncpy(extra, "802.11ab (3)", MAX_WX_STRING);
9343 break;
9344 case IEEE_G:
9345 strncpy(extra, "802.11g (4)", MAX_WX_STRING);
9346 break;
9347 case IEEE_A | IEEE_G:
9348 strncpy(extra, "802.11ag (5)", MAX_WX_STRING);
9349 break;
9350 case IEEE_B | IEEE_G:
9351 strncpy(extra, "802.11bg (6)", MAX_WX_STRING);
9352 break;
9353 case IEEE_A | IEEE_B | IEEE_G:
9354 strncpy(extra, "802.11abg (7)", MAX_WX_STRING);
9355 break;
9356 default:
9357 strncpy(extra, "unknown", MAX_WX_STRING);
James Ketrenos43f66a62005-03-25 12:31:53 -06009358 break;
Jeff Garzikbf794512005-07-31 13:07:26 -04009359 }
9360
James Ketrenos43f66a62005-03-25 12:31:53 -06009361 IPW_DEBUG_WX("PRIV GET MODE: %s\n", extra);
9362
Jeff Garzik0edd5b42005-09-07 00:48:31 -04009363 wrqu->data.length = strlen(extra) + 1;
James Ketrenosc848d0a2005-08-24 21:56:24 -05009364 up(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06009365
Jeff Garzik0edd5b42005-09-07 00:48:31 -04009366 return 0;
James Ketrenos43f66a62005-03-25 12:31:53 -06009367}
9368
James Ketrenosea2b26e2005-08-24 21:25:16 -05009369static int ipw_wx_set_preamble(struct net_device *dev,
9370 struct iw_request_info *info,
9371 union iwreq_data *wrqu, char *extra)
9372{
9373 struct ipw_priv *priv = ieee80211_priv(dev);
9374 int mode = *(int *)extra;
James Ketrenosc848d0a2005-08-24 21:56:24 -05009375 down(&priv->sem);
James Ketrenosea2b26e2005-08-24 21:25:16 -05009376 /* Switching from SHORT -> LONG requires a disassociation */
9377 if (mode == 1) {
9378 if (!(priv->config & CFG_PREAMBLE_LONG)) {
9379 priv->config |= CFG_PREAMBLE_LONG;
James Ketrenosc848d0a2005-08-24 21:56:24 -05009380
9381 /* Network configuration changed -- force [re]association */
9382 IPW_DEBUG_ASSOC
9383 ("[re]association triggered due to preamble change.\n");
9384 if (!ipw_disassociate(priv))
9385 ipw_associate(priv);
James Ketrenosea2b26e2005-08-24 21:25:16 -05009386 }
9387 goto done;
9388 }
9389
9390 if (mode == 0) {
9391 priv->config &= ~CFG_PREAMBLE_LONG;
9392 goto done;
9393 }
James Ketrenosc848d0a2005-08-24 21:56:24 -05009394 up(&priv->sem);
James Ketrenosea2b26e2005-08-24 21:25:16 -05009395 return -EINVAL;
9396
9397 done:
James Ketrenosc848d0a2005-08-24 21:56:24 -05009398 up(&priv->sem);
James Ketrenosea2b26e2005-08-24 21:25:16 -05009399 return 0;
9400}
9401
9402static int ipw_wx_get_preamble(struct net_device *dev,
9403 struct iw_request_info *info,
9404 union iwreq_data *wrqu, char *extra)
9405{
9406 struct ipw_priv *priv = ieee80211_priv(dev);
James Ketrenosc848d0a2005-08-24 21:56:24 -05009407 down(&priv->sem);
James Ketrenosea2b26e2005-08-24 21:25:16 -05009408 if (priv->config & CFG_PREAMBLE_LONG)
9409 snprintf(wrqu->name, IFNAMSIZ, "long (1)");
9410 else
9411 snprintf(wrqu->name, IFNAMSIZ, "auto (0)");
James Ketrenosc848d0a2005-08-24 21:56:24 -05009412 up(&priv->sem);
James Ketrenosea2b26e2005-08-24 21:25:16 -05009413 return 0;
9414}
9415
James Ketrenosb095c382005-08-24 22:04:42 -05009416#ifdef CONFIG_IPW2200_MONITOR
James Ketrenosea2b26e2005-08-24 21:25:16 -05009417static int ipw_wx_set_monitor(struct net_device *dev,
Jeff Garzikbf794512005-07-31 13:07:26 -04009418 struct iw_request_info *info,
James Ketrenos43f66a62005-03-25 12:31:53 -06009419 union iwreq_data *wrqu, char *extra)
Jeff Garzikbf794512005-07-31 13:07:26 -04009420{
James Ketrenos43f66a62005-03-25 12:31:53 -06009421 struct ipw_priv *priv = ieee80211_priv(dev);
9422 int *parms = (int *)extra;
9423 int enable = (parms[0] > 0);
James Ketrenosc848d0a2005-08-24 21:56:24 -05009424 down(&priv->sem);
James Ketrenosea2b26e2005-08-24 21:25:16 -05009425 IPW_DEBUG_WX("SET MONITOR: %d %d\n", enable, parms[1]);
James Ketrenos43f66a62005-03-25 12:31:53 -06009426 if (enable) {
9427 if (priv->ieee->iw_mode != IW_MODE_MONITOR) {
Mike Kershaw24a47db2005-08-26 00:41:54 -05009428#ifdef CONFIG_IEEE80211_RADIOTAP
9429 priv->net_dev->type = ARPHRD_IEEE80211_RADIOTAP;
9430#else
James Ketrenos43f66a62005-03-25 12:31:53 -06009431 priv->net_dev->type = ARPHRD_IEEE80211;
Mike Kershaw24a47db2005-08-26 00:41:54 -05009432#endif
James Ketrenosa613bff2005-08-24 21:43:11 -05009433 queue_work(priv->workqueue, &priv->adapter_restart);
James Ketrenos43f66a62005-03-25 12:31:53 -06009434 }
Jeff Garzikbf794512005-07-31 13:07:26 -04009435
James Ketrenos43f66a62005-03-25 12:31:53 -06009436 ipw_set_channel(priv, parms[1]);
9437 } else {
James Ketrenosc848d0a2005-08-24 21:56:24 -05009438 if (priv->ieee->iw_mode != IW_MODE_MONITOR) {
9439 up(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06009440 return 0;
James Ketrenosc848d0a2005-08-24 21:56:24 -05009441 }
James Ketrenos43f66a62005-03-25 12:31:53 -06009442 priv->net_dev->type = ARPHRD_ETHER;
James Ketrenosa613bff2005-08-24 21:43:11 -05009443 queue_work(priv->workqueue, &priv->adapter_restart);
James Ketrenos43f66a62005-03-25 12:31:53 -06009444 }
James Ketrenosc848d0a2005-08-24 21:56:24 -05009445 up(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06009446 return 0;
9447}
9448
James Ketrenosb095c382005-08-24 22:04:42 -05009449#endif // CONFIG_IPW2200_MONITOR
9450
Jeff Garzikbf794512005-07-31 13:07:26 -04009451static int ipw_wx_reset(struct net_device *dev,
9452 struct iw_request_info *info,
James Ketrenos43f66a62005-03-25 12:31:53 -06009453 union iwreq_data *wrqu, char *extra)
Jeff Garzikbf794512005-07-31 13:07:26 -04009454{
James Ketrenos43f66a62005-03-25 12:31:53 -06009455 struct ipw_priv *priv = ieee80211_priv(dev);
9456 IPW_DEBUG_WX("RESET\n");
James Ketrenosa613bff2005-08-24 21:43:11 -05009457 queue_work(priv->workqueue, &priv->adapter_restart);
James Ketrenos43f66a62005-03-25 12:31:53 -06009458 return 0;
9459}
James Ketrenosb095c382005-08-24 22:04:42 -05009460
James Ketrenosb095c382005-08-24 22:04:42 -05009461static int ipw_wx_sw_reset(struct net_device *dev,
9462 struct iw_request_info *info,
9463 union iwreq_data *wrqu, char *extra)
9464{
9465 struct ipw_priv *priv = ieee80211_priv(dev);
9466 union iwreq_data wrqu_sec = {
9467 .encoding = {
9468 .flags = IW_ENCODE_DISABLED,
9469 },
9470 };
James Ketrenosafbf30a2005-08-25 00:05:33 -05009471 int ret;
James Ketrenosb095c382005-08-24 22:04:42 -05009472
9473 IPW_DEBUG_WX("SW_RESET\n");
9474
9475 down(&priv->sem);
9476
James Ketrenosafbf30a2005-08-25 00:05:33 -05009477 ret = ipw_sw_reset(priv, 0);
9478 if (!ret) {
9479 free_firmware();
9480 ipw_adapter_restart(priv);
9481 }
James Ketrenosb095c382005-08-24 22:04:42 -05009482
9483 /* The SW reset bit might have been toggled on by the 'disable'
9484 * module parameter, so take appropriate action */
9485 ipw_radio_kill_sw(priv, priv->status & STATUS_RF_KILL_SW);
9486
9487 up(&priv->sem);
9488 ieee80211_wx_set_encode(priv->ieee, info, &wrqu_sec, NULL);
9489 down(&priv->sem);
9490
9491 if (!(priv->status & STATUS_RF_KILL_MASK)) {
9492 /* Configuration likely changed -- force [re]association */
9493 IPW_DEBUG_ASSOC("[re]association triggered due to sw "
9494 "reset.\n");
9495 if (!ipw_disassociate(priv))
9496 ipw_associate(priv);
9497 }
9498
9499 up(&priv->sem);
9500
9501 return 0;
9502}
James Ketrenos43f66a62005-03-25 12:31:53 -06009503
9504/* Rebase the WE IOCTLs to zero for the handler array */
9505#define IW_IOCTL(x) [(x)-SIOCSIWCOMMIT]
Jeff Garzik0edd5b42005-09-07 00:48:31 -04009506static iw_handler ipw_wx_handlers[] = {
James Ketrenosea2b26e2005-08-24 21:25:16 -05009507 IW_IOCTL(SIOCGIWNAME) = ipw_wx_get_name,
9508 IW_IOCTL(SIOCSIWFREQ) = ipw_wx_set_freq,
9509 IW_IOCTL(SIOCGIWFREQ) = ipw_wx_get_freq,
9510 IW_IOCTL(SIOCSIWMODE) = ipw_wx_set_mode,
9511 IW_IOCTL(SIOCGIWMODE) = ipw_wx_get_mode,
9512 IW_IOCTL(SIOCGIWRANGE) = ipw_wx_get_range,
9513 IW_IOCTL(SIOCSIWAP) = ipw_wx_set_wap,
9514 IW_IOCTL(SIOCGIWAP) = ipw_wx_get_wap,
9515 IW_IOCTL(SIOCSIWSCAN) = ipw_wx_set_scan,
9516 IW_IOCTL(SIOCGIWSCAN) = ipw_wx_get_scan,
9517 IW_IOCTL(SIOCSIWESSID) = ipw_wx_set_essid,
9518 IW_IOCTL(SIOCGIWESSID) = ipw_wx_get_essid,
9519 IW_IOCTL(SIOCSIWNICKN) = ipw_wx_set_nick,
9520 IW_IOCTL(SIOCGIWNICKN) = ipw_wx_get_nick,
9521 IW_IOCTL(SIOCSIWRATE) = ipw_wx_set_rate,
9522 IW_IOCTL(SIOCGIWRATE) = ipw_wx_get_rate,
9523 IW_IOCTL(SIOCSIWRTS) = ipw_wx_set_rts,
9524 IW_IOCTL(SIOCGIWRTS) = ipw_wx_get_rts,
9525 IW_IOCTL(SIOCSIWFRAG) = ipw_wx_set_frag,
9526 IW_IOCTL(SIOCGIWFRAG) = ipw_wx_get_frag,
9527 IW_IOCTL(SIOCSIWTXPOW) = ipw_wx_set_txpow,
9528 IW_IOCTL(SIOCGIWTXPOW) = ipw_wx_get_txpow,
9529 IW_IOCTL(SIOCSIWRETRY) = ipw_wx_set_retry,
9530 IW_IOCTL(SIOCGIWRETRY) = ipw_wx_get_retry,
9531 IW_IOCTL(SIOCSIWENCODE) = ipw_wx_set_encode,
9532 IW_IOCTL(SIOCGIWENCODE) = ipw_wx_get_encode,
9533 IW_IOCTL(SIOCSIWPOWER) = ipw_wx_set_power,
9534 IW_IOCTL(SIOCGIWPOWER) = ipw_wx_get_power,
James Ketrenosa613bff2005-08-24 21:43:11 -05009535 IW_IOCTL(SIOCSIWSPY) = iw_handler_set_spy,
9536 IW_IOCTL(SIOCGIWSPY) = iw_handler_get_spy,
9537 IW_IOCTL(SIOCSIWTHRSPY) = iw_handler_set_thrspy,
9538 IW_IOCTL(SIOCGIWTHRSPY) = iw_handler_get_thrspy,
James Ketrenosafbf30a2005-08-25 00:05:33 -05009539 IW_IOCTL(SIOCSIWGENIE) = ipw_wx_set_genie,
9540 IW_IOCTL(SIOCGIWGENIE) = ipw_wx_get_genie,
9541 IW_IOCTL(SIOCSIWMLME) = ipw_wx_set_mlme,
9542 IW_IOCTL(SIOCSIWAUTH) = ipw_wx_set_auth,
9543 IW_IOCTL(SIOCGIWAUTH) = ipw_wx_get_auth,
9544 IW_IOCTL(SIOCSIWENCODEEXT) = ipw_wx_set_encodeext,
9545 IW_IOCTL(SIOCGIWENCODEEXT) = ipw_wx_get_encodeext,
James Ketrenos43f66a62005-03-25 12:31:53 -06009546};
9547
James Ketrenosb095c382005-08-24 22:04:42 -05009548enum {
9549 IPW_PRIV_SET_POWER = SIOCIWFIRSTPRIV,
9550 IPW_PRIV_GET_POWER,
9551 IPW_PRIV_SET_MODE,
9552 IPW_PRIV_GET_MODE,
9553 IPW_PRIV_SET_PREAMBLE,
9554 IPW_PRIV_GET_PREAMBLE,
9555 IPW_PRIV_RESET,
9556 IPW_PRIV_SW_RESET,
9557#ifdef CONFIG_IPW2200_MONITOR
9558 IPW_PRIV_SET_MONITOR,
9559#endif
9560};
James Ketrenos43f66a62005-03-25 12:31:53 -06009561
Jeff Garzikbf794512005-07-31 13:07:26 -04009562static struct iw_priv_args ipw_priv_args[] = {
James Ketrenos43f66a62005-03-25 12:31:53 -06009563 {
Jeff Garzik0edd5b42005-09-07 00:48:31 -04009564 .cmd = IPW_PRIV_SET_POWER,
9565 .set_args = IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
9566 .name = "set_power"},
James Ketrenos43f66a62005-03-25 12:31:53 -06009567 {
Jeff Garzik0edd5b42005-09-07 00:48:31 -04009568 .cmd = IPW_PRIV_GET_POWER,
9569 .get_args = IW_PRIV_TYPE_CHAR | IW_PRIV_SIZE_FIXED | MAX_WX_STRING,
9570 .name = "get_power"},
James Ketrenos43f66a62005-03-25 12:31:53 -06009571 {
Jeff Garzik0edd5b42005-09-07 00:48:31 -04009572 .cmd = IPW_PRIV_SET_MODE,
9573 .set_args = IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
9574 .name = "set_mode"},
James Ketrenos43f66a62005-03-25 12:31:53 -06009575 {
Jeff Garzik0edd5b42005-09-07 00:48:31 -04009576 .cmd = IPW_PRIV_GET_MODE,
9577 .get_args = IW_PRIV_TYPE_CHAR | IW_PRIV_SIZE_FIXED | MAX_WX_STRING,
9578 .name = "get_mode"},
James Ketrenos43f66a62005-03-25 12:31:53 -06009579 {
James Ketrenosea2b26e2005-08-24 21:25:16 -05009580 .cmd = IPW_PRIV_SET_PREAMBLE,
9581 .set_args = IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
9582 .name = "set_preamble"},
9583 {
9584 .cmd = IPW_PRIV_GET_PREAMBLE,
9585 .get_args = IW_PRIV_TYPE_CHAR | IW_PRIV_SIZE_FIXED | IFNAMSIZ,
9586 .name = "get_preamble"},
James Ketrenos43f66a62005-03-25 12:31:53 -06009587 {
Jeff Garzik0edd5b42005-09-07 00:48:31 -04009588 IPW_PRIV_RESET,
9589 IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 0, 0, "reset"},
James Ketrenosb095c382005-08-24 22:04:42 -05009590 {
9591 IPW_PRIV_SW_RESET,
9592 IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 0, 0, "sw_reset"},
9593#ifdef CONFIG_IPW2200_MONITOR
9594 {
9595 IPW_PRIV_SET_MONITOR,
9596 IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 2, 0, "monitor"},
9597#endif /* CONFIG_IPW2200_MONITOR */
James Ketrenos43f66a62005-03-25 12:31:53 -06009598};
9599
9600static iw_handler ipw_priv_handler[] = {
9601 ipw_wx_set_powermode,
9602 ipw_wx_get_powermode,
9603 ipw_wx_set_wireless_mode,
9604 ipw_wx_get_wireless_mode,
James Ketrenosea2b26e2005-08-24 21:25:16 -05009605 ipw_wx_set_preamble,
9606 ipw_wx_get_preamble,
Jeff Garzikbf794512005-07-31 13:07:26 -04009607 ipw_wx_reset,
James Ketrenosb095c382005-08-24 22:04:42 -05009608 ipw_wx_sw_reset,
9609#ifdef CONFIG_IPW2200_MONITOR
9610 ipw_wx_set_monitor,
James Ketrenos43f66a62005-03-25 12:31:53 -06009611#endif
9612};
9613
Jeff Garzik0edd5b42005-09-07 00:48:31 -04009614static struct iw_handler_def ipw_wx_handler_def = {
James Ketrenosea2b26e2005-08-24 21:25:16 -05009615 .standard = ipw_wx_handlers,
9616 .num_standard = ARRAY_SIZE(ipw_wx_handlers),
9617 .num_private = ARRAY_SIZE(ipw_priv_handler),
9618 .num_private_args = ARRAY_SIZE(ipw_priv_args),
9619 .private = ipw_priv_handler,
9620 .private_args = ipw_priv_args,
Benoit Boissinot97a78ca2005-09-15 17:30:28 +00009621 .get_wireless_stats = ipw_get_wireless_stats,
James Ketrenos43f66a62005-03-25 12:31:53 -06009622};
9623
James Ketrenos43f66a62005-03-25 12:31:53 -06009624/*
9625 * Get wireless statistics.
9626 * Called by /proc/net/wireless
9627 * Also called by SIOCGIWSTATS
9628 */
Jeff Garzik0edd5b42005-09-07 00:48:31 -04009629static struct iw_statistics *ipw_get_wireless_stats(struct net_device *dev)
James Ketrenos43f66a62005-03-25 12:31:53 -06009630{
9631 struct ipw_priv *priv = ieee80211_priv(dev);
9632 struct iw_statistics *wstats;
Jeff Garzikbf794512005-07-31 13:07:26 -04009633
James Ketrenos43f66a62005-03-25 12:31:53 -06009634 wstats = &priv->wstats;
9635
James Ketrenosea2b26e2005-08-24 21:25:16 -05009636 /* if hw is disabled, then ipw_get_ordinal() can't be called.
James Ketrenosafbf30a2005-08-25 00:05:33 -05009637 * netdev->get_wireless_stats seems to be called before fw is
James Ketrenos43f66a62005-03-25 12:31:53 -06009638 * initialized. STATUS_ASSOCIATED will only be set if the hw is up
9639 * and associated; if not associcated, the values are all meaningless
9640 * anyway, so set them all to NULL and INVALID */
9641 if (!(priv->status & STATUS_ASSOCIATED)) {
9642 wstats->miss.beacon = 0;
9643 wstats->discard.retries = 0;
9644 wstats->qual.qual = 0;
9645 wstats->qual.level = 0;
9646 wstats->qual.noise = 0;
9647 wstats->qual.updated = 7;
9648 wstats->qual.updated |= IW_QUAL_NOISE_INVALID |
Jeff Garzik0edd5b42005-09-07 00:48:31 -04009649 IW_QUAL_QUAL_INVALID | IW_QUAL_LEVEL_INVALID;
James Ketrenos43f66a62005-03-25 12:31:53 -06009650 return wstats;
Jeff Garzikbf794512005-07-31 13:07:26 -04009651 }
James Ketrenos43f66a62005-03-25 12:31:53 -06009652
9653 wstats->qual.qual = priv->quality;
9654 wstats->qual.level = average_value(&priv->average_rssi);
9655 wstats->qual.noise = average_value(&priv->average_noise);
9656 wstats->qual.updated = IW_QUAL_QUAL_UPDATED | IW_QUAL_LEVEL_UPDATED |
Jeff Garzik0edd5b42005-09-07 00:48:31 -04009657 IW_QUAL_NOISE_UPDATED;
James Ketrenos43f66a62005-03-25 12:31:53 -06009658
9659 wstats->miss.beacon = average_value(&priv->average_missed_beacons);
9660 wstats->discard.retries = priv->last_tx_failures;
9661 wstats->discard.code = priv->ieee->ieee_stats.rx_discards_undecryptable;
Jeff Garzikbf794512005-07-31 13:07:26 -04009662
James Ketrenos43f66a62005-03-25 12:31:53 -06009663/* if (ipw_get_ordinal(priv, IPW_ORD_STAT_TX_RETRY, &tx_retry, &len))
9664 goto fail_get_ordinal;
9665 wstats->discard.retries += tx_retry; */
Jeff Garzikbf794512005-07-31 13:07:26 -04009666
James Ketrenos43f66a62005-03-25 12:31:53 -06009667 return wstats;
9668}
9669
James Ketrenos43f66a62005-03-25 12:31:53 -06009670/* net device stuff */
9671
Arjan van de Ven858119e2006-01-14 13:20:43 -08009672static void init_sys_config(struct ipw_sys_config *sys_config)
James Ketrenos43f66a62005-03-25 12:31:53 -06009673{
Jeff Garzik0edd5b42005-09-07 00:48:31 -04009674 memset(sys_config, 0, sizeof(struct ipw_sys_config));
Zhu Yi810dabd2006-01-24 16:36:59 +08009675 sys_config->bt_coexistence = 0;
James Ketrenos43f66a62005-03-25 12:31:53 -06009676 sys_config->answer_broadcast_ssid_probe = 0;
9677 sys_config->accept_all_data_frames = 0;
9678 sys_config->accept_non_directed_frames = 1;
9679 sys_config->exclude_unicast_unencrypted = 0;
9680 sys_config->disable_unicast_decryption = 1;
9681 sys_config->exclude_multicast_unencrypted = 0;
9682 sys_config->disable_multicast_decryption = 1;
9683 sys_config->antenna_diversity = CFG_SYS_ANTENNA_BOTH;
Jeff Garzik0edd5b42005-09-07 00:48:31 -04009684 sys_config->pass_crc_to_host = 0; /* TODO: See if 1 gives us FCS */
James Ketrenos43f66a62005-03-25 12:31:53 -06009685 sys_config->dot11g_auto_detection = 0;
Jeff Garzikbf794512005-07-31 13:07:26 -04009686 sys_config->enable_cts_to_self = 0;
James Ketrenos43f66a62005-03-25 12:31:53 -06009687 sys_config->bt_coexist_collision_thr = 0;
James Ketrenosc848d0a2005-08-24 21:56:24 -05009688 sys_config->pass_noise_stats_to_host = 1; //1 -- fix for 256
James Ketrenos43f66a62005-03-25 12:31:53 -06009689}
9690
9691static int ipw_net_open(struct net_device *dev)
9692{
9693 struct ipw_priv *priv = ieee80211_priv(dev);
9694 IPW_DEBUG_INFO("dev->open\n");
9695 /* we should be verifying the device is ready to be opened */
James Ketrenosc848d0a2005-08-24 21:56:24 -05009696 down(&priv->sem);
Jeff Garzikbf794512005-07-31 13:07:26 -04009697 if (!(priv->status & STATUS_RF_KILL_MASK) &&
9698 (priv->status & STATUS_ASSOCIATED))
James Ketrenos43f66a62005-03-25 12:31:53 -06009699 netif_start_queue(dev);
James Ketrenosc848d0a2005-08-24 21:56:24 -05009700 up(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06009701 return 0;
9702}
9703
9704static int ipw_net_stop(struct net_device *dev)
9705{
9706 IPW_DEBUG_INFO("dev->close\n");
9707 netif_stop_queue(dev);
9708 return 0;
9709}
9710
9711/*
9712todo:
9713
9714modify to send one tfd per fragment instead of using chunking. otherwise
9715we need to heavily modify the ieee80211_skb_to_txb.
9716*/
9717
Arjan van de Ven858119e2006-01-14 13:20:43 -08009718static int ipw_tx_skb(struct ipw_priv *priv, struct ieee80211_txb *txb,
James Ketrenos227d2dc2005-07-28 16:25:55 -05009719 int pri)
James Ketrenos43f66a62005-03-25 12:31:53 -06009720{
James Ketrenos0dacca12005-09-21 12:23:41 -05009721 struct ieee80211_hdr_3addr *hdr = (struct ieee80211_hdr_3addr *)
Jeff Garzik0edd5b42005-09-07 00:48:31 -04009722 txb->fragments[0]->data;
James Ketrenos43f66a62005-03-25 12:31:53 -06009723 int i = 0;
9724 struct tfd_frame *tfd;
James Ketrenosb095c382005-08-24 22:04:42 -05009725#ifdef CONFIG_IPW_QOS
9726 int tx_id = ipw_get_tx_queue_number(priv, pri);
9727 struct clx2_tx_queue *txq = &priv->txq[tx_id];
9728#else
James Ketrenos43f66a62005-03-25 12:31:53 -06009729 struct clx2_tx_queue *txq = &priv->txq[0];
James Ketrenosb095c382005-08-24 22:04:42 -05009730#endif
James Ketrenos43f66a62005-03-25 12:31:53 -06009731 struct clx2_queue *q = &txq->q;
9732 u8 id, hdr_len, unicast;
9733 u16 remaining_bytes;
James Ketrenosc848d0a2005-08-24 21:56:24 -05009734 int fc;
James Ketrenos43f66a62005-03-25 12:31:53 -06009735
James Ketrenos227d2dc2005-07-28 16:25:55 -05009736 /* If there isn't room in the queue, we return busy and let the
9737 * network stack requeue the packet for us */
9738 if (ipw_queue_space(q) < q->high_mark)
9739 return NETDEV_TX_BUSY;
James Ketrenos43f66a62005-03-25 12:31:53 -06009740
9741 switch (priv->ieee->iw_mode) {
9742 case IW_MODE_ADHOC:
9743 hdr_len = IEEE80211_3ADDR_LEN;
Stephen Hemminger3c190652006-01-03 15:27:38 -08009744 unicast = !is_multicast_ether_addr(hdr->addr1);
James Ketrenos43f66a62005-03-25 12:31:53 -06009745 id = ipw_find_station(priv, hdr->addr1);
9746 if (id == IPW_INVALID_STATION) {
9747 id = ipw_add_station(priv, hdr->addr1);
9748 if (id == IPW_INVALID_STATION) {
9749 IPW_WARNING("Attempt to send data to "
Jeff Garzikbf794512005-07-31 13:07:26 -04009750 "invalid cell: " MAC_FMT "\n",
James Ketrenos43f66a62005-03-25 12:31:53 -06009751 MAC_ARG(hdr->addr1));
9752 goto drop;
9753 }
9754 }
9755 break;
9756
9757 case IW_MODE_INFRA:
9758 default:
Stephen Hemminger3c190652006-01-03 15:27:38 -08009759 unicast = !is_multicast_ether_addr(hdr->addr3);
James Ketrenos43f66a62005-03-25 12:31:53 -06009760 hdr_len = IEEE80211_3ADDR_LEN;
9761 id = 0;
9762 break;
9763 }
9764
9765 tfd = &txq->bd[q->first_empty];
9766 txq->txb[q->first_empty] = txb;
9767 memset(tfd, 0, sizeof(*tfd));
9768 tfd->u.data.station_number = id;
9769
9770 tfd->control_flags.message_type = TX_FRAME_TYPE;
9771 tfd->control_flags.control_bits = TFD_NEED_IRQ_MASK;
9772
9773 tfd->u.data.cmd_id = DINO_CMD_TX;
James Ketrenosa613bff2005-08-24 21:43:11 -05009774 tfd->u.data.len = cpu_to_le16(txb->payload_size);
James Ketrenos43f66a62005-03-25 12:31:53 -06009775 remaining_bytes = txb->payload_size;
Jeff Garzikbf794512005-07-31 13:07:26 -04009776
James Ketrenos43f66a62005-03-25 12:31:53 -06009777 if (priv->assoc_request.ieee_mode == IPW_B_MODE)
James Ketrenosb095c382005-08-24 22:04:42 -05009778 tfd->u.data.tx_flags_ext |= DCT_FLAG_EXT_MODE_CCK;
James Ketrenos43f66a62005-03-25 12:31:53 -06009779 else
James Ketrenosb095c382005-08-24 22:04:42 -05009780 tfd->u.data.tx_flags_ext |= DCT_FLAG_EXT_MODE_OFDM;
James Ketrenos43f66a62005-03-25 12:31:53 -06009781
James Ketrenosea2b26e2005-08-24 21:25:16 -05009782 if (priv->assoc_request.preamble_length == DCT_FLAG_SHORT_PREAMBLE)
9783 tfd->u.data.tx_flags |= DCT_FLAG_SHORT_PREAMBLE;
James Ketrenos43f66a62005-03-25 12:31:53 -06009784
James Ketrenosc848d0a2005-08-24 21:56:24 -05009785 fc = le16_to_cpu(hdr->frame_ctl);
9786 hdr->frame_ctl = cpu_to_le16(fc & ~IEEE80211_FCTL_MOREFRAGS);
James Ketrenos43f66a62005-03-25 12:31:53 -06009787
9788 memcpy(&tfd->u.data.tfd.tfd_24.mchdr, hdr, hdr_len);
9789
James Ketrenosb095c382005-08-24 22:04:42 -05009790 if (likely(unicast))
9791 tfd->u.data.tx_flags |= DCT_FLAG_ACK_REQD;
9792
9793 if (txb->encrypted && !priv->ieee->host_encrypt) {
9794 switch (priv->ieee->sec.level) {
9795 case SEC_LEVEL_3:
9796 tfd->u.data.tfd.tfd_24.mchdr.frame_ctl |=
9797 IEEE80211_FCTL_PROTECTED;
9798 /* XXX: ACK flag must be set for CCMP even if it
9799 * is a multicast/broadcast packet, because CCMP
9800 * group communication encrypted by GTK is
9801 * actually done by the AP. */
9802 if (!unicast)
9803 tfd->u.data.tx_flags |= DCT_FLAG_ACK_REQD;
9804
9805 tfd->u.data.tx_flags &= ~DCT_FLAG_NO_WEP;
9806 tfd->u.data.tx_flags_ext |= DCT_FLAG_EXT_SECURITY_CCM;
9807 tfd->u.data.key_index = 0;
9808 tfd->u.data.key_index |= DCT_WEP_INDEX_USE_IMMEDIATE;
9809 break;
9810 case SEC_LEVEL_2:
9811 tfd->u.data.tfd.tfd_24.mchdr.frame_ctl |=
9812 IEEE80211_FCTL_PROTECTED;
9813 tfd->u.data.tx_flags &= ~DCT_FLAG_NO_WEP;
9814 tfd->u.data.tx_flags_ext |= DCT_FLAG_EXT_SECURITY_TKIP;
9815 tfd->u.data.key_index = DCT_WEP_INDEX_USE_IMMEDIATE;
9816 break;
9817 case SEC_LEVEL_1:
9818 tfd->u.data.tfd.tfd_24.mchdr.frame_ctl |=
9819 IEEE80211_FCTL_PROTECTED;
9820 tfd->u.data.key_index = priv->ieee->tx_keyidx;
9821 if (priv->ieee->sec.key_sizes[priv->ieee->tx_keyidx] <=
9822 40)
9823 tfd->u.data.key_index |= DCT_WEP_KEY_64Bit;
9824 else
9825 tfd->u.data.key_index |= DCT_WEP_KEY_128Bit;
9826 break;
9827 case SEC_LEVEL_0:
9828 break;
9829 default:
9830 printk(KERN_ERR "Unknow security level %d\n",
9831 priv->ieee->sec.level);
9832 break;
9833 }
9834 } else
9835 /* No hardware encryption */
9836 tfd->u.data.tx_flags |= DCT_FLAG_NO_WEP;
9837
9838#ifdef CONFIG_IPW_QOS
9839 ipw_qos_set_tx_queue_command(priv, pri, &(tfd->u.data), unicast);
9840#endif /* CONFIG_IPW_QOS */
9841
James Ketrenos43f66a62005-03-25 12:31:53 -06009842 /* payload */
James Ketrenosa613bff2005-08-24 21:43:11 -05009843 tfd->u.data.num_chunks = cpu_to_le32(min((u8) (NUM_TFD_CHUNKS - 2),
9844 txb->nr_frags));
9845 IPW_DEBUG_FRAG("%i fragments being sent as %i chunks.\n",
9846 txb->nr_frags, le32_to_cpu(tfd->u.data.num_chunks));
9847 for (i = 0; i < le32_to_cpu(tfd->u.data.num_chunks); i++) {
9848 IPW_DEBUG_FRAG("Adding fragment %i of %i (%d bytes).\n",
9849 i, le32_to_cpu(tfd->u.data.num_chunks),
9850 txb->fragments[i]->len - hdr_len);
Jeff Garzikbf794512005-07-31 13:07:26 -04009851 IPW_DEBUG_TX("Dumping TX packet frag %i of %i (%d bytes):\n",
James Ketrenos43f66a62005-03-25 12:31:53 -06009852 i, tfd->u.data.num_chunks,
9853 txb->fragments[i]->len - hdr_len);
Jeff Garzikbf794512005-07-31 13:07:26 -04009854 printk_buf(IPW_DL_TX, txb->fragments[i]->data + hdr_len,
James Ketrenos43f66a62005-03-25 12:31:53 -06009855 txb->fragments[i]->len - hdr_len);
9856
Jeff Garzik0edd5b42005-09-07 00:48:31 -04009857 tfd->u.data.chunk_ptr[i] =
James Ketrenosa613bff2005-08-24 21:43:11 -05009858 cpu_to_le32(pci_map_single
9859 (priv->pci_dev,
9860 txb->fragments[i]->data + hdr_len,
9861 txb->fragments[i]->len - hdr_len,
9862 PCI_DMA_TODEVICE));
9863 tfd->u.data.chunk_len[i] =
9864 cpu_to_le16(txb->fragments[i]->len - hdr_len);
James Ketrenos43f66a62005-03-25 12:31:53 -06009865 }
9866
9867 if (i != txb->nr_frags) {
9868 struct sk_buff *skb;
9869 u16 remaining_bytes = 0;
9870 int j;
9871
9872 for (j = i; j < txb->nr_frags; j++)
9873 remaining_bytes += txb->fragments[j]->len - hdr_len;
9874
9875 printk(KERN_INFO "Trying to reallocate for %d bytes\n",
9876 remaining_bytes);
9877 skb = alloc_skb(remaining_bytes, GFP_ATOMIC);
9878 if (skb != NULL) {
James Ketrenosa613bff2005-08-24 21:43:11 -05009879 tfd->u.data.chunk_len[i] = cpu_to_le16(remaining_bytes);
James Ketrenos43f66a62005-03-25 12:31:53 -06009880 for (j = i; j < txb->nr_frags; j++) {
9881 int size = txb->fragments[j]->len - hdr_len;
James Ketrenosafbf30a2005-08-25 00:05:33 -05009882
James Ketrenos43f66a62005-03-25 12:31:53 -06009883 printk(KERN_INFO "Adding frag %d %d...\n",
Jeff Garzik0edd5b42005-09-07 00:48:31 -04009884 j, size);
James Ketrenos43f66a62005-03-25 12:31:53 -06009885 memcpy(skb_put(skb, size),
Jeff Garzik0edd5b42005-09-07 00:48:31 -04009886 txb->fragments[j]->data + hdr_len, size);
James Ketrenos43f66a62005-03-25 12:31:53 -06009887 }
9888 dev_kfree_skb_any(txb->fragments[i]);
9889 txb->fragments[i] = skb;
Jeff Garzik0edd5b42005-09-07 00:48:31 -04009890 tfd->u.data.chunk_ptr[i] =
James Ketrenosa613bff2005-08-24 21:43:11 -05009891 cpu_to_le32(pci_map_single
9892 (priv->pci_dev, skb->data,
9893 tfd->u.data.chunk_len[i],
9894 PCI_DMA_TODEVICE));
9895
9896 tfd->u.data.num_chunks =
9897 cpu_to_le32(le32_to_cpu(tfd->u.data.num_chunks) +
9898 1);
Jeff Garzikbf794512005-07-31 13:07:26 -04009899 }
James Ketrenos43f66a62005-03-25 12:31:53 -06009900 }
9901
9902 /* kick DMA */
9903 q->first_empty = ipw_queue_inc_wrap(q->first_empty, q->n_bd);
9904 ipw_write32(priv, q->reg_w, q->first_empty);
9905
James Ketrenos227d2dc2005-07-28 16:25:55 -05009906 return NETDEV_TX_OK;
James Ketrenos43f66a62005-03-25 12:31:53 -06009907
Jeff Garzik0edd5b42005-09-07 00:48:31 -04009908 drop:
James Ketrenos43f66a62005-03-25 12:31:53 -06009909 IPW_DEBUG_DROP("Silently dropping Tx packet.\n");
9910 ieee80211_txb_free(txb);
James Ketrenos227d2dc2005-07-28 16:25:55 -05009911 return NETDEV_TX_OK;
9912}
9913
9914static int ipw_net_is_queue_full(struct net_device *dev, int pri)
9915{
9916 struct ipw_priv *priv = ieee80211_priv(dev);
9917#ifdef CONFIG_IPW_QOS
9918 int tx_id = ipw_get_tx_queue_number(priv, pri);
9919 struct clx2_tx_queue *txq = &priv->txq[tx_id];
9920#else
9921 struct clx2_tx_queue *txq = &priv->txq[0];
9922#endif /* CONFIG_IPW_QOS */
9923
9924 if (ipw_queue_space(&txq->q) < txq->q.high_mark)
9925 return 1;
9926
9927 return 0;
James Ketrenos43f66a62005-03-25 12:31:53 -06009928}
9929
9930static int ipw_net_hard_start_xmit(struct ieee80211_txb *txb,
James Ketrenosc8d42d12005-09-21 12:23:43 -05009931 struct net_device *dev, int pri)
James Ketrenos43f66a62005-03-25 12:31:53 -06009932{
9933 struct ipw_priv *priv = ieee80211_priv(dev);
9934 unsigned long flags;
James Ketrenos227d2dc2005-07-28 16:25:55 -05009935 int ret;
James Ketrenos43f66a62005-03-25 12:31:53 -06009936
9937 IPW_DEBUG_TX("dev->xmit(%d bytes)\n", txb->payload_size);
James Ketrenos43f66a62005-03-25 12:31:53 -06009938 spin_lock_irqsave(&priv->lock, flags);
9939
9940 if (!(priv->status & STATUS_ASSOCIATED)) {
9941 IPW_DEBUG_INFO("Tx attempt while not associated.\n");
9942 priv->ieee->stats.tx_carrier_errors++;
9943 netif_stop_queue(dev);
9944 goto fail_unlock;
9945 }
9946
James Ketrenos227d2dc2005-07-28 16:25:55 -05009947 ret = ipw_tx_skb(priv, txb, pri);
9948 if (ret == NETDEV_TX_OK)
9949 __ipw_led_activity_on(priv);
James Ketrenos43f66a62005-03-25 12:31:53 -06009950 spin_unlock_irqrestore(&priv->lock, flags);
James Ketrenos43f66a62005-03-25 12:31:53 -06009951
James Ketrenos227d2dc2005-07-28 16:25:55 -05009952 return ret;
James Ketrenos43f66a62005-03-25 12:31:53 -06009953
Jeff Garzik0edd5b42005-09-07 00:48:31 -04009954 fail_unlock:
James Ketrenos43f66a62005-03-25 12:31:53 -06009955 spin_unlock_irqrestore(&priv->lock, flags);
9956 return 1;
9957}
9958
9959static struct net_device_stats *ipw_net_get_stats(struct net_device *dev)
9960{
9961 struct ipw_priv *priv = ieee80211_priv(dev);
Jeff Garzikbf794512005-07-31 13:07:26 -04009962
James Ketrenos43f66a62005-03-25 12:31:53 -06009963 priv->ieee->stats.tx_packets = priv->tx_packets;
9964 priv->ieee->stats.rx_packets = priv->rx_packets;
9965 return &priv->ieee->stats;
9966}
9967
9968static void ipw_net_set_multicast_list(struct net_device *dev)
9969{
9970
9971}
9972
9973static int ipw_net_set_mac_address(struct net_device *dev, void *p)
9974{
9975 struct ipw_priv *priv = ieee80211_priv(dev);
9976 struct sockaddr *addr = p;
9977 if (!is_valid_ether_addr(addr->sa_data))
9978 return -EADDRNOTAVAIL;
James Ketrenosc848d0a2005-08-24 21:56:24 -05009979 down(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06009980 priv->config |= CFG_CUSTOM_MAC;
9981 memcpy(priv->mac_addr, addr->sa_data, ETH_ALEN);
9982 printk(KERN_INFO "%s: Setting MAC to " MAC_FMT "\n",
9983 priv->net_dev->name, MAC_ARG(priv->mac_addr));
James Ketrenosa613bff2005-08-24 21:43:11 -05009984 queue_work(priv->workqueue, &priv->adapter_restart);
James Ketrenosc848d0a2005-08-24 21:56:24 -05009985 up(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06009986 return 0;
9987}
9988
Jeff Garzikbf794512005-07-31 13:07:26 -04009989static void ipw_ethtool_get_drvinfo(struct net_device *dev,
James Ketrenos43f66a62005-03-25 12:31:53 -06009990 struct ethtool_drvinfo *info)
9991{
9992 struct ipw_priv *p = ieee80211_priv(dev);
9993 char vers[64];
9994 char date[32];
9995 u32 len;
9996
9997 strcpy(info->driver, DRV_NAME);
9998 strcpy(info->version, DRV_VERSION);
9999
10000 len = sizeof(vers);
10001 ipw_get_ordinal(p, IPW_ORD_STAT_FW_VERSION, vers, &len);
10002 len = sizeof(date);
10003 ipw_get_ordinal(p, IPW_ORD_STAT_FW_DATE, date, &len);
10004
Jeff Garzik0edd5b42005-09-07 00:48:31 -040010005 snprintf(info->fw_version, sizeof(info->fw_version), "%s (%s)",
James Ketrenos43f66a62005-03-25 12:31:53 -060010006 vers, date);
10007 strcpy(info->bus_info, pci_name(p->pci_dev));
James Ketrenosb095c382005-08-24 22:04:42 -050010008 info->eedump_len = IPW_EEPROM_IMAGE_SIZE;
James Ketrenos43f66a62005-03-25 12:31:53 -060010009}
10010
10011static u32 ipw_ethtool_get_link(struct net_device *dev)
10012{
10013 struct ipw_priv *priv = ieee80211_priv(dev);
10014 return (priv->status & STATUS_ASSOCIATED) != 0;
10015}
10016
10017static int ipw_ethtool_get_eeprom_len(struct net_device *dev)
10018{
James Ketrenosb095c382005-08-24 22:04:42 -050010019 return IPW_EEPROM_IMAGE_SIZE;
James Ketrenos43f66a62005-03-25 12:31:53 -060010020}
10021
10022static int ipw_ethtool_get_eeprom(struct net_device *dev,
Jeff Garzik0edd5b42005-09-07 00:48:31 -040010023 struct ethtool_eeprom *eeprom, u8 * bytes)
James Ketrenos43f66a62005-03-25 12:31:53 -060010024{
10025 struct ipw_priv *p = ieee80211_priv(dev);
10026
James Ketrenosb095c382005-08-24 22:04:42 -050010027 if (eeprom->offset + eeprom->len > IPW_EEPROM_IMAGE_SIZE)
James Ketrenos43f66a62005-03-25 12:31:53 -060010028 return -EINVAL;
James Ketrenosc848d0a2005-08-24 21:56:24 -050010029 down(&p->sem);
James Ketrenosafbf30a2005-08-25 00:05:33 -050010030 memcpy(bytes, &p->eeprom[eeprom->offset], eeprom->len);
James Ketrenosc848d0a2005-08-24 21:56:24 -050010031 up(&p->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -060010032 return 0;
10033}
10034
10035static int ipw_ethtool_set_eeprom(struct net_device *dev,
Jeff Garzik0edd5b42005-09-07 00:48:31 -040010036 struct ethtool_eeprom *eeprom, u8 * bytes)
James Ketrenos43f66a62005-03-25 12:31:53 -060010037{
10038 struct ipw_priv *p = ieee80211_priv(dev);
10039 int i;
10040
James Ketrenosb095c382005-08-24 22:04:42 -050010041 if (eeprom->offset + eeprom->len > IPW_EEPROM_IMAGE_SIZE)
James Ketrenos43f66a62005-03-25 12:31:53 -060010042 return -EINVAL;
James Ketrenosc848d0a2005-08-24 21:56:24 -050010043 down(&p->sem);
James Ketrenosafbf30a2005-08-25 00:05:33 -050010044 memcpy(&p->eeprom[eeprom->offset], bytes, eeprom->len);
Jeff Garzikbf794512005-07-31 13:07:26 -040010045 for (i = IPW_EEPROM_DATA;
James Ketrenosb095c382005-08-24 22:04:42 -050010046 i < IPW_EEPROM_DATA + IPW_EEPROM_IMAGE_SIZE; i++)
James Ketrenos43f66a62005-03-25 12:31:53 -060010047 ipw_write8(p, i, p->eeprom[i]);
James Ketrenosc848d0a2005-08-24 21:56:24 -050010048 up(&p->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -060010049 return 0;
10050}
10051
10052static struct ethtool_ops ipw_ethtool_ops = {
James Ketrenosea2b26e2005-08-24 21:25:16 -050010053 .get_link = ipw_ethtool_get_link,
10054 .get_drvinfo = ipw_ethtool_get_drvinfo,
10055 .get_eeprom_len = ipw_ethtool_get_eeprom_len,
10056 .get_eeprom = ipw_ethtool_get_eeprom,
10057 .set_eeprom = ipw_ethtool_set_eeprom,
James Ketrenos43f66a62005-03-25 12:31:53 -060010058};
10059
10060static irqreturn_t ipw_isr(int irq, void *data, struct pt_regs *regs)
10061{
10062 struct ipw_priv *priv = data;
10063 u32 inta, inta_mask;
Jeff Garzikbf794512005-07-31 13:07:26 -040010064
James Ketrenos43f66a62005-03-25 12:31:53 -060010065 if (!priv)
10066 return IRQ_NONE;
10067
10068 spin_lock(&priv->lock);
10069
10070 if (!(priv->status & STATUS_INT_ENABLED)) {
10071 /* Shared IRQ */
10072 goto none;
10073 }
10074
James Ketrenosb095c382005-08-24 22:04:42 -050010075 inta = ipw_read32(priv, IPW_INTA_RW);
10076 inta_mask = ipw_read32(priv, IPW_INTA_MASK_R);
Jeff Garzikbf794512005-07-31 13:07:26 -040010077
James Ketrenos43f66a62005-03-25 12:31:53 -060010078 if (inta == 0xFFFFFFFF) {
10079 /* Hardware disappeared */
10080 IPW_WARNING("IRQ INTA == 0xFFFFFFFF\n");
10081 goto none;
10082 }
10083
James Ketrenosb095c382005-08-24 22:04:42 -050010084 if (!(inta & (IPW_INTA_MASK_ALL & inta_mask))) {
James Ketrenos43f66a62005-03-25 12:31:53 -060010085 /* Shared interrupt */
10086 goto none;
10087 }
10088
10089 /* tell the device to stop sending interrupts */
10090 ipw_disable_interrupts(priv);
Jeff Garzikbf794512005-07-31 13:07:26 -040010091
James Ketrenos43f66a62005-03-25 12:31:53 -060010092 /* ack current interrupts */
James Ketrenosb095c382005-08-24 22:04:42 -050010093 inta &= (IPW_INTA_MASK_ALL & inta_mask);
10094 ipw_write32(priv, IPW_INTA_RW, inta);
Jeff Garzikbf794512005-07-31 13:07:26 -040010095
James Ketrenos43f66a62005-03-25 12:31:53 -060010096 /* Cache INTA value for our tasklet */
10097 priv->isr_inta = inta;
10098
10099 tasklet_schedule(&priv->irq_tasklet);
10100
Jeff Garzik0edd5b42005-09-07 00:48:31 -040010101 spin_unlock(&priv->lock);
James Ketrenos43f66a62005-03-25 12:31:53 -060010102
10103 return IRQ_HANDLED;
Jeff Garzik0edd5b42005-09-07 00:48:31 -040010104 none:
James Ketrenos43f66a62005-03-25 12:31:53 -060010105 spin_unlock(&priv->lock);
10106 return IRQ_NONE;
10107}
10108
10109static void ipw_rf_kill(void *adapter)
10110{
10111 struct ipw_priv *priv = adapter;
10112 unsigned long flags;
Jeff Garzikbf794512005-07-31 13:07:26 -040010113
James Ketrenos43f66a62005-03-25 12:31:53 -060010114 spin_lock_irqsave(&priv->lock, flags);
10115
10116 if (rf_kill_active(priv)) {
10117 IPW_DEBUG_RF_KILL("RF Kill active, rescheduling GPIO check\n");
10118 if (priv->workqueue)
10119 queue_delayed_work(priv->workqueue,
10120 &priv->rf_kill, 2 * HZ);
10121 goto exit_unlock;
10122 }
10123
10124 /* RF Kill is now disabled, so bring the device back up */
10125
10126 if (!(priv->status & STATUS_RF_KILL_MASK)) {
10127 IPW_DEBUG_RF_KILL("HW RF Kill no longer active, restarting "
10128 "device\n");
10129
10130 /* we can not do an adapter restart while inside an irq lock */
10131 queue_work(priv->workqueue, &priv->adapter_restart);
Jeff Garzikbf794512005-07-31 13:07:26 -040010132 } else
James Ketrenos43f66a62005-03-25 12:31:53 -060010133 IPW_DEBUG_RF_KILL("HW RF Kill deactivated. SW RF Kill still "
10134 "enabled\n");
10135
Jeff Garzik0edd5b42005-09-07 00:48:31 -040010136 exit_unlock:
James Ketrenos43f66a62005-03-25 12:31:53 -060010137 spin_unlock_irqrestore(&priv->lock, flags);
10138}
10139
James Ketrenosc848d0a2005-08-24 21:56:24 -050010140static void ipw_bg_rf_kill(void *data)
10141{
10142 struct ipw_priv *priv = data;
10143 down(&priv->sem);
10144 ipw_rf_kill(data);
10145 up(&priv->sem);
10146}
10147
Adrian Bunka73e22b2006-01-21 01:39:42 +010010148static void ipw_link_up(struct ipw_priv *priv)
James Ketrenosa613bff2005-08-24 21:43:11 -050010149{
James Ketrenosafbf30a2005-08-25 00:05:33 -050010150 priv->last_seq_num = -1;
10151 priv->last_frag_num = -1;
10152 priv->last_packet_time = 0;
10153
James Ketrenosa613bff2005-08-24 21:43:11 -050010154 netif_carrier_on(priv->net_dev);
10155 if (netif_queue_stopped(priv->net_dev)) {
10156 IPW_DEBUG_NOTIF("waking queue\n");
10157 netif_wake_queue(priv->net_dev);
10158 } else {
10159 IPW_DEBUG_NOTIF("starting queue\n");
10160 netif_start_queue(priv->net_dev);
10161 }
10162
James Ketrenosc848d0a2005-08-24 21:56:24 -050010163 cancel_delayed_work(&priv->request_scan);
James Ketrenosa613bff2005-08-24 21:43:11 -050010164 ipw_reset_stats(priv);
10165 /* Ensure the rate is updated immediately */
10166 priv->last_rate = ipw_get_current_rate(priv);
10167 ipw_gather_stats(priv);
10168 ipw_led_link_up(priv);
10169 notify_wx_assoc_event(priv);
10170
10171 if (priv->config & CFG_BACKGROUND_SCAN)
10172 queue_delayed_work(priv->workqueue, &priv->request_scan, HZ);
10173}
10174
James Ketrenosc848d0a2005-08-24 21:56:24 -050010175static void ipw_bg_link_up(void *data)
10176{
10177 struct ipw_priv *priv = data;
10178 down(&priv->sem);
10179 ipw_link_up(data);
10180 up(&priv->sem);
10181}
10182
Adrian Bunka73e22b2006-01-21 01:39:42 +010010183static void ipw_link_down(struct ipw_priv *priv)
James Ketrenosa613bff2005-08-24 21:43:11 -050010184{
10185 ipw_led_link_down(priv);
10186 netif_carrier_off(priv->net_dev);
10187 netif_stop_queue(priv->net_dev);
10188 notify_wx_assoc_event(priv);
10189
10190 /* Cancel any queued work ... */
10191 cancel_delayed_work(&priv->request_scan);
10192 cancel_delayed_work(&priv->adhoc_check);
10193 cancel_delayed_work(&priv->gather_stats);
10194
10195 ipw_reset_stats(priv);
10196
James Ketrenosafbf30a2005-08-25 00:05:33 -050010197 if (!(priv->status & STATUS_EXIT_PENDING)) {
10198 /* Queue up another scan... */
10199 queue_work(priv->workqueue, &priv->request_scan);
10200 }
James Ketrenosa613bff2005-08-24 21:43:11 -050010201}
10202
James Ketrenosc848d0a2005-08-24 21:56:24 -050010203static void ipw_bg_link_down(void *data)
10204{
10205 struct ipw_priv *priv = data;
10206 down(&priv->sem);
10207 ipw_link_down(data);
10208 up(&priv->sem);
10209}
10210
James Ketrenos43f66a62005-03-25 12:31:53 -060010211static int ipw_setup_deferred_work(struct ipw_priv *priv)
10212{
10213 int ret = 0;
10214
James Ketrenos43f66a62005-03-25 12:31:53 -060010215 priv->workqueue = create_workqueue(DRV_NAME);
James Ketrenos43f66a62005-03-25 12:31:53 -060010216 init_waitqueue_head(&priv->wait_command_queue);
James Ketrenosafbf30a2005-08-25 00:05:33 -050010217 init_waitqueue_head(&priv->wait_state);
James Ketrenos43f66a62005-03-25 12:31:53 -060010218
James Ketrenosc848d0a2005-08-24 21:56:24 -050010219 INIT_WORK(&priv->adhoc_check, ipw_bg_adhoc_check, priv);
10220 INIT_WORK(&priv->associate, ipw_bg_associate, priv);
10221 INIT_WORK(&priv->disassociate, ipw_bg_disassociate, priv);
Zhu Yid8bad6d2005-07-13 12:25:38 -050010222 INIT_WORK(&priv->system_config, ipw_system_config, priv);
James Ketrenosc848d0a2005-08-24 21:56:24 -050010223 INIT_WORK(&priv->rx_replenish, ipw_bg_rx_queue_replenish, priv);
10224 INIT_WORK(&priv->adapter_restart, ipw_bg_adapter_restart, priv);
10225 INIT_WORK(&priv->rf_kill, ipw_bg_rf_kill, priv);
10226 INIT_WORK(&priv->up, (void (*)(void *))ipw_bg_up, priv);
10227 INIT_WORK(&priv->down, (void (*)(void *))ipw_bg_down, priv);
Jeff Garzikbf794512005-07-31 13:07:26 -040010228 INIT_WORK(&priv->request_scan,
James Ketrenos43f66a62005-03-25 12:31:53 -060010229 (void (*)(void *))ipw_request_scan, priv);
Jeff Garzikbf794512005-07-31 13:07:26 -040010230 INIT_WORK(&priv->gather_stats,
James Ketrenosc848d0a2005-08-24 21:56:24 -050010231 (void (*)(void *))ipw_bg_gather_stats, priv);
10232 INIT_WORK(&priv->abort_scan, (void (*)(void *))ipw_bg_abort_scan, priv);
10233 INIT_WORK(&priv->roam, ipw_bg_roam, priv);
10234 INIT_WORK(&priv->scan_check, ipw_bg_scan_check, priv);
10235 INIT_WORK(&priv->link_up, (void (*)(void *))ipw_bg_link_up, priv);
10236 INIT_WORK(&priv->link_down, (void (*)(void *))ipw_bg_link_down, priv);
10237 INIT_WORK(&priv->led_link_on, (void (*)(void *))ipw_bg_led_link_on,
James Ketrenosa613bff2005-08-24 21:43:11 -050010238 priv);
James Ketrenosc848d0a2005-08-24 21:56:24 -050010239 INIT_WORK(&priv->led_link_off, (void (*)(void *))ipw_bg_led_link_off,
James Ketrenosa613bff2005-08-24 21:43:11 -050010240 priv);
James Ketrenosc848d0a2005-08-24 21:56:24 -050010241 INIT_WORK(&priv->led_act_off, (void (*)(void *))ipw_bg_led_activity_off,
10242 priv);
10243 INIT_WORK(&priv->merge_networks,
10244 (void (*)(void *))ipw_merge_adhoc_network, priv);
James Ketrenos43f66a62005-03-25 12:31:53 -060010245
James Ketrenosb095c382005-08-24 22:04:42 -050010246#ifdef CONFIG_IPW_QOS
10247 INIT_WORK(&priv->qos_activate, (void (*)(void *))ipw_bg_qos_activate,
10248 priv);
10249#endif /* CONFIG_IPW_QOS */
James Ketrenos43f66a62005-03-25 12:31:53 -060010250
10251 tasklet_init(&priv->irq_tasklet, (void (*)(unsigned long))
10252 ipw_irq_tasklet, (unsigned long)priv);
10253
10254 return ret;
10255}
10256
James Ketrenos43f66a62005-03-25 12:31:53 -060010257static void shim__set_security(struct net_device *dev,
10258 struct ieee80211_security *sec)
10259{
10260 struct ipw_priv *priv = ieee80211_priv(dev);
10261 int i;
Jeff Garzikbf794512005-07-31 13:07:26 -040010262 for (i = 0; i < 4; i++) {
James Ketrenos43f66a62005-03-25 12:31:53 -060010263 if (sec->flags & (1 << i)) {
James Ketrenosafbf30a2005-08-25 00:05:33 -050010264 priv->ieee->sec.encode_alg[i] = sec->encode_alg[i];
James Ketrenosb095c382005-08-24 22:04:42 -050010265 priv->ieee->sec.key_sizes[i] = sec->key_sizes[i];
James Ketrenos43f66a62005-03-25 12:31:53 -060010266 if (sec->key_sizes[i] == 0)
James Ketrenosb095c382005-08-24 22:04:42 -050010267 priv->ieee->sec.flags &= ~(1 << i);
10268 else {
10269 memcpy(priv->ieee->sec.keys[i], sec->keys[i],
James Ketrenos43f66a62005-03-25 12:31:53 -060010270 sec->key_sizes[i]);
James Ketrenosb095c382005-08-24 22:04:42 -050010271 priv->ieee->sec.flags |= (1 << i);
10272 }
James Ketrenos43f66a62005-03-25 12:31:53 -060010273 priv->status |= STATUS_SECURITY_UPDATED;
James Ketrenosb095c382005-08-24 22:04:42 -050010274 } else if (sec->level != SEC_LEVEL_1)
10275 priv->ieee->sec.flags &= ~(1 << i);
James Ketrenos43f66a62005-03-25 12:31:53 -060010276 }
10277
James Ketrenosb095c382005-08-24 22:04:42 -050010278 if (sec->flags & SEC_ACTIVE_KEY) {
James Ketrenos43f66a62005-03-25 12:31:53 -060010279 if (sec->active_key <= 3) {
James Ketrenosb095c382005-08-24 22:04:42 -050010280 priv->ieee->sec.active_key = sec->active_key;
10281 priv->ieee->sec.flags |= SEC_ACTIVE_KEY;
Jeff Garzikbf794512005-07-31 13:07:26 -040010282 } else
James Ketrenosb095c382005-08-24 22:04:42 -050010283 priv->ieee->sec.flags &= ~SEC_ACTIVE_KEY;
James Ketrenos43f66a62005-03-25 12:31:53 -060010284 priv->status |= STATUS_SECURITY_UPDATED;
James Ketrenosb095c382005-08-24 22:04:42 -050010285 } else
10286 priv->ieee->sec.flags &= ~SEC_ACTIVE_KEY;
James Ketrenos43f66a62005-03-25 12:31:53 -060010287
10288 if ((sec->flags & SEC_AUTH_MODE) &&
James Ketrenosb095c382005-08-24 22:04:42 -050010289 (priv->ieee->sec.auth_mode != sec->auth_mode)) {
10290 priv->ieee->sec.auth_mode = sec->auth_mode;
10291 priv->ieee->sec.flags |= SEC_AUTH_MODE;
James Ketrenos43f66a62005-03-25 12:31:53 -060010292 if (sec->auth_mode == WLAN_AUTH_SHARED_KEY)
10293 priv->capability |= CAP_SHARED_KEY;
10294 else
10295 priv->capability &= ~CAP_SHARED_KEY;
10296 priv->status |= STATUS_SECURITY_UPDATED;
10297 }
Jeff Garzikbf794512005-07-31 13:07:26 -040010298
James Ketrenosb095c382005-08-24 22:04:42 -050010299 if (sec->flags & SEC_ENABLED && priv->ieee->sec.enabled != sec->enabled) {
10300 priv->ieee->sec.flags |= SEC_ENABLED;
10301 priv->ieee->sec.enabled = sec->enabled;
James Ketrenos43f66a62005-03-25 12:31:53 -060010302 priv->status |= STATUS_SECURITY_UPDATED;
Jeff Garzikbf794512005-07-31 13:07:26 -040010303 if (sec->enabled)
James Ketrenos43f66a62005-03-25 12:31:53 -060010304 priv->capability |= CAP_PRIVACY_ON;
10305 else
10306 priv->capability &= ~CAP_PRIVACY_ON;
10307 }
Jeff Garzikbf794512005-07-31 13:07:26 -040010308
James Ketrenosafbf30a2005-08-25 00:05:33 -050010309 if (sec->flags & SEC_ENCRYPT)
10310 priv->ieee->sec.encrypt = sec->encrypt;
James Ketrenos43f66a62005-03-25 12:31:53 -060010311
James Ketrenosb095c382005-08-24 22:04:42 -050010312 if (sec->flags & SEC_LEVEL && priv->ieee->sec.level != sec->level) {
10313 priv->ieee->sec.level = sec->level;
10314 priv->ieee->sec.flags |= SEC_LEVEL;
James Ketrenos43f66a62005-03-25 12:31:53 -060010315 priv->status |= STATUS_SECURITY_UPDATED;
10316 }
10317
Zhu Yi1fbfea52005-08-05 17:22:56 +080010318 if (!priv->ieee->host_encrypt && (sec->flags & SEC_ENCRYPT))
10319 ipw_set_hwcrypto_keys(priv);
10320
Jeff Garzikbf794512005-07-31 13:07:26 -040010321 /* To match current functionality of ipw2100 (which works well w/
10322 * various supplicants, we don't force a disassociate if the
James Ketrenos43f66a62005-03-25 12:31:53 -060010323 * privacy capability changes ... */
10324#if 0
10325 if ((priv->status & (STATUS_ASSOCIATED | STATUS_ASSOCIATING)) &&
Jeff Garzikbf794512005-07-31 13:07:26 -040010326 (((priv->assoc_request.capability &
James Ketrenos43f66a62005-03-25 12:31:53 -060010327 WLAN_CAPABILITY_PRIVACY) && !sec->enabled) ||
Jeff Garzikbf794512005-07-31 13:07:26 -040010328 (!(priv->assoc_request.capability &
Jeff Garzik0edd5b42005-09-07 00:48:31 -040010329 WLAN_CAPABILITY_PRIVACY) && sec->enabled))) {
James Ketrenos43f66a62005-03-25 12:31:53 -060010330 IPW_DEBUG_ASSOC("Disassociating due to capability "
10331 "change.\n");
10332 ipw_disassociate(priv);
10333 }
10334#endif
10335}
10336
Jeff Garzikbf794512005-07-31 13:07:26 -040010337static int init_supported_rates(struct ipw_priv *priv,
James Ketrenos43f66a62005-03-25 12:31:53 -060010338 struct ipw_supported_rates *rates)
10339{
10340 /* TODO: Mask out rates based on priv->rates_mask */
10341
10342 memset(rates, 0, sizeof(*rates));
Jeff Garzik0edd5b42005-09-07 00:48:31 -040010343 /* configure supported rates */
James Ketrenos43f66a62005-03-25 12:31:53 -060010344 switch (priv->ieee->freq_band) {
10345 case IEEE80211_52GHZ_BAND:
10346 rates->ieee_mode = IPW_A_MODE;
10347 rates->purpose = IPW_RATE_CAPABILITIES;
10348 ipw_add_ofdm_scan_rates(rates, IEEE80211_CCK_MODULATION,
10349 IEEE80211_OFDM_DEFAULT_RATES_MASK);
10350 break;
10351
Jeff Garzik0edd5b42005-09-07 00:48:31 -040010352 default: /* Mixed or 2.4Ghz */
James Ketrenos43f66a62005-03-25 12:31:53 -060010353 rates->ieee_mode = IPW_G_MODE;
10354 rates->purpose = IPW_RATE_CAPABILITIES;
10355 ipw_add_cck_scan_rates(rates, IEEE80211_CCK_MODULATION,
10356 IEEE80211_CCK_DEFAULT_RATES_MASK);
10357 if (priv->ieee->modulation & IEEE80211_OFDM_MODULATION) {
10358 ipw_add_ofdm_scan_rates(rates, IEEE80211_CCK_MODULATION,
10359 IEEE80211_OFDM_DEFAULT_RATES_MASK);
10360 }
10361 break;
10362 }
10363
10364 return 0;
10365}
10366
Jeff Garzikbf794512005-07-31 13:07:26 -040010367static int ipw_config(struct ipw_priv *priv)
James Ketrenos43f66a62005-03-25 12:31:53 -060010368{
James Ketrenos43f66a62005-03-25 12:31:53 -060010369 /* This is only called from ipw_up, which resets/reloads the firmware
10370 so, we don't need to first disable the card before we configure
10371 it */
Zhu Yi6de9f7f2005-08-11 14:39:33 +080010372 if (ipw_set_tx_power(priv))
James Ketrenos43f66a62005-03-25 12:31:53 -060010373 goto error;
10374
10375 /* initialize adapter address */
10376 if (ipw_send_adapter_address(priv, priv->net_dev->dev_addr))
10377 goto error;
10378
10379 /* set basic system config settings */
10380 init_sys_config(&priv->sys_config);
Zhu Yi810dabd2006-01-24 16:36:59 +080010381
10382 /* Support Bluetooth if we have BT h/w on board, and user wants to.
10383 * Does not support BT priority yet (don't abort or defer our Tx) */
10384 if (bt_coexist) {
10385 unsigned char bt_caps = priv->eeprom[EEPROM_SKU_CAPABILITY];
10386
10387 if (bt_caps & EEPROM_SKU_CAP_BT_CHANNEL_SIG)
10388 priv->sys_config.bt_coexistence
10389 |= CFG_BT_COEXISTENCE_SIGNAL_CHNL;
10390 if (bt_caps & EEPROM_SKU_CAP_BT_OOB)
10391 priv->sys_config.bt_coexistence
10392 |= CFG_BT_COEXISTENCE_OOB;
10393 }
10394
James Ketrenosc848d0a2005-08-24 21:56:24 -050010395 if (priv->ieee->iw_mode == IW_MODE_ADHOC)
10396 priv->sys_config.answer_broadcast_ssid_probe = 1;
10397 else
10398 priv->sys_config.answer_broadcast_ssid_probe = 0;
10399
James Ketrenos43f66a62005-03-25 12:31:53 -060010400 if (ipw_send_system_config(priv, &priv->sys_config))
10401 goto error;
10402
Jeff Garzik0edd5b42005-09-07 00:48:31 -040010403 init_supported_rates(priv, &priv->rates);
10404 if (ipw_send_supported_rates(priv, &priv->rates))
James Ketrenos43f66a62005-03-25 12:31:53 -060010405 goto error;
10406
10407 /* Set request-to-send threshold */
10408 if (priv->rts_threshold) {
10409 if (ipw_send_rts_threshold(priv, priv->rts_threshold))
10410 goto error;
10411 }
James Ketrenosb095c382005-08-24 22:04:42 -050010412#ifdef CONFIG_IPW_QOS
10413 IPW_DEBUG_QOS("QoS: call ipw_qos_activate\n");
10414 ipw_qos_activate(priv, NULL);
10415#endif /* CONFIG_IPW_QOS */
James Ketrenos43f66a62005-03-25 12:31:53 -060010416
10417 if (ipw_set_random_seed(priv))
10418 goto error;
Jeff Garzikbf794512005-07-31 13:07:26 -040010419
James Ketrenos43f66a62005-03-25 12:31:53 -060010420 /* final state transition to the RUN state */
10421 if (ipw_send_host_complete(priv))
10422 goto error;
10423
James Ketrenose6666192005-08-12 09:17:04 -050010424 priv->status |= STATUS_INIT;
10425
10426 ipw_led_init(priv);
10427 ipw_led_radio_on(priv);
10428 priv->notif_missed_beacons = 0;
10429
10430 /* Set hardware WEP key if it is configured. */
10431 if ((priv->capability & CAP_PRIVACY_ON) &&
10432 (priv->ieee->sec.level == SEC_LEVEL_1) &&
10433 !(priv->ieee->host_encrypt || priv->ieee->host_decrypt))
10434 ipw_set_hwcrypto_keys(priv);
James Ketrenos43f66a62005-03-25 12:31:53 -060010435
10436 return 0;
Jeff Garzikbf794512005-07-31 13:07:26 -040010437
Jeff Garzik0edd5b42005-09-07 00:48:31 -040010438 error:
James Ketrenos43f66a62005-03-25 12:31:53 -060010439 return -EIO;
10440}
10441
James Ketrenos4f36f802005-08-03 20:36:56 -050010442/*
10443 * NOTE:
10444 *
10445 * These tables have been tested in conjunction with the
10446 * Intel PRO/Wireless 2200BG and 2915ABG Network Connection Adapters.
10447 *
10448 * Altering this values, using it on other hardware, or in geographies
10449 * not intended for resale of the above mentioned Intel adapters has
10450 * not been tested.
10451 *
10452 */
10453static const struct ieee80211_geo ipw_geos[] = {
10454 { /* Restricted */
10455 "---",
10456 .bg_channels = 11,
10457 .bg = {{2412, 1}, {2417, 2}, {2422, 3},
10458 {2427, 4}, {2432, 5}, {2437, 6},
10459 {2442, 7}, {2447, 8}, {2452, 9},
10460 {2457, 10}, {2462, 11}},
10461 },
10462
10463 { /* Custom US/Canada */
10464 "ZZF",
10465 .bg_channels = 11,
10466 .bg = {{2412, 1}, {2417, 2}, {2422, 3},
10467 {2427, 4}, {2432, 5}, {2437, 6},
10468 {2442, 7}, {2447, 8}, {2452, 9},
10469 {2457, 10}, {2462, 11}},
10470 .a_channels = 8,
10471 .a = {{5180, 36},
10472 {5200, 40},
10473 {5220, 44},
10474 {5240, 48},
10475 {5260, 52, IEEE80211_CH_PASSIVE_ONLY},
10476 {5280, 56, IEEE80211_CH_PASSIVE_ONLY},
10477 {5300, 60, IEEE80211_CH_PASSIVE_ONLY},
10478 {5320, 64, IEEE80211_CH_PASSIVE_ONLY}},
10479 },
10480
10481 { /* Rest of World */
10482 "ZZD",
10483 .bg_channels = 13,
10484 .bg = {{2412, 1}, {2417, 2}, {2422, 3},
10485 {2427, 4}, {2432, 5}, {2437, 6},
10486 {2442, 7}, {2447, 8}, {2452, 9},
10487 {2457, 10}, {2462, 11}, {2467, 12},
10488 {2472, 13}},
10489 },
10490
10491 { /* Custom USA & Europe & High */
10492 "ZZA",
10493 .bg_channels = 11,
10494 .bg = {{2412, 1}, {2417, 2}, {2422, 3},
10495 {2427, 4}, {2432, 5}, {2437, 6},
10496 {2442, 7}, {2447, 8}, {2452, 9},
10497 {2457, 10}, {2462, 11}},
10498 .a_channels = 13,
10499 .a = {{5180, 36},
10500 {5200, 40},
10501 {5220, 44},
10502 {5240, 48},
10503 {5260, 52, IEEE80211_CH_PASSIVE_ONLY},
10504 {5280, 56, IEEE80211_CH_PASSIVE_ONLY},
10505 {5300, 60, IEEE80211_CH_PASSIVE_ONLY},
10506 {5320, 64, IEEE80211_CH_PASSIVE_ONLY},
10507 {5745, 149},
10508 {5765, 153},
10509 {5785, 157},
10510 {5805, 161},
10511 {5825, 165}},
10512 },
10513
10514 { /* Custom NA & Europe */
10515 "ZZB",
10516 .bg_channels = 11,
10517 .bg = {{2412, 1}, {2417, 2}, {2422, 3},
10518 {2427, 4}, {2432, 5}, {2437, 6},
10519 {2442, 7}, {2447, 8}, {2452, 9},
10520 {2457, 10}, {2462, 11}},
10521 .a_channels = 13,
10522 .a = {{5180, 36},
10523 {5200, 40},
10524 {5220, 44},
10525 {5240, 48},
10526 {5260, 52, IEEE80211_CH_PASSIVE_ONLY},
10527 {5280, 56, IEEE80211_CH_PASSIVE_ONLY},
10528 {5300, 60, IEEE80211_CH_PASSIVE_ONLY},
10529 {5320, 64, IEEE80211_CH_PASSIVE_ONLY},
10530 {5745, 149, IEEE80211_CH_PASSIVE_ONLY},
10531 {5765, 153, IEEE80211_CH_PASSIVE_ONLY},
10532 {5785, 157, IEEE80211_CH_PASSIVE_ONLY},
10533 {5805, 161, IEEE80211_CH_PASSIVE_ONLY},
10534 {5825, 165, IEEE80211_CH_PASSIVE_ONLY}},
10535 },
10536
10537 { /* Custom Japan */
10538 "ZZC",
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 .a_channels = 4,
10545 .a = {{5170, 34}, {5190, 38},
10546 {5210, 42}, {5230, 46}},
10547 },
10548
10549 { /* Custom */
10550 "ZZM",
10551 .bg_channels = 11,
10552 .bg = {{2412, 1}, {2417, 2}, {2422, 3},
10553 {2427, 4}, {2432, 5}, {2437, 6},
10554 {2442, 7}, {2447, 8}, {2452, 9},
10555 {2457, 10}, {2462, 11}},
10556 },
10557
10558 { /* Europe */
10559 "ZZE",
10560 .bg_channels = 13,
10561 .bg = {{2412, 1}, {2417, 2}, {2422, 3},
10562 {2427, 4}, {2432, 5}, {2437, 6},
10563 {2442, 7}, {2447, 8}, {2452, 9},
10564 {2457, 10}, {2462, 11}, {2467, 12},
10565 {2472, 13}},
10566 .a_channels = 19,
10567 .a = {{5180, 36},
10568 {5200, 40},
10569 {5220, 44},
10570 {5240, 48},
10571 {5260, 52, IEEE80211_CH_PASSIVE_ONLY},
10572 {5280, 56, IEEE80211_CH_PASSIVE_ONLY},
10573 {5300, 60, IEEE80211_CH_PASSIVE_ONLY},
10574 {5320, 64, IEEE80211_CH_PASSIVE_ONLY},
10575 {5500, 100, IEEE80211_CH_PASSIVE_ONLY},
10576 {5520, 104, IEEE80211_CH_PASSIVE_ONLY},
10577 {5540, 108, IEEE80211_CH_PASSIVE_ONLY},
10578 {5560, 112, IEEE80211_CH_PASSIVE_ONLY},
10579 {5580, 116, IEEE80211_CH_PASSIVE_ONLY},
10580 {5600, 120, IEEE80211_CH_PASSIVE_ONLY},
10581 {5620, 124, IEEE80211_CH_PASSIVE_ONLY},
10582 {5640, 128, IEEE80211_CH_PASSIVE_ONLY},
10583 {5660, 132, IEEE80211_CH_PASSIVE_ONLY},
10584 {5680, 136, IEEE80211_CH_PASSIVE_ONLY},
10585 {5700, 140, IEEE80211_CH_PASSIVE_ONLY}},
10586 },
10587
10588 { /* Custom Japan */
10589 "ZZJ",
10590 .bg_channels = 14,
10591 .bg = {{2412, 1}, {2417, 2}, {2422, 3},
10592 {2427, 4}, {2432, 5}, {2437, 6},
10593 {2442, 7}, {2447, 8}, {2452, 9},
10594 {2457, 10}, {2462, 11}, {2467, 12},
10595 {2472, 13}, {2484, 14, IEEE80211_CH_B_ONLY}},
10596 .a_channels = 4,
10597 .a = {{5170, 34}, {5190, 38},
10598 {5210, 42}, {5230, 46}},
10599 },
10600
James Ketrenos03520572005-10-19 16:12:31 -050010601 { /* Rest of World */
10602 "ZZR",
10603 .bg_channels = 14,
10604 .bg = {{2412, 1}, {2417, 2}, {2422, 3},
10605 {2427, 4}, {2432, 5}, {2437, 6},
10606 {2442, 7}, {2447, 8}, {2452, 9},
10607 {2457, 10}, {2462, 11}, {2467, 12},
10608 {2472, 13}, {2484, 14, IEEE80211_CH_B_ONLY |
10609 IEEE80211_CH_PASSIVE_ONLY}},
10610 },
10611
James Ketrenos4f36f802005-08-03 20:36:56 -050010612 { /* High Band */
10613 "ZZH",
10614 .bg_channels = 13,
10615 .bg = {{2412, 1}, {2417, 2}, {2422, 3},
10616 {2427, 4}, {2432, 5}, {2437, 6},
10617 {2442, 7}, {2447, 8}, {2452, 9},
10618 {2457, 10}, {2462, 11},
10619 {2467, 12, IEEE80211_CH_PASSIVE_ONLY},
10620 {2472, 13, IEEE80211_CH_PASSIVE_ONLY}},
10621 .a_channels = 4,
10622 .a = {{5745, 149}, {5765, 153},
10623 {5785, 157}, {5805, 161}},
10624 },
10625
10626 { /* Custom Europe */
10627 "ZZG",
10628 .bg_channels = 13,
10629 .bg = {{2412, 1}, {2417, 2}, {2422, 3},
10630 {2427, 4}, {2432, 5}, {2437, 6},
10631 {2442, 7}, {2447, 8}, {2452, 9},
10632 {2457, 10}, {2462, 11},
10633 {2467, 12}, {2472, 13}},
10634 .a_channels = 4,
10635 .a = {{5180, 36}, {5200, 40},
10636 {5220, 44}, {5240, 48}},
10637 },
10638
10639 { /* Europe */
10640 "ZZK",
10641 .bg_channels = 13,
10642 .bg = {{2412, 1}, {2417, 2}, {2422, 3},
10643 {2427, 4}, {2432, 5}, {2437, 6},
10644 {2442, 7}, {2447, 8}, {2452, 9},
10645 {2457, 10}, {2462, 11},
10646 {2467, 12, IEEE80211_CH_PASSIVE_ONLY},
10647 {2472, 13, IEEE80211_CH_PASSIVE_ONLY}},
10648 .a_channels = 24,
10649 .a = {{5180, 36, IEEE80211_CH_PASSIVE_ONLY},
10650 {5200, 40, IEEE80211_CH_PASSIVE_ONLY},
10651 {5220, 44, IEEE80211_CH_PASSIVE_ONLY},
10652 {5240, 48, IEEE80211_CH_PASSIVE_ONLY},
10653 {5260, 52, IEEE80211_CH_PASSIVE_ONLY},
10654 {5280, 56, IEEE80211_CH_PASSIVE_ONLY},
10655 {5300, 60, IEEE80211_CH_PASSIVE_ONLY},
10656 {5320, 64, IEEE80211_CH_PASSIVE_ONLY},
10657 {5500, 100, IEEE80211_CH_PASSIVE_ONLY},
10658 {5520, 104, IEEE80211_CH_PASSIVE_ONLY},
10659 {5540, 108, IEEE80211_CH_PASSIVE_ONLY},
10660 {5560, 112, IEEE80211_CH_PASSIVE_ONLY},
10661 {5580, 116, IEEE80211_CH_PASSIVE_ONLY},
10662 {5600, 120, IEEE80211_CH_PASSIVE_ONLY},
10663 {5620, 124, IEEE80211_CH_PASSIVE_ONLY},
10664 {5640, 128, IEEE80211_CH_PASSIVE_ONLY},
10665 {5660, 132, IEEE80211_CH_PASSIVE_ONLY},
10666 {5680, 136, IEEE80211_CH_PASSIVE_ONLY},
10667 {5700, 140, IEEE80211_CH_PASSIVE_ONLY},
10668 {5745, 149, IEEE80211_CH_PASSIVE_ONLY},
10669 {5765, 153, IEEE80211_CH_PASSIVE_ONLY},
10670 {5785, 157, IEEE80211_CH_PASSIVE_ONLY},
10671 {5805, 161, IEEE80211_CH_PASSIVE_ONLY},
10672 {5825, 165, IEEE80211_CH_PASSIVE_ONLY}},
10673 },
10674
10675 { /* Europe */
10676 "ZZL",
10677 .bg_channels = 11,
10678 .bg = {{2412, 1}, {2417, 2}, {2422, 3},
10679 {2427, 4}, {2432, 5}, {2437, 6},
10680 {2442, 7}, {2447, 8}, {2452, 9},
10681 {2457, 10}, {2462, 11}},
10682 .a_channels = 13,
10683 .a = {{5180, 36, IEEE80211_CH_PASSIVE_ONLY},
10684 {5200, 40, IEEE80211_CH_PASSIVE_ONLY},
10685 {5220, 44, IEEE80211_CH_PASSIVE_ONLY},
10686 {5240, 48, IEEE80211_CH_PASSIVE_ONLY},
10687 {5260, 52, IEEE80211_CH_PASSIVE_ONLY},
10688 {5280, 56, IEEE80211_CH_PASSIVE_ONLY},
10689 {5300, 60, IEEE80211_CH_PASSIVE_ONLY},
10690 {5320, 64, IEEE80211_CH_PASSIVE_ONLY},
10691 {5745, 149, IEEE80211_CH_PASSIVE_ONLY},
10692 {5765, 153, IEEE80211_CH_PASSIVE_ONLY},
10693 {5785, 157, IEEE80211_CH_PASSIVE_ONLY},
10694 {5805, 161, IEEE80211_CH_PASSIVE_ONLY},
10695 {5825, 165, IEEE80211_CH_PASSIVE_ONLY}},
10696 }
James Ketrenosafbf30a2005-08-25 00:05:33 -050010697};
10698
Liu Hong1fe0adb2005-08-19 09:33:10 -050010699/* GEO code borrowed from ieee80211_geo.c */
10700static int ipw_is_valid_channel(struct ieee80211_device *ieee, u8 channel)
10701{
10702 int i;
10703
10704 /* Driver needs to initialize the geography map before using
10705 * these helper functions */
10706 BUG_ON(ieee->geo.bg_channels == 0 && ieee->geo.a_channels == 0);
10707
10708 if (ieee->freq_band & IEEE80211_24GHZ_BAND)
10709 for (i = 0; i < ieee->geo.bg_channels; i++)
10710 /* NOTE: If G mode is currently supported but
10711 * this is a B only channel, we don't see it
10712 * as valid. */
10713 if ((ieee->geo.bg[i].channel == channel) &&
10714 (!(ieee->mode & IEEE_G) ||
10715 !(ieee->geo.bg[i].flags & IEEE80211_CH_B_ONLY)))
10716 return IEEE80211_24GHZ_BAND;
10717
10718 if (ieee->freq_band & IEEE80211_52GHZ_BAND)
10719 for (i = 0; i < ieee->geo.a_channels; i++)
10720 if (ieee->geo.a[i].channel == channel)
10721 return IEEE80211_52GHZ_BAND;
10722
10723 return 0;
10724}
10725
10726static int ipw_channel_to_index(struct ieee80211_device *ieee, u8 channel)
10727{
10728 int i;
10729
10730 /* Driver needs to initialize the geography map before using
10731 * these helper functions */
10732 BUG_ON(ieee->geo.bg_channels == 0 && ieee->geo.a_channels == 0);
10733
10734 if (ieee->freq_band & IEEE80211_24GHZ_BAND)
10735 for (i = 0; i < ieee->geo.bg_channels; i++)
10736 if (ieee->geo.bg[i].channel == channel)
10737 return i;
10738
10739 if (ieee->freq_band & IEEE80211_52GHZ_BAND)
10740 for (i = 0; i < ieee->geo.a_channels; i++)
10741 if (ieee->geo.a[i].channel == channel)
10742 return i;
10743
10744 return -1;
10745}
10746
10747static u8 ipw_freq_to_channel(struct ieee80211_device *ieee, u32 freq)
10748{
10749 int i;
10750
10751 /* Driver needs to initialize the geography map before using
10752 * these helper functions */
10753 BUG_ON(ieee->geo.bg_channels == 0 && ieee->geo.a_channels == 0);
10754
10755 freq /= 100000;
10756
10757 if (ieee->freq_band & IEEE80211_24GHZ_BAND)
10758 for (i = 0; i < ieee->geo.bg_channels; i++)
10759 if (ieee->geo.bg[i].freq == freq)
10760 return ieee->geo.bg[i].channel;
10761
10762 if (ieee->freq_band & IEEE80211_52GHZ_BAND)
10763 for (i = 0; i < ieee->geo.a_channels; i++)
10764 if (ieee->geo.a[i].freq == freq)
10765 return ieee->geo.a[i].channel;
10766
10767 return 0;
10768}
10769
10770static int ipw_set_geo(struct ieee80211_device *ieee,
10771 const struct ieee80211_geo *geo)
10772{
10773 memcpy(ieee->geo.name, geo->name, 3);
10774 ieee->geo.name[3] = '\0';
10775 ieee->geo.bg_channels = geo->bg_channels;
10776 ieee->geo.a_channels = geo->a_channels;
10777 memcpy(ieee->geo.bg, geo->bg, geo->bg_channels *
10778 sizeof(struct ieee80211_channel));
10779 memcpy(ieee->geo.a, geo->a, ieee->geo.a_channels *
10780 sizeof(struct ieee80211_channel));
10781 return 0;
10782}
10783
10784static const struct ieee80211_geo *ipw_get_geo(struct ieee80211_device *ieee)
10785{
10786 return &ieee->geo;
10787}
10788
James Ketrenos43f66a62005-03-25 12:31:53 -060010789#define MAX_HW_RESTARTS 5
10790static int ipw_up(struct ipw_priv *priv)
10791{
James Ketrenos4f36f802005-08-03 20:36:56 -050010792 int rc, i, j;
James Ketrenos43f66a62005-03-25 12:31:53 -060010793
10794 if (priv->status & STATUS_EXIT_PENDING)
10795 return -EIO;
10796
James Ketrenosf6c5cb72005-08-25 00:39:09 -050010797 if (cmdlog && !priv->cmdlog) {
10798 priv->cmdlog = kmalloc(sizeof(*priv->cmdlog) * cmdlog,
10799 GFP_KERNEL);
10800 if (priv->cmdlog == NULL) {
10801 IPW_ERROR("Error allocating %d command log entries.\n",
10802 cmdlog);
10803 } else {
10804 memset(priv->cmdlog, 0, sizeof(*priv->cmdlog) * cmdlog);
10805 priv->cmdlog_len = cmdlog;
10806 }
10807 }
10808
Jeff Garzik0edd5b42005-09-07 00:48:31 -040010809 for (i = 0; i < MAX_HW_RESTARTS; i++) {
Jeff Garzikbf794512005-07-31 13:07:26 -040010810 /* Load the microcode, firmware, and eeprom.
James Ketrenos43f66a62005-03-25 12:31:53 -060010811 * Also start the clocks. */
10812 rc = ipw_load(priv);
10813 if (rc) {
Peter Jonesa4f6bbb2005-08-26 00:33:34 -050010814 IPW_ERROR("Unable to load firmware: %d\n", rc);
James Ketrenos43f66a62005-03-25 12:31:53 -060010815 return rc;
10816 }
10817
10818 ipw_init_ordinals(priv);
10819 if (!(priv->config & CFG_CUSTOM_MAC))
10820 eeprom_parse_mac(priv, priv->mac_addr);
10821 memcpy(priv->net_dev->dev_addr, priv->mac_addr, ETH_ALEN);
10822
James Ketrenos4f36f802005-08-03 20:36:56 -050010823 for (j = 0; j < ARRAY_SIZE(ipw_geos); j++) {
10824 if (!memcmp(&priv->eeprom[EEPROM_COUNTRY_CODE],
10825 ipw_geos[j].name, 3))
10826 break;
10827 }
James Ketrenos03520572005-10-19 16:12:31 -050010828 if (j == ARRAY_SIZE(ipw_geos)) {
10829 IPW_WARNING("SKU [%c%c%c] not recognized.\n",
10830 priv->eeprom[EEPROM_COUNTRY_CODE + 0],
10831 priv->eeprom[EEPROM_COUNTRY_CODE + 1],
10832 priv->eeprom[EEPROM_COUNTRY_CODE + 2]);
James Ketrenos4f36f802005-08-03 20:36:56 -050010833 j = 0;
James Ketrenos03520572005-10-19 16:12:31 -050010834 }
Liu Hong1fe0adb2005-08-19 09:33:10 -050010835 if (ipw_set_geo(priv->ieee, &ipw_geos[j])) {
James Ketrenos4f36f802005-08-03 20:36:56 -050010836 IPW_WARNING("Could not set geography.");
James Ketrenos43f66a62005-03-25 12:31:53 -060010837 return 0;
James Ketrenos4f36f802005-08-03 20:36:56 -050010838 }
10839
10840 IPW_DEBUG_INFO("Geography %03d [%s] detected.\n",
10841 j, priv->ieee->geo.name);
James Ketrenosafbf30a2005-08-25 00:05:33 -050010842
James Ketrenosb095c382005-08-24 22:04:42 -050010843 if (priv->status & STATUS_RF_KILL_SW) {
10844 IPW_WARNING("Radio disabled by module parameter.\n");
10845 return 0;
10846 } else if (rf_kill_active(priv)) {
10847 IPW_WARNING("Radio Frequency Kill Switch is On:\n"
10848 "Kill switch must be turned off for "
10849 "wireless networking to work.\n");
10850 queue_delayed_work(priv->workqueue, &priv->rf_kill,
10851 2 * HZ);
James Ketrenos43f66a62005-03-25 12:31:53 -060010852 return 0;
James Ketrenosc848d0a2005-08-24 21:56:24 -050010853 }
James Ketrenos43f66a62005-03-25 12:31:53 -060010854
10855 rc = ipw_config(priv);
10856 if (!rc) {
10857 IPW_DEBUG_INFO("Configured device on count %i\n", i);
James Ketrenosafbf30a2005-08-25 00:05:33 -050010858
James Ketrenose6666192005-08-12 09:17:04 -050010859 /* If configure to try and auto-associate, kick
10860 * off a scan. */
10861 queue_work(priv->workqueue, &priv->request_scan);
James Ketrenosafbf30a2005-08-25 00:05:33 -050010862
James Ketrenos43f66a62005-03-25 12:31:53 -060010863 return 0;
James Ketrenos43f66a62005-03-25 12:31:53 -060010864 }
Jeff Garzikbf794512005-07-31 13:07:26 -040010865
James Ketrenosc848d0a2005-08-24 21:56:24 -050010866 IPW_DEBUG_INFO("Device configuration failed: 0x%08X\n", rc);
James Ketrenos43f66a62005-03-25 12:31:53 -060010867 IPW_DEBUG_INFO("Failed to config device on retry %d of %d\n",
10868 i, MAX_HW_RESTARTS);
10869
10870 /* We had an error bringing up the hardware, so take it
10871 * all the way back down so we can try again */
10872 ipw_down(priv);
10873 }
10874
Jeff Garzikbf794512005-07-31 13:07:26 -040010875 /* tried to restart and config the device for as long as our
James Ketrenos43f66a62005-03-25 12:31:53 -060010876 * patience could withstand */
Jeff Garzik0edd5b42005-09-07 00:48:31 -040010877 IPW_ERROR("Unable to initialize device after %d attempts.\n", i);
James Ketrenosc848d0a2005-08-24 21:56:24 -050010878
James Ketrenos43f66a62005-03-25 12:31:53 -060010879 return -EIO;
10880}
10881
James Ketrenosc848d0a2005-08-24 21:56:24 -050010882static void ipw_bg_up(void *data)
10883{
10884 struct ipw_priv *priv = data;
10885 down(&priv->sem);
10886 ipw_up(data);
10887 up(&priv->sem);
10888}
10889
James Ketrenosb095c382005-08-24 22:04:42 -050010890static void ipw_deinit(struct ipw_priv *priv)
James Ketrenos43f66a62005-03-25 12:31:53 -060010891{
James Ketrenosb095c382005-08-24 22:04:42 -050010892 int i;
10893
10894 if (priv->status & STATUS_SCANNING) {
10895 IPW_DEBUG_INFO("Aborting scan during shutdown.\n");
10896 ipw_abort_scan(priv);
10897 }
10898
10899 if (priv->status & STATUS_ASSOCIATED) {
10900 IPW_DEBUG_INFO("Disassociating during shutdown.\n");
10901 ipw_disassociate(priv);
10902 }
10903
10904 ipw_led_shutdown(priv);
10905
10906 /* Wait up to 1s for status to change to not scanning and not
10907 * associated (disassociation can take a while for a ful 802.11
10908 * exchange */
10909 for (i = 1000; i && (priv->status &
10910 (STATUS_DISASSOCIATING |
10911 STATUS_ASSOCIATED | STATUS_SCANNING)); i--)
10912 udelay(10);
10913
10914 if (priv->status & (STATUS_DISASSOCIATING |
10915 STATUS_ASSOCIATED | STATUS_SCANNING))
10916 IPW_DEBUG_INFO("Still associated or scanning...\n");
10917 else
10918 IPW_DEBUG_INFO("Took %dms to de-init\n", 1000 - i);
10919
James Ketrenosc848d0a2005-08-24 21:56:24 -050010920 /* Attempt to disable the card */
James Ketrenos43f66a62005-03-25 12:31:53 -060010921 ipw_send_card_disable(priv, 0);
James Ketrenosb095c382005-08-24 22:04:42 -050010922
10923 priv->status &= ~STATUS_INIT;
10924}
10925
James Ketrenos43f66a62005-03-25 12:31:53 -060010926static void ipw_down(struct ipw_priv *priv)
10927{
James Ketrenosb095c382005-08-24 22:04:42 -050010928 int exit_pending = priv->status & STATUS_EXIT_PENDING;
10929
10930 priv->status |= STATUS_EXIT_PENDING;
10931
10932 if (ipw_is_init(priv))
10933 ipw_deinit(priv);
10934
10935 /* Wipe out the EXIT_PENDING status bit if we are not actually
10936 * exiting the module */
10937 if (!exit_pending)
10938 priv->status &= ~STATUS_EXIT_PENDING;
James Ketrenos43f66a62005-03-25 12:31:53 -060010939
10940 /* tell the device to stop sending interrupts */
10941 ipw_disable_interrupts(priv);
10942
10943 /* Clear all bits but the RF Kill */
James Ketrenosb095c382005-08-24 22:04:42 -050010944 priv->status &= STATUS_RF_KILL_MASK | STATUS_EXIT_PENDING;
James Ketrenos43f66a62005-03-25 12:31:53 -060010945 netif_carrier_off(priv->net_dev);
10946 netif_stop_queue(priv->net_dev);
10947
10948 ipw_stop_nic(priv);
James Ketrenosa613bff2005-08-24 21:43:11 -050010949
10950 ipw_led_radio_off(priv);
James Ketrenos43f66a62005-03-25 12:31:53 -060010951}
10952
James Ketrenosc848d0a2005-08-24 21:56:24 -050010953static void ipw_bg_down(void *data)
10954{
10955 struct ipw_priv *priv = data;
10956 down(&priv->sem);
10957 ipw_down(data);
10958 up(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -060010959}
10960
10961/* Called by register_netdev() */
10962static int ipw_net_init(struct net_device *dev)
10963{
10964 struct ipw_priv *priv = ieee80211_priv(dev);
James Ketrenosc848d0a2005-08-24 21:56:24 -050010965 down(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -060010966
James Ketrenosc848d0a2005-08-24 21:56:24 -050010967 if (ipw_up(priv)) {
10968 up(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -060010969 return -EIO;
James Ketrenos43f66a62005-03-25 12:31:53 -060010970 }
10971
James Ketrenosc848d0a2005-08-24 21:56:24 -050010972 up(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -060010973 return 0;
10974}
10975
10976/* PCI driver stuff */
10977static struct pci_device_id card_ids[] = {
10978 {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2701, 0, 0, 0},
10979 {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2702, 0, 0, 0},
10980 {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2711, 0, 0, 0},
10981 {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2712, 0, 0, 0},
10982 {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2721, 0, 0, 0},
10983 {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2722, 0, 0, 0},
10984 {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2731, 0, 0, 0},
10985 {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2732, 0, 0, 0},
10986 {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2741, 0, 0, 0},
10987 {PCI_VENDOR_ID_INTEL, 0x1043, 0x103c, 0x2741, 0, 0, 0},
10988 {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2742, 0, 0, 0},
10989 {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2751, 0, 0, 0},
10990 {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2752, 0, 0, 0},
10991 {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2753, 0, 0, 0},
10992 {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2754, 0, 0, 0},
10993 {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2761, 0, 0, 0},
10994 {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2762, 0, 0, 0},
10995 {PCI_VENDOR_ID_INTEL, 0x104f, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
Jeff Garzik0edd5b42005-09-07 00:48:31 -040010996 {PCI_VENDOR_ID_INTEL, 0x4220, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, /* BG */
James Ketrenosa613bff2005-08-24 21:43:11 -050010997 {PCI_VENDOR_ID_INTEL, 0x4221, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, /* BG */
Jeff Garzik0edd5b42005-09-07 00:48:31 -040010998 {PCI_VENDOR_ID_INTEL, 0x4223, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, /* ABG */
10999 {PCI_VENDOR_ID_INTEL, 0x4224, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, /* ABG */
Jeff Garzikbf794512005-07-31 13:07:26 -040011000
James Ketrenos43f66a62005-03-25 12:31:53 -060011001 /* required last entry */
11002 {0,}
11003};
11004
11005MODULE_DEVICE_TABLE(pci, card_ids);
11006
11007static struct attribute *ipw_sysfs_entries[] = {
11008 &dev_attr_rf_kill.attr,
11009 &dev_attr_direct_dword.attr,
11010 &dev_attr_indirect_byte.attr,
11011 &dev_attr_indirect_dword.attr,
11012 &dev_attr_mem_gpio_reg.attr,
11013 &dev_attr_command_event_reg.attr,
11014 &dev_attr_nic_type.attr,
11015 &dev_attr_status.attr,
11016 &dev_attr_cfg.attr,
James Ketrenosb39860c2005-08-12 09:36:32 -050011017 &dev_attr_error.attr,
11018 &dev_attr_event_log.attr,
James Ketrenosf6c5cb72005-08-25 00:39:09 -050011019 &dev_attr_cmd_log.attr,
James Ketrenos43f66a62005-03-25 12:31:53 -060011020 &dev_attr_eeprom_delay.attr,
11021 &dev_attr_ucode_version.attr,
11022 &dev_attr_rtc.attr,
James Ketrenosa613bff2005-08-24 21:43:11 -050011023 &dev_attr_scan_age.attr,
11024 &dev_attr_led.attr,
James Ketrenosb095c382005-08-24 22:04:42 -050011025 &dev_attr_speed_scan.attr,
11026 &dev_attr_net_stats.attr,
James Ketrenos43f66a62005-03-25 12:31:53 -060011027 NULL
11028};
11029
11030static struct attribute_group ipw_attribute_group = {
11031 .name = NULL, /* put in device directory */
Jeff Garzik0edd5b42005-09-07 00:48:31 -040011032 .attrs = ipw_sysfs_entries,
James Ketrenos43f66a62005-03-25 12:31:53 -060011033};
11034
Jeff Garzik0edd5b42005-09-07 00:48:31 -040011035static int ipw_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
James Ketrenos43f66a62005-03-25 12:31:53 -060011036{
11037 int err = 0;
11038 struct net_device *net_dev;
11039 void __iomem *base;
11040 u32 length, val;
11041 struct ipw_priv *priv;
James Ketrenosafbf30a2005-08-25 00:05:33 -050011042 int i;
James Ketrenos43f66a62005-03-25 12:31:53 -060011043
11044 net_dev = alloc_ieee80211(sizeof(struct ipw_priv));
11045 if (net_dev == NULL) {
11046 err = -ENOMEM;
11047 goto out;
11048 }
11049
11050 priv = ieee80211_priv(net_dev);
11051 priv->ieee = netdev_priv(net_dev);
James Ketrenosa613bff2005-08-24 21:43:11 -050011052
James Ketrenos43f66a62005-03-25 12:31:53 -060011053 priv->net_dev = net_dev;
11054 priv->pci_dev = pdev;
Brice Goglin0f52bf92005-12-01 01:41:46 -080011055#ifdef CONFIG_IPW2200_DEBUG
James Ketrenos43f66a62005-03-25 12:31:53 -060011056 ipw_debug_level = debug;
11057#endif
11058 spin_lock_init(&priv->lock);
James Ketrenosafbf30a2005-08-25 00:05:33 -050011059 for (i = 0; i < IPW_IBSS_MAC_HASH_SIZE; i++)
11060 INIT_LIST_HEAD(&priv->ibss_mac_hash[i]);
James Ketrenos43f66a62005-03-25 12:31:53 -060011061
James Ketrenosc848d0a2005-08-24 21:56:24 -050011062 init_MUTEX(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -060011063 if (pci_enable_device(pdev)) {
11064 err = -ENODEV;
11065 goto out_free_ieee80211;
11066 }
11067
11068 pci_set_master(pdev);
11069
Tobias Klauser0e08b442005-06-20 14:28:41 -070011070 err = pci_set_dma_mask(pdev, DMA_32BIT_MASK);
Jeff Garzikbf794512005-07-31 13:07:26 -040011071 if (!err)
Tobias Klauser0e08b442005-06-20 14:28:41 -070011072 err = pci_set_consistent_dma_mask(pdev, DMA_32BIT_MASK);
James Ketrenos43f66a62005-03-25 12:31:53 -060011073 if (err) {
11074 printk(KERN_WARNING DRV_NAME ": No suitable DMA available.\n");
11075 goto out_pci_disable_device;
11076 }
11077
11078 pci_set_drvdata(pdev, priv);
11079
11080 err = pci_request_regions(pdev, DRV_NAME);
Jeff Garzikbf794512005-07-31 13:07:26 -040011081 if (err)
James Ketrenos43f66a62005-03-25 12:31:53 -060011082 goto out_pci_disable_device;
11083
Jeff Garzikbf794512005-07-31 13:07:26 -040011084 /* We disable the RETRY_TIMEOUT register (0x41) to keep
James Ketrenos43f66a62005-03-25 12:31:53 -060011085 * PCI Tx retries from interfering with C3 CPU state */
Jeff Garzikbf794512005-07-31 13:07:26 -040011086 pci_read_config_dword(pdev, 0x40, &val);
11087 if ((val & 0x0000ff00) != 0)
James Ketrenos43f66a62005-03-25 12:31:53 -060011088 pci_write_config_dword(pdev, 0x40, val & 0xffff00ff);
Jeff Garzikbf794512005-07-31 13:07:26 -040011089
James Ketrenos43f66a62005-03-25 12:31:53 -060011090 length = pci_resource_len(pdev, 0);
11091 priv->hw_len = length;
Jeff Garzikbf794512005-07-31 13:07:26 -040011092
James Ketrenos43f66a62005-03-25 12:31:53 -060011093 base = ioremap_nocache(pci_resource_start(pdev, 0), length);
11094 if (!base) {
11095 err = -ENODEV;
11096 goto out_pci_release_regions;
11097 }
11098
11099 priv->hw_base = base;
11100 IPW_DEBUG_INFO("pci_resource_len = 0x%08x\n", length);
11101 IPW_DEBUG_INFO("pci_resource_base = %p\n", base);
11102
11103 err = ipw_setup_deferred_work(priv);
11104 if (err) {
11105 IPW_ERROR("Unable to setup deferred work\n");
11106 goto out_iounmap;
11107 }
11108
James Ketrenosb095c382005-08-24 22:04:42 -050011109 ipw_sw_reset(priv, 1);
James Ketrenos43f66a62005-03-25 12:31:53 -060011110
Jeff Garzik0edd5b42005-09-07 00:48:31 -040011111 err = request_irq(pdev->irq, ipw_isr, SA_SHIRQ, DRV_NAME, priv);
James Ketrenos43f66a62005-03-25 12:31:53 -060011112 if (err) {
11113 IPW_ERROR("Error allocating IRQ %d\n", pdev->irq);
11114 goto out_destroy_workqueue;
11115 }
11116
11117 SET_MODULE_OWNER(net_dev);
11118 SET_NETDEV_DEV(net_dev, &pdev->dev);
11119
James Ketrenosc848d0a2005-08-24 21:56:24 -050011120 down(&priv->sem);
11121
James Ketrenos43f66a62005-03-25 12:31:53 -060011122 priv->ieee->hard_start_xmit = ipw_net_hard_start_xmit;
11123 priv->ieee->set_security = shim__set_security;
James Ketrenos227d2dc2005-07-28 16:25:55 -050011124 priv->ieee->is_queue_full = ipw_net_is_queue_full;
James Ketrenos43f66a62005-03-25 12:31:53 -060011125
James Ketrenosb095c382005-08-24 22:04:42 -050011126#ifdef CONFIG_IPW_QOS
James Ketrenos3b9990c2005-08-19 13:18:55 -050011127 priv->ieee->handle_probe_response = ipw_handle_beacon;
11128 priv->ieee->handle_beacon = ipw_handle_probe_response;
11129 priv->ieee->handle_assoc_response = ipw_handle_assoc_response;
James Ketrenosb095c382005-08-24 22:04:42 -050011130#endif /* CONFIG_IPW_QOS */
11131
James Ketrenosc848d0a2005-08-24 21:56:24 -050011132 priv->ieee->perfect_rssi = -20;
11133 priv->ieee->worst_rssi = -85;
James Ketrenos43f66a62005-03-25 12:31:53 -060011134
11135 net_dev->open = ipw_net_open;
11136 net_dev->stop = ipw_net_stop;
11137 net_dev->init = ipw_net_init;
11138 net_dev->get_stats = ipw_net_get_stats;
11139 net_dev->set_multicast_list = ipw_net_set_multicast_list;
11140 net_dev->set_mac_address = ipw_net_set_mac_address;
Benoit Boissinot97a78ca2005-09-15 17:30:28 +000011141 priv->wireless_data.spy_data = &priv->ieee->spy_data;
Benoit Boissinot97a78ca2005-09-15 17:30:28 +000011142 net_dev->wireless_data = &priv->wireless_data;
James Ketrenos43f66a62005-03-25 12:31:53 -060011143 net_dev->wireless_handlers = &ipw_wx_handler_def;
11144 net_dev->ethtool_ops = &ipw_ethtool_ops;
11145 net_dev->irq = pdev->irq;
Jeff Garzik0edd5b42005-09-07 00:48:31 -040011146 net_dev->base_addr = (unsigned long)priv->hw_base;
James Ketrenos43f66a62005-03-25 12:31:53 -060011147 net_dev->mem_start = pci_resource_start(pdev, 0);
11148 net_dev->mem_end = net_dev->mem_start + pci_resource_len(pdev, 0) - 1;
11149
11150 err = sysfs_create_group(&pdev->dev.kobj, &ipw_attribute_group);
11151 if (err) {
11152 IPW_ERROR("failed to create sysfs device attributes\n");
James Ketrenosc848d0a2005-08-24 21:56:24 -050011153 up(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -060011154 goto out_release_irq;
11155 }
11156
James Ketrenosc848d0a2005-08-24 21:56:24 -050011157 up(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -060011158 err = register_netdev(net_dev);
11159 if (err) {
11160 IPW_ERROR("failed to register network device\n");
James Ketrenosa613bff2005-08-24 21:43:11 -050011161 goto out_remove_sysfs;
James Ketrenos43f66a62005-03-25 12:31:53 -060011162 }
James Ketrenos43f66a62005-03-25 12:31:53 -060011163 return 0;
11164
James Ketrenosa613bff2005-08-24 21:43:11 -050011165 out_remove_sysfs:
James Ketrenos43f66a62005-03-25 12:31:53 -060011166 sysfs_remove_group(&pdev->dev.kobj, &ipw_attribute_group);
Jeff Garzik0edd5b42005-09-07 00:48:31 -040011167 out_release_irq:
James Ketrenos43f66a62005-03-25 12:31:53 -060011168 free_irq(pdev->irq, priv);
Jeff Garzik0edd5b42005-09-07 00:48:31 -040011169 out_destroy_workqueue:
James Ketrenos43f66a62005-03-25 12:31:53 -060011170 destroy_workqueue(priv->workqueue);
11171 priv->workqueue = NULL;
Jeff Garzik0edd5b42005-09-07 00:48:31 -040011172 out_iounmap:
James Ketrenos43f66a62005-03-25 12:31:53 -060011173 iounmap(priv->hw_base);
Jeff Garzik0edd5b42005-09-07 00:48:31 -040011174 out_pci_release_regions:
James Ketrenos43f66a62005-03-25 12:31:53 -060011175 pci_release_regions(pdev);
Jeff Garzik0edd5b42005-09-07 00:48:31 -040011176 out_pci_disable_device:
James Ketrenos43f66a62005-03-25 12:31:53 -060011177 pci_disable_device(pdev);
11178 pci_set_drvdata(pdev, NULL);
Jeff Garzik0edd5b42005-09-07 00:48:31 -040011179 out_free_ieee80211:
James Ketrenos43f66a62005-03-25 12:31:53 -060011180 free_ieee80211(priv->net_dev);
Jeff Garzik0edd5b42005-09-07 00:48:31 -040011181 out:
James Ketrenos43f66a62005-03-25 12:31:53 -060011182 return err;
11183}
11184
11185static void ipw_pci_remove(struct pci_dev *pdev)
11186{
11187 struct ipw_priv *priv = pci_get_drvdata(pdev);
James Ketrenosafbf30a2005-08-25 00:05:33 -050011188 struct list_head *p, *q;
11189 int i;
James Ketrenosb095c382005-08-24 22:04:42 -050011190
James Ketrenos43f66a62005-03-25 12:31:53 -060011191 if (!priv)
11192 return;
11193
James Ketrenosb095c382005-08-24 22:04:42 -050011194 down(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -060011195
James Ketrenosafbf30a2005-08-25 00:05:33 -050011196 priv->status |= STATUS_EXIT_PENDING;
James Ketrenos43f66a62005-03-25 12:31:53 -060011197 ipw_down(priv);
James Ketrenos43f66a62005-03-25 12:31:53 -060011198 sysfs_remove_group(&pdev->dev.kobj, &ipw_attribute_group);
11199
James Ketrenosb095c382005-08-24 22:04:42 -050011200 up(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -060011201
11202 unregister_netdev(priv->net_dev);
11203
11204 if (priv->rxq) {
11205 ipw_rx_queue_free(priv, priv->rxq);
11206 priv->rxq = NULL;
11207 }
11208 ipw_tx_queue_free(priv);
11209
James Ketrenosf6c5cb72005-08-25 00:39:09 -050011210 if (priv->cmdlog) {
11211 kfree(priv->cmdlog);
11212 priv->cmdlog = NULL;
11213 }
James Ketrenos43f66a62005-03-25 12:31:53 -060011214 /* ipw_down will ensure that there is no more pending work
11215 * in the workqueue's, so we can safely remove them now. */
James Ketrenosa613bff2005-08-24 21:43:11 -050011216 cancel_delayed_work(&priv->adhoc_check);
11217 cancel_delayed_work(&priv->gather_stats);
11218 cancel_delayed_work(&priv->request_scan);
11219 cancel_delayed_work(&priv->rf_kill);
11220 cancel_delayed_work(&priv->scan_check);
11221 destroy_workqueue(priv->workqueue);
11222 priv->workqueue = NULL;
James Ketrenos43f66a62005-03-25 12:31:53 -060011223
James Ketrenosafbf30a2005-08-25 00:05:33 -050011224 /* Free MAC hash list for ADHOC */
11225 for (i = 0; i < IPW_IBSS_MAC_HASH_SIZE; i++) {
11226 list_for_each_safe(p, q, &priv->ibss_mac_hash[i]) {
James Ketrenosafbf30a2005-08-25 00:05:33 -050011227 list_del(p);
Zhu Yi489f4452006-01-24 16:37:41 +080011228 kfree(list_entry(p, struct ipw_ibss_seq, list));
James Ketrenosafbf30a2005-08-25 00:05:33 -050011229 }
11230 }
11231
James Ketrenosb39860c2005-08-12 09:36:32 -050011232 if (priv->error) {
11233 ipw_free_error_log(priv->error);
11234 priv->error = NULL;
James Ketrenos43f66a62005-03-25 12:31:53 -060011235 }
11236
11237 free_irq(pdev->irq, priv);
11238 iounmap(priv->hw_base);
11239 pci_release_regions(pdev);
11240 pci_disable_device(pdev);
11241 pci_set_drvdata(pdev, NULL);
11242 free_ieee80211(priv->net_dev);
James Ketrenosafbf30a2005-08-25 00:05:33 -050011243 free_firmware();
James Ketrenos43f66a62005-03-25 12:31:53 -060011244}
11245
James Ketrenos43f66a62005-03-25 12:31:53 -060011246#ifdef CONFIG_PM
Pavel Machek583a4e82005-09-03 15:56:58 -070011247static int ipw_pci_suspend(struct pci_dev *pdev, pm_message_t state)
James Ketrenos43f66a62005-03-25 12:31:53 -060011248{
11249 struct ipw_priv *priv = pci_get_drvdata(pdev);
11250 struct net_device *dev = priv->net_dev;
11251
11252 printk(KERN_INFO "%s: Going into suspend...\n", dev->name);
11253
Jeff Garzik0edd5b42005-09-07 00:48:31 -040011254 /* Take down the device; powers it off, etc. */
James Ketrenos43f66a62005-03-25 12:31:53 -060011255 ipw_down(priv);
11256
11257 /* Remove the PRESENT state of the device */
11258 netif_device_detach(dev);
11259
James Ketrenos43f66a62005-03-25 12:31:53 -060011260 pci_save_state(pdev);
James Ketrenos43f66a62005-03-25 12:31:53 -060011261 pci_disable_device(pdev);
Pavel Machek583a4e82005-09-03 15:56:58 -070011262 pci_set_power_state(pdev, pci_choose_state(pdev, state));
Jeff Garzikbf794512005-07-31 13:07:26 -040011263
James Ketrenos43f66a62005-03-25 12:31:53 -060011264 return 0;
11265}
11266
11267static int ipw_pci_resume(struct pci_dev *pdev)
11268{
11269 struct ipw_priv *priv = pci_get_drvdata(pdev);
11270 struct net_device *dev = priv->net_dev;
11271 u32 val;
Jeff Garzikbf794512005-07-31 13:07:26 -040011272
James Ketrenos43f66a62005-03-25 12:31:53 -060011273 printk(KERN_INFO "%s: Coming out of suspend...\n", dev->name);
11274
James Ketrenosea2b26e2005-08-24 21:25:16 -050011275 pci_set_power_state(pdev, PCI_D0);
James Ketrenos43f66a62005-03-25 12:31:53 -060011276 pci_enable_device(pdev);
James Ketrenos43f66a62005-03-25 12:31:53 -060011277 pci_restore_state(pdev);
James Ketrenosea2b26e2005-08-24 21:25:16 -050011278
James Ketrenos43f66a62005-03-25 12:31:53 -060011279 /*
11280 * Suspend/Resume resets the PCI configuration space, so we have to
11281 * re-disable the RETRY_TIMEOUT register (0x41) to keep PCI Tx retries
11282 * from interfering with C3 CPU state. pci_restore_state won't help
11283 * here since it only restores the first 64 bytes pci config header.
11284 */
Jeff Garzikbf794512005-07-31 13:07:26 -040011285 pci_read_config_dword(pdev, 0x40, &val);
11286 if ((val & 0x0000ff00) != 0)
James Ketrenos43f66a62005-03-25 12:31:53 -060011287 pci_write_config_dword(pdev, 0x40, val & 0xffff00ff);
11288
11289 /* Set the device back into the PRESENT state; this will also wake
11290 * the queue of needed */
11291 netif_device_attach(dev);
11292
11293 /* Bring the device back up */
11294 queue_work(priv->workqueue, &priv->up);
Jeff Garzikbf794512005-07-31 13:07:26 -040011295
James Ketrenos43f66a62005-03-25 12:31:53 -060011296 return 0;
11297}
11298#endif
11299
11300/* driver initialization stuff */
11301static struct pci_driver ipw_driver = {
11302 .name = DRV_NAME,
11303 .id_table = card_ids,
11304 .probe = ipw_pci_probe,
11305 .remove = __devexit_p(ipw_pci_remove),
11306#ifdef CONFIG_PM
11307 .suspend = ipw_pci_suspend,
11308 .resume = ipw_pci_resume,
11309#endif
11310};
11311
11312static int __init ipw_init(void)
11313{
11314 int ret;
11315
11316 printk(KERN_INFO DRV_NAME ": " DRV_DESCRIPTION ", " DRV_VERSION "\n");
11317 printk(KERN_INFO DRV_NAME ": " DRV_COPYRIGHT "\n");
11318
11319 ret = pci_module_init(&ipw_driver);
11320 if (ret) {
11321 IPW_ERROR("Unable to initialize PCI module\n");
11322 return ret;
11323 }
11324
Jeff Garzik0edd5b42005-09-07 00:48:31 -040011325 ret = driver_create_file(&ipw_driver.driver, &driver_attr_debug_level);
James Ketrenos43f66a62005-03-25 12:31:53 -060011326 if (ret) {
11327 IPW_ERROR("Unable to create driver sysfs file\n");
11328 pci_unregister_driver(&ipw_driver);
11329 return ret;
11330 }
11331
11332 return ret;
11333}
11334
11335static void __exit ipw_exit(void)
11336{
11337 driver_remove_file(&ipw_driver.driver, &driver_attr_debug_level);
11338 pci_unregister_driver(&ipw_driver);
11339}
11340
11341module_param(disable, int, 0444);
11342MODULE_PARM_DESC(disable, "manually disable the radio (default 0 [radio on])");
11343
11344module_param(associate, int, 0444);
11345MODULE_PARM_DESC(associate, "auto associate when scanning (default on)");
11346
11347module_param(auto_create, int, 0444);
11348MODULE_PARM_DESC(auto_create, "auto create adhoc network (default on)");
11349
James Ketrenosa613bff2005-08-24 21:43:11 -050011350module_param(led, int, 0444);
James Ketrenosc848d0a2005-08-24 21:56:24 -050011351MODULE_PARM_DESC(led, "enable led control on some systems (default 0 off)\n");
James Ketrenosa613bff2005-08-24 21:43:11 -050011352
James Ketrenos43f66a62005-03-25 12:31:53 -060011353module_param(debug, int, 0444);
11354MODULE_PARM_DESC(debug, "debug output mask");
11355
11356module_param(channel, int, 0444);
Jeff Garzikbf794512005-07-31 13:07:26 -040011357MODULE_PARM_DESC(channel, "channel to limit associate to (default 0 [ANY])");
James Ketrenos43f66a62005-03-25 12:31:53 -060011358
James Ketrenosb095c382005-08-24 22:04:42 -050011359#ifdef CONFIG_IPW_QOS
11360module_param(qos_enable, int, 0444);
11361MODULE_PARM_DESC(qos_enable, "enable all QoS functionalitis");
James Ketrenos43f66a62005-03-25 12:31:53 -060011362
James Ketrenosb095c382005-08-24 22:04:42 -050011363module_param(qos_burst_enable, int, 0444);
11364MODULE_PARM_DESC(qos_burst_enable, "enable QoS burst mode");
11365
11366module_param(qos_no_ack_mask, int, 0444);
11367MODULE_PARM_DESC(qos_no_ack_mask, "mask Tx_Queue to no ack");
11368
11369module_param(burst_duration_CCK, int, 0444);
11370MODULE_PARM_DESC(burst_duration_CCK, "set CCK burst value");
11371
11372module_param(burst_duration_OFDM, int, 0444);
11373MODULE_PARM_DESC(burst_duration_OFDM, "set OFDM burst value");
11374#endif /* CONFIG_IPW_QOS */
11375
11376#ifdef CONFIG_IPW2200_MONITOR
James Ketrenos43f66a62005-03-25 12:31:53 -060011377module_param(mode, int, 0444);
11378MODULE_PARM_DESC(mode, "network mode (0=BSS,1=IBSS,2=Monitor)");
11379#else
11380module_param(mode, int, 0444);
11381MODULE_PARM_DESC(mode, "network mode (0=BSS,1=IBSS)");
11382#endif
11383
Zhu Yi810dabd2006-01-24 16:36:59 +080011384module_param(bt_coexist, int, 0444);
11385MODULE_PARM_DESC(bt_coexist, "enable bluetooth coexistence (default off)");
11386
James Ketrenosb095c382005-08-24 22:04:42 -050011387module_param(hwcrypto, int, 0444);
11388MODULE_PARM_DESC(hwcrypto, "enable hardware crypto (default on)");
11389
James Ketrenosf6c5cb72005-08-25 00:39:09 -050011390module_param(cmdlog, int, 0444);
11391MODULE_PARM_DESC(cmdlog,
11392 "allocate a ring buffer for logging firmware commands");
11393
Zhu Yi4bfdb912006-01-24 16:37:16 +080011394module_param(roaming, int, 0444);
11395MODULE_PARM_DESC(roaming, "enable roaming support (default on)");
11396
James Ketrenos43f66a62005-03-25 12:31:53 -060011397module_exit(ipw_exit);
11398module_init(ipw_init);