blob: bcb5993b68bd8b214754775216340d3da11836b5 [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"
34
James Ketrenosafbf30a2005-08-25 00:05:33 -050035#define IPW2200_VERSION "1.0.5"
James Ketrenos43f66a62005-03-25 12:31:53 -060036#define DRV_DESCRIPTION "Intel(R) PRO/Wireless 2200/2915 Network Driver"
James Ketrenos2b184d52005-08-03 20:33:14 -050037#define DRV_COPYRIGHT "Copyright(c) 2003-2005 Intel Corporation"
James Ketrenos43f66a62005-03-25 12:31:53 -060038#define DRV_VERSION IPW2200_VERSION
39
James Ketrenosb095c382005-08-24 22:04:42 -050040#define ETH_P_80211_STATS (ETH_P_80211_RAW + 1)
41
James Ketrenos43f66a62005-03-25 12:31:53 -060042MODULE_DESCRIPTION(DRV_DESCRIPTION);
43MODULE_VERSION(DRV_VERSION);
44MODULE_AUTHOR(DRV_COPYRIGHT);
45MODULE_LICENSE("GPL");
46
James Ketrenosf6c5cb72005-08-25 00:39:09 -050047static int cmdlog = 0;
James Ketrenos43f66a62005-03-25 12:31:53 -060048static int debug = 0;
49static int channel = 0;
James Ketrenos43f66a62005-03-25 12:31:53 -060050static int mode = 0;
51
52static u32 ipw_debug_level;
53static int associate = 1;
54static int auto_create = 1;
James Ketrenosa613bff2005-08-24 21:43:11 -050055static int led = 0;
James Ketrenos43f66a62005-03-25 12:31:53 -060056static int disable = 0;
James Ketrenosb095c382005-08-24 22:04:42 -050057static int hwcrypto = 1;
James Ketrenos43f66a62005-03-25 12:31:53 -060058static const char ipw_modes[] = {
59 'a', 'b', 'g', '?'
60};
61
James Ketrenosb095c382005-08-24 22:04:42 -050062#ifdef CONFIG_IPW_QOS
63static int qos_enable = 0;
64static int qos_burst_enable = 0;
65static int qos_no_ack_mask = 0;
66static int burst_duration_CCK = 0;
67static int burst_duration_OFDM = 0;
68
69static struct ieee80211_qos_parameters def_qos_parameters_OFDM = {
70 {QOS_TX0_CW_MIN_OFDM, QOS_TX1_CW_MIN_OFDM, QOS_TX2_CW_MIN_OFDM,
71 QOS_TX3_CW_MIN_OFDM},
72 {QOS_TX0_CW_MAX_OFDM, QOS_TX1_CW_MAX_OFDM, QOS_TX2_CW_MAX_OFDM,
73 QOS_TX3_CW_MAX_OFDM},
74 {QOS_TX0_AIFS, QOS_TX1_AIFS, QOS_TX2_AIFS, QOS_TX3_AIFS},
75 {QOS_TX0_ACM, QOS_TX1_ACM, QOS_TX2_ACM, QOS_TX3_ACM},
76 {QOS_TX0_TXOP_LIMIT_OFDM, QOS_TX1_TXOP_LIMIT_OFDM,
77 QOS_TX2_TXOP_LIMIT_OFDM, QOS_TX3_TXOP_LIMIT_OFDM}
78};
79
80static struct ieee80211_qos_parameters def_qos_parameters_CCK = {
81 {QOS_TX0_CW_MIN_CCK, QOS_TX1_CW_MIN_CCK, QOS_TX2_CW_MIN_CCK,
82 QOS_TX3_CW_MIN_CCK},
83 {QOS_TX0_CW_MAX_CCK, QOS_TX1_CW_MAX_CCK, QOS_TX2_CW_MAX_CCK,
84 QOS_TX3_CW_MAX_CCK},
85 {QOS_TX0_AIFS, QOS_TX1_AIFS, QOS_TX2_AIFS, QOS_TX3_AIFS},
86 {QOS_TX0_ACM, QOS_TX1_ACM, QOS_TX2_ACM, QOS_TX3_ACM},
87 {QOS_TX0_TXOP_LIMIT_CCK, QOS_TX1_TXOP_LIMIT_CCK, QOS_TX2_TXOP_LIMIT_CCK,
88 QOS_TX3_TXOP_LIMIT_CCK}
89};
90
91static struct ieee80211_qos_parameters def_parameters_OFDM = {
92 {DEF_TX0_CW_MIN_OFDM, DEF_TX1_CW_MIN_OFDM, DEF_TX2_CW_MIN_OFDM,
93 DEF_TX3_CW_MIN_OFDM},
94 {DEF_TX0_CW_MAX_OFDM, DEF_TX1_CW_MAX_OFDM, DEF_TX2_CW_MAX_OFDM,
95 DEF_TX3_CW_MAX_OFDM},
96 {DEF_TX0_AIFS, DEF_TX1_AIFS, DEF_TX2_AIFS, DEF_TX3_AIFS},
97 {DEF_TX0_ACM, DEF_TX1_ACM, DEF_TX2_ACM, DEF_TX3_ACM},
98 {DEF_TX0_TXOP_LIMIT_OFDM, DEF_TX1_TXOP_LIMIT_OFDM,
99 DEF_TX2_TXOP_LIMIT_OFDM, DEF_TX3_TXOP_LIMIT_OFDM}
100};
101
102static struct ieee80211_qos_parameters def_parameters_CCK = {
103 {DEF_TX0_CW_MIN_CCK, DEF_TX1_CW_MIN_CCK, DEF_TX2_CW_MIN_CCK,
104 DEF_TX3_CW_MIN_CCK},
105 {DEF_TX0_CW_MAX_CCK, DEF_TX1_CW_MAX_CCK, DEF_TX2_CW_MAX_CCK,
106 DEF_TX3_CW_MAX_CCK},
107 {DEF_TX0_AIFS, DEF_TX1_AIFS, DEF_TX2_AIFS, DEF_TX3_AIFS},
108 {DEF_TX0_ACM, DEF_TX1_ACM, DEF_TX2_ACM, DEF_TX3_ACM},
109 {DEF_TX0_TXOP_LIMIT_CCK, DEF_TX1_TXOP_LIMIT_CCK, DEF_TX2_TXOP_LIMIT_CCK,
110 DEF_TX3_TXOP_LIMIT_CCK}
111};
112
113static u8 qos_oui[QOS_OUI_LEN] = { 0x00, 0x50, 0xF2 };
114
115static int from_priority_to_tx_queue[] = {
116 IPW_TX_QUEUE_1, IPW_TX_QUEUE_2, IPW_TX_QUEUE_2, IPW_TX_QUEUE_1,
117 IPW_TX_QUEUE_3, IPW_TX_QUEUE_3, IPW_TX_QUEUE_4, IPW_TX_QUEUE_4
118};
119
120static u32 ipw_qos_get_burst_duration(struct ipw_priv *priv);
121
122static int ipw_send_qos_params_command(struct ipw_priv *priv, struct ieee80211_qos_parameters
123 *qos_param);
124static int ipw_send_qos_info_command(struct ipw_priv *priv, struct ieee80211_qos_information_element
125 *qos_param);
126#endif /* CONFIG_IPW_QOS */
127
128static void ipw_remove_current_network(struct ipw_priv *priv);
James Ketrenos43f66a62005-03-25 12:31:53 -0600129static void ipw_rx(struct ipw_priv *priv);
Jeff Garzikbf794512005-07-31 13:07:26 -0400130static int ipw_queue_tx_reclaim(struct ipw_priv *priv,
James Ketrenos43f66a62005-03-25 12:31:53 -0600131 struct clx2_tx_queue *txq, int qindex);
132static int ipw_queue_reset(struct ipw_priv *priv);
133
134static int ipw_queue_tx_hcmd(struct ipw_priv *priv, int hcmd, void *buf,
135 int len, int sync);
136
137static void ipw_tx_queue_free(struct ipw_priv *);
138
139static struct ipw_rx_queue *ipw_rx_queue_alloc(struct ipw_priv *);
140static void ipw_rx_queue_free(struct ipw_priv *, struct ipw_rx_queue *);
141static void ipw_rx_queue_replenish(void *);
James Ketrenos43f66a62005-03-25 12:31:53 -0600142static int ipw_up(struct ipw_priv *);
James Ketrenosc848d0a2005-08-24 21:56:24 -0500143static void ipw_bg_up(void *);
James Ketrenos43f66a62005-03-25 12:31:53 -0600144static void ipw_down(struct ipw_priv *);
James Ketrenosc848d0a2005-08-24 21:56:24 -0500145static void ipw_bg_down(void *);
James Ketrenos43f66a62005-03-25 12:31:53 -0600146static int ipw_config(struct ipw_priv *);
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400147static int init_supported_rates(struct ipw_priv *priv,
148 struct ipw_supported_rates *prates);
James Ketrenosb095c382005-08-24 22:04:42 -0500149static void ipw_set_hwcrypto_keys(struct ipw_priv *);
150static void ipw_send_wep_keys(struct ipw_priv *, int);
James Ketrenos43f66a62005-03-25 12:31:53 -0600151
James Ketrenosf6c5cb72005-08-25 00:39:09 -0500152static int snprint_line(char *buf, size_t count,
153 const u8 * data, u32 len, u32 ofs)
James Ketrenos43f66a62005-03-25 12:31:53 -0600154{
155 int out, i, j, l;
156 char c;
Jeff Garzikbf794512005-07-31 13:07:26 -0400157
James Ketrenos43f66a62005-03-25 12:31:53 -0600158 out = snprintf(buf, count, "%08X", ofs);
159
160 for (l = 0, i = 0; i < 2; i++) {
161 out += snprintf(buf + out, count - out, " ");
Jeff Garzikbf794512005-07-31 13:07:26 -0400162 for (j = 0; j < 8 && l < len; j++, l++)
163 out += snprintf(buf + out, count - out, "%02X ",
James Ketrenos43f66a62005-03-25 12:31:53 -0600164 data[(i * 8 + j)]);
165 for (; j < 8; j++)
166 out += snprintf(buf + out, count - out, " ");
167 }
Jeff Garzikbf794512005-07-31 13:07:26 -0400168
James Ketrenos43f66a62005-03-25 12:31:53 -0600169 out += snprintf(buf + out, count - out, " ");
170 for (l = 0, i = 0; i < 2; i++) {
171 out += snprintf(buf + out, count - out, " ");
172 for (j = 0; j < 8 && l < len; j++, l++) {
173 c = data[(i * 8 + j)];
174 if (!isascii(c) || !isprint(c))
175 c = '.';
Jeff Garzikbf794512005-07-31 13:07:26 -0400176
James Ketrenos43f66a62005-03-25 12:31:53 -0600177 out += snprintf(buf + out, count - out, "%c", c);
178 }
179
180 for (; j < 8; j++)
181 out += snprintf(buf + out, count - out, " ");
182 }
Jeff Garzikbf794512005-07-31 13:07:26 -0400183
James Ketrenosf6c5cb72005-08-25 00:39:09 -0500184 return out;
James Ketrenos43f66a62005-03-25 12:31:53 -0600185}
186
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400187static void printk_buf(int level, const u8 * data, u32 len)
James Ketrenos43f66a62005-03-25 12:31:53 -0600188{
189 char line[81];
190 u32 ofs = 0;
191 if (!(ipw_debug_level & level))
192 return;
193
194 while (len) {
James Ketrenosf6c5cb72005-08-25 00:39:09 -0500195 snprint_line(line, sizeof(line), &data[ofs],
196 min(len, 16U), ofs);
197 printk(KERN_DEBUG "%s\n", line);
James Ketrenos43f66a62005-03-25 12:31:53 -0600198 ofs += 16;
199 len -= min(len, 16U);
200 }
201}
202
James Ketrenosf6c5cb72005-08-25 00:39:09 -0500203static int snprintk_buf(u8 * output, size_t size, const u8 * data, size_t len)
204{
205 size_t out = size;
206 u32 ofs = 0;
207 int total = 0;
208
209 while (size && len) {
210 out = snprint_line(output, size, &data[ofs],
211 min_t(size_t, len, 16U), ofs);
212
213 ofs += 16;
214 output += out;
215 size -= out;
216 len -= min_t(size_t, len, 16U);
217 total += out;
218 }
219 return total;
220}
221
James Ketrenos43f66a62005-03-25 12:31:53 -0600222static u32 _ipw_read_reg32(struct ipw_priv *priv, u32 reg);
223#define ipw_read_reg32(a, b) _ipw_read_reg32(a, b)
224
225static u8 _ipw_read_reg8(struct ipw_priv *ipw, u32 reg);
226#define ipw_read_reg8(a, b) _ipw_read_reg8(a, b)
227
228static void _ipw_write_reg8(struct ipw_priv *priv, u32 reg, u8 value);
229static inline void ipw_write_reg8(struct ipw_priv *a, u32 b, u8 c)
230{
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400231 IPW_DEBUG_IO("%s %d: write_indirect8(0x%08X, 0x%08X)\n", __FILE__,
232 __LINE__, (u32) (b), (u32) (c));
James Ketrenos43f66a62005-03-25 12:31:53 -0600233 _ipw_write_reg8(a, b, c);
234}
235
236static void _ipw_write_reg16(struct ipw_priv *priv, u32 reg, u16 value);
237static inline void ipw_write_reg16(struct ipw_priv *a, u32 b, u16 c)
238{
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400239 IPW_DEBUG_IO("%s %d: write_indirect16(0x%08X, 0x%08X)\n", __FILE__,
240 __LINE__, (u32) (b), (u32) (c));
James Ketrenos43f66a62005-03-25 12:31:53 -0600241 _ipw_write_reg16(a, b, c);
242}
243
244static void _ipw_write_reg32(struct ipw_priv *priv, u32 reg, u32 value);
245static inline void ipw_write_reg32(struct ipw_priv *a, u32 b, u32 c)
246{
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400247 IPW_DEBUG_IO("%s %d: write_indirect32(0x%08X, 0x%08X)\n", __FILE__,
248 __LINE__, (u32) (b), (u32) (c));
James Ketrenos43f66a62005-03-25 12:31:53 -0600249 _ipw_write_reg32(a, b, c);
250}
251
252#define _ipw_write8(ipw, ofs, val) writeb((val), (ipw)->hw_base + (ofs))
253#define ipw_write8(ipw, ofs, val) \
254 IPW_DEBUG_IO("%s %d: write_direct8(0x%08X, 0x%08X)\n", __FILE__, __LINE__, (u32)(ofs), (u32)(val)); \
255 _ipw_write8(ipw, ofs, val)
256
257#define _ipw_write16(ipw, ofs, val) writew((val), (ipw)->hw_base + (ofs))
258#define ipw_write16(ipw, ofs, val) \
259 IPW_DEBUG_IO("%s %d: write_direct16(0x%08X, 0x%08X)\n", __FILE__, __LINE__, (u32)(ofs), (u32)(val)); \
260 _ipw_write16(ipw, ofs, val)
261
262#define _ipw_write32(ipw, ofs, val) writel((val), (ipw)->hw_base + (ofs))
263#define ipw_write32(ipw, ofs, val) \
264 IPW_DEBUG_IO("%s %d: write_direct32(0x%08X, 0x%08X)\n", __FILE__, __LINE__, (u32)(ofs), (u32)(val)); \
265 _ipw_write32(ipw, ofs, val)
266
267#define _ipw_read8(ipw, ofs) readb((ipw)->hw_base + (ofs))
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400268static inline u8 __ipw_read8(char *f, u32 l, struct ipw_priv *ipw, u32 ofs)
269{
270 IPW_DEBUG_IO("%s %d: read_direct8(0x%08X)\n", f, l, (u32) (ofs));
James Ketrenos43f66a62005-03-25 12:31:53 -0600271 return _ipw_read8(ipw, ofs);
272}
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400273
James Ketrenos43f66a62005-03-25 12:31:53 -0600274#define ipw_read8(ipw, ofs) __ipw_read8(__FILE__, __LINE__, ipw, ofs)
275
276#define _ipw_read16(ipw, ofs) readw((ipw)->hw_base + (ofs))
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400277static inline u16 __ipw_read16(char *f, u32 l, struct ipw_priv *ipw, u32 ofs)
278{
279 IPW_DEBUG_IO("%s %d: read_direct16(0x%08X)\n", f, l, (u32) (ofs));
James Ketrenos43f66a62005-03-25 12:31:53 -0600280 return _ipw_read16(ipw, ofs);
281}
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400282
James Ketrenos43f66a62005-03-25 12:31:53 -0600283#define ipw_read16(ipw, ofs) __ipw_read16(__FILE__, __LINE__, ipw, ofs)
284
285#define _ipw_read32(ipw, ofs) readl((ipw)->hw_base + (ofs))
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400286static inline u32 __ipw_read32(char *f, u32 l, struct ipw_priv *ipw, u32 ofs)
287{
288 IPW_DEBUG_IO("%s %d: read_direct32(0x%08X)\n", f, l, (u32) (ofs));
James Ketrenos43f66a62005-03-25 12:31:53 -0600289 return _ipw_read32(ipw, ofs);
290}
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400291
James Ketrenos43f66a62005-03-25 12:31:53 -0600292#define ipw_read32(ipw, ofs) __ipw_read32(__FILE__, __LINE__, ipw, ofs)
293
294static void _ipw_read_indirect(struct ipw_priv *, u32, u8 *, int);
James Ketrenosf6c5cb72005-08-25 00:39:09 -0500295static inline void __ipw_read_indirect(const char *f, int l,
296 struct ipw_priv *a, u32 b, u8 * c, int d)
297{
298 IPW_DEBUG_IO("%s %d: read_indirect(0x%08X) %d bytes\n", f, l, (u32) (b),
299 d);
300 _ipw_read_indirect(a, b, c, d);
301}
302
303#define ipw_read_indirect(a, b, c, d) __ipw_read_indirect(__FILE__, __LINE__, a, b, c, d)
James Ketrenos43f66a62005-03-25 12:31:53 -0600304
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400305static void _ipw_write_indirect(struct ipw_priv *priv, u32 addr, u8 * data,
306 int num);
James Ketrenos43f66a62005-03-25 12:31:53 -0600307#define ipw_write_indirect(a, b, c, d) \
308 IPW_DEBUG_IO("%s %d: write_indirect(0x%08X) %d bytes\n", __FILE__, __LINE__, (u32)(b), d); \
James Ketrenosafbf30a2005-08-25 00:05:33 -0500309 _ipw_write_indirect(a, b, c, d)
James Ketrenos43f66a62005-03-25 12:31:53 -0600310
311/* indirect write s */
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400312static void _ipw_write_reg32(struct ipw_priv *priv, u32 reg, u32 value)
James Ketrenos43f66a62005-03-25 12:31:53 -0600313{
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400314 IPW_DEBUG_IO(" %p : reg = 0x%8X : value = 0x%8X\n", priv, reg, value);
James Ketrenosb095c382005-08-24 22:04:42 -0500315 _ipw_write32(priv, IPW_INDIRECT_ADDR, reg);
316 _ipw_write32(priv, IPW_INDIRECT_DATA, value);
James Ketrenos43f66a62005-03-25 12:31:53 -0600317}
318
James Ketrenos43f66a62005-03-25 12:31:53 -0600319static void _ipw_write_reg8(struct ipw_priv *priv, u32 reg, u8 value)
320{
321 IPW_DEBUG_IO(" reg = 0x%8X : value = 0x%8X\n", reg, value);
James Ketrenosb095c382005-08-24 22:04:42 -0500322 _ipw_write32(priv, IPW_INDIRECT_ADDR, reg & IPW_INDIRECT_ADDR_MASK);
323 _ipw_write8(priv, IPW_INDIRECT_DATA, value);
James Ketrenos43f66a62005-03-25 12:31:53 -0600324}
325
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400326static void _ipw_write_reg16(struct ipw_priv *priv, u32 reg, u16 value)
James Ketrenos43f66a62005-03-25 12:31:53 -0600327{
328 IPW_DEBUG_IO(" reg = 0x%8X : value = 0x%8X\n", reg, value);
James Ketrenosb095c382005-08-24 22:04:42 -0500329 _ipw_write32(priv, IPW_INDIRECT_ADDR, reg & IPW_INDIRECT_ADDR_MASK);
330 _ipw_write16(priv, IPW_INDIRECT_DATA, value);
James Ketrenos43f66a62005-03-25 12:31:53 -0600331}
332
333/* indirect read s */
334
335static u8 _ipw_read_reg8(struct ipw_priv *priv, u32 reg)
336{
337 u32 word;
James Ketrenosb095c382005-08-24 22:04:42 -0500338 _ipw_write32(priv, IPW_INDIRECT_ADDR, reg & IPW_INDIRECT_ADDR_MASK);
James Ketrenos43f66a62005-03-25 12:31:53 -0600339 IPW_DEBUG_IO(" reg = 0x%8X : \n", reg);
James Ketrenosb095c382005-08-24 22:04:42 -0500340 word = _ipw_read32(priv, IPW_INDIRECT_DATA);
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400341 return (word >> ((reg & 0x3) * 8)) & 0xff;
James Ketrenos43f66a62005-03-25 12:31:53 -0600342}
343
344static u32 _ipw_read_reg32(struct ipw_priv *priv, u32 reg)
345{
346 u32 value;
347
348 IPW_DEBUG_IO("%p : reg = 0x%08x\n", priv, reg);
349
James Ketrenosb095c382005-08-24 22:04:42 -0500350 _ipw_write32(priv, IPW_INDIRECT_ADDR, reg);
351 value = _ipw_read32(priv, IPW_INDIRECT_DATA);
James Ketrenos43f66a62005-03-25 12:31:53 -0600352 IPW_DEBUG_IO(" reg = 0x%4X : value = 0x%4x \n", reg, value);
353 return value;
354}
355
356/* iterative/auto-increment 32 bit reads and writes */
357static void _ipw_read_indirect(struct ipw_priv *priv, u32 addr, u8 * buf,
358 int num)
359{
James Ketrenosb095c382005-08-24 22:04:42 -0500360 u32 aligned_addr = addr & IPW_INDIRECT_ADDR_MASK;
James Ketrenos43f66a62005-03-25 12:31:53 -0600361 u32 dif_len = addr - aligned_addr;
James Ketrenos43f66a62005-03-25 12:31:53 -0600362 u32 i;
Jeff Garzikbf794512005-07-31 13:07:26 -0400363
James Ketrenos43f66a62005-03-25 12:31:53 -0600364 IPW_DEBUG_IO("addr = %i, buf = %p, num = %i\n", addr, buf, num);
365
James Ketrenosea2b26e2005-08-24 21:25:16 -0500366 if (num <= 0) {
367 return;
368 }
369
James Ketrenos43f66a62005-03-25 12:31:53 -0600370 /* Read the first nibble byte by byte */
371 if (unlikely(dif_len)) {
James Ketrenosb095c382005-08-24 22:04:42 -0500372 _ipw_write32(priv, IPW_INDIRECT_ADDR, aligned_addr);
James Ketrenosea2b26e2005-08-24 21:25:16 -0500373 /* Start reading at aligned_addr + dif_len */
374 for (i = dif_len; ((i < 4) && (num > 0)); i++, num--)
James Ketrenosb095c382005-08-24 22:04:42 -0500375 *buf++ = _ipw_read8(priv, IPW_INDIRECT_DATA + i);
James Ketrenos43f66a62005-03-25 12:31:53 -0600376 aligned_addr += 4;
377 }
378
James Ketrenosb095c382005-08-24 22:04:42 -0500379 _ipw_write32(priv, IPW_AUTOINC_ADDR, aligned_addr);
James Ketrenosea2b26e2005-08-24 21:25:16 -0500380 for (; num >= 4; buf += 4, aligned_addr += 4, num -= 4)
James Ketrenosb095c382005-08-24 22:04:42 -0500381 *(u32 *) buf = _ipw_read32(priv, IPW_AUTOINC_DATA);
Jeff Garzikbf794512005-07-31 13:07:26 -0400382
James Ketrenos43f66a62005-03-25 12:31:53 -0600383 /* Copy the last nibble */
James Ketrenosea2b26e2005-08-24 21:25:16 -0500384 if (unlikely(num)) {
James Ketrenosb095c382005-08-24 22:04:42 -0500385 _ipw_write32(priv, IPW_INDIRECT_ADDR, aligned_addr);
James Ketrenosea2b26e2005-08-24 21:25:16 -0500386 for (i = 0; num > 0; i++, num--)
James Ketrenosb095c382005-08-24 22:04:42 -0500387 *buf++ = ipw_read8(priv, IPW_INDIRECT_DATA + i);
James Ketrenosea2b26e2005-08-24 21:25:16 -0500388 }
James Ketrenos43f66a62005-03-25 12:31:53 -0600389}
390
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400391static void _ipw_write_indirect(struct ipw_priv *priv, u32 addr, u8 * buf,
James Ketrenos43f66a62005-03-25 12:31:53 -0600392 int num)
393{
James Ketrenosb095c382005-08-24 22:04:42 -0500394 u32 aligned_addr = addr & IPW_INDIRECT_ADDR_MASK;
James Ketrenos43f66a62005-03-25 12:31:53 -0600395 u32 dif_len = addr - aligned_addr;
James Ketrenos43f66a62005-03-25 12:31:53 -0600396 u32 i;
Jeff Garzikbf794512005-07-31 13:07:26 -0400397
James Ketrenos43f66a62005-03-25 12:31:53 -0600398 IPW_DEBUG_IO("addr = %i, buf = %p, num = %i\n", addr, buf, num);
Jeff Garzikbf794512005-07-31 13:07:26 -0400399
James Ketrenosea2b26e2005-08-24 21:25:16 -0500400 if (num <= 0) {
401 return;
402 }
403
James Ketrenos43f66a62005-03-25 12:31:53 -0600404 /* Write the first nibble byte by byte */
405 if (unlikely(dif_len)) {
James Ketrenosb095c382005-08-24 22:04:42 -0500406 _ipw_write32(priv, IPW_INDIRECT_ADDR, aligned_addr);
James Ketrenosea2b26e2005-08-24 21:25:16 -0500407 /* Start reading at aligned_addr + dif_len */
408 for (i = dif_len; ((i < 4) && (num > 0)); i++, num--, buf++)
James Ketrenosb095c382005-08-24 22:04:42 -0500409 _ipw_write8(priv, IPW_INDIRECT_DATA + i, *buf);
James Ketrenos43f66a62005-03-25 12:31:53 -0600410 aligned_addr += 4;
411 }
Jeff Garzikbf794512005-07-31 13:07:26 -0400412
James Ketrenosb095c382005-08-24 22:04:42 -0500413 _ipw_write32(priv, IPW_AUTOINC_ADDR, aligned_addr);
James Ketrenosea2b26e2005-08-24 21:25:16 -0500414 for (; num >= 4; buf += 4, aligned_addr += 4, num -= 4)
James Ketrenosb095c382005-08-24 22:04:42 -0500415 _ipw_write32(priv, IPW_AUTOINC_DATA, *(u32 *) buf);
Jeff Garzikbf794512005-07-31 13:07:26 -0400416
James Ketrenos43f66a62005-03-25 12:31:53 -0600417 /* Copy the last nibble */
James Ketrenosea2b26e2005-08-24 21:25:16 -0500418 if (unlikely(num)) {
James Ketrenosb095c382005-08-24 22:04:42 -0500419 _ipw_write32(priv, IPW_INDIRECT_ADDR, aligned_addr);
James Ketrenosea2b26e2005-08-24 21:25:16 -0500420 for (i = 0; num > 0; i++, num--, buf++)
James Ketrenosb095c382005-08-24 22:04:42 -0500421 _ipw_write8(priv, IPW_INDIRECT_DATA + i, *buf);
James Ketrenosea2b26e2005-08-24 21:25:16 -0500422 }
James Ketrenos43f66a62005-03-25 12:31:53 -0600423}
424
Jeff Garzikbf794512005-07-31 13:07:26 -0400425static void ipw_write_direct(struct ipw_priv *priv, u32 addr, void *buf,
James Ketrenos43f66a62005-03-25 12:31:53 -0600426 int num)
427{
428 memcpy_toio((priv->hw_base + addr), buf, num);
429}
430
431static inline void ipw_set_bit(struct ipw_priv *priv, u32 reg, u32 mask)
432{
433 ipw_write32(priv, reg, ipw_read32(priv, reg) | mask);
434}
435
436static inline void ipw_clear_bit(struct ipw_priv *priv, u32 reg, u32 mask)
437{
438 ipw_write32(priv, reg, ipw_read32(priv, reg) & ~mask);
439}
440
441static inline void ipw_enable_interrupts(struct ipw_priv *priv)
442{
443 if (priv->status & STATUS_INT_ENABLED)
444 return;
445 priv->status |= STATUS_INT_ENABLED;
James Ketrenosb095c382005-08-24 22:04:42 -0500446 ipw_write32(priv, IPW_INTA_MASK_R, IPW_INTA_MASK_ALL);
James Ketrenos43f66a62005-03-25 12:31:53 -0600447}
448
449static inline void ipw_disable_interrupts(struct ipw_priv *priv)
450{
451 if (!(priv->status & STATUS_INT_ENABLED))
452 return;
453 priv->status &= ~STATUS_INT_ENABLED;
James Ketrenosb095c382005-08-24 22:04:42 -0500454 ipw_write32(priv, IPW_INTA_MASK_R, ~IPW_INTA_MASK_ALL);
James Ketrenos43f66a62005-03-25 12:31:53 -0600455}
456
James Ketrenosb39860c2005-08-12 09:36:32 -0500457#ifdef CONFIG_IPW_DEBUG
James Ketrenos43f66a62005-03-25 12:31:53 -0600458static char *ipw_error_desc(u32 val)
459{
460 switch (val) {
Jeff Garzikbf794512005-07-31 13:07:26 -0400461 case IPW_FW_ERROR_OK:
James Ketrenos43f66a62005-03-25 12:31:53 -0600462 return "ERROR_OK";
Jeff Garzikbf794512005-07-31 13:07:26 -0400463 case IPW_FW_ERROR_FAIL:
James Ketrenos43f66a62005-03-25 12:31:53 -0600464 return "ERROR_FAIL";
Jeff Garzikbf794512005-07-31 13:07:26 -0400465 case IPW_FW_ERROR_MEMORY_UNDERFLOW:
James Ketrenos43f66a62005-03-25 12:31:53 -0600466 return "MEMORY_UNDERFLOW";
Jeff Garzikbf794512005-07-31 13:07:26 -0400467 case IPW_FW_ERROR_MEMORY_OVERFLOW:
James Ketrenos43f66a62005-03-25 12:31:53 -0600468 return "MEMORY_OVERFLOW";
Jeff Garzikbf794512005-07-31 13:07:26 -0400469 case IPW_FW_ERROR_BAD_PARAM:
James Ketrenosb095c382005-08-24 22:04:42 -0500470 return "BAD_PARAM";
Jeff Garzikbf794512005-07-31 13:07:26 -0400471 case IPW_FW_ERROR_BAD_CHECKSUM:
James Ketrenosb095c382005-08-24 22:04:42 -0500472 return "BAD_CHECKSUM";
Jeff Garzikbf794512005-07-31 13:07:26 -0400473 case IPW_FW_ERROR_NMI_INTERRUPT:
James Ketrenosb095c382005-08-24 22:04:42 -0500474 return "NMI_INTERRUPT";
Jeff Garzikbf794512005-07-31 13:07:26 -0400475 case IPW_FW_ERROR_BAD_DATABASE:
James Ketrenosb095c382005-08-24 22:04:42 -0500476 return "BAD_DATABASE";
Jeff Garzikbf794512005-07-31 13:07:26 -0400477 case IPW_FW_ERROR_ALLOC_FAIL:
James Ketrenosb095c382005-08-24 22:04:42 -0500478 return "ALLOC_FAIL";
Jeff Garzikbf794512005-07-31 13:07:26 -0400479 case IPW_FW_ERROR_DMA_UNDERRUN:
James Ketrenosb095c382005-08-24 22:04:42 -0500480 return "DMA_UNDERRUN";
Jeff Garzikbf794512005-07-31 13:07:26 -0400481 case IPW_FW_ERROR_DMA_STATUS:
James Ketrenosb095c382005-08-24 22:04:42 -0500482 return "DMA_STATUS";
483 case IPW_FW_ERROR_DINO_ERROR:
484 return "DINO_ERROR";
485 case IPW_FW_ERROR_EEPROM_ERROR:
486 return "EEPROM_ERROR";
Jeff Garzikbf794512005-07-31 13:07:26 -0400487 case IPW_FW_ERROR_SYSASSERT:
James Ketrenosb095c382005-08-24 22:04:42 -0500488 return "SYSASSERT";
Jeff Garzikbf794512005-07-31 13:07:26 -0400489 case IPW_FW_ERROR_FATAL_ERROR:
James Ketrenosb095c382005-08-24 22:04:42 -0500490 return "FATAL_ERROR";
Jeff Garzikbf794512005-07-31 13:07:26 -0400491 default:
James Ketrenosb095c382005-08-24 22:04:42 -0500492 return "UNKNOWN_ERROR";
James Ketrenos43f66a62005-03-25 12:31:53 -0600493 }
494}
495
James Ketrenosb39860c2005-08-12 09:36:32 -0500496static void ipw_dump_error_log(struct ipw_priv *priv,
497 struct ipw_fw_error *error)
James Ketrenos43f66a62005-03-25 12:31:53 -0600498{
James Ketrenosb39860c2005-08-12 09:36:32 -0500499 u32 i;
James Ketrenos43f66a62005-03-25 12:31:53 -0600500
James Ketrenosb39860c2005-08-12 09:36:32 -0500501 if (!error) {
502 IPW_ERROR("Error allocating and capturing error log. "
503 "Nothing to dump.\n");
504 return;
James Ketrenos43f66a62005-03-25 12:31:53 -0600505 }
506
James Ketrenosb39860c2005-08-12 09:36:32 -0500507 IPW_ERROR("Start IPW Error Log Dump:\n");
508 IPW_ERROR("Status: 0x%08X, Config: %08X\n",
509 error->status, error->config);
James Ketrenos43f66a62005-03-25 12:31:53 -0600510
James Ketrenosb39860c2005-08-12 09:36:32 -0500511 for (i = 0; i < error->elem_len; i++)
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400512 IPW_ERROR("%s %i 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x\n",
James Ketrenosb39860c2005-08-12 09:36:32 -0500513 ipw_error_desc(error->elem[i].desc),
514 error->elem[i].time,
515 error->elem[i].blink1,
516 error->elem[i].blink2,
517 error->elem[i].link1,
518 error->elem[i].link2, error->elem[i].data);
519 for (i = 0; i < error->log_len; i++)
520 IPW_ERROR("%i\t0x%08x\t%i\n",
521 error->log[i].time,
522 error->log[i].event, error->log[i].data);
James Ketrenos43f66a62005-03-25 12:31:53 -0600523}
James Ketrenos43f66a62005-03-25 12:31:53 -0600524#endif
James Ketrenos43f66a62005-03-25 12:31:53 -0600525
James Ketrenosc848d0a2005-08-24 21:56:24 -0500526static inline int ipw_is_init(struct ipw_priv *priv)
527{
528 return (priv->status & STATUS_INIT) ? 1 : 0;
529}
530
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400531static int ipw_get_ordinal(struct ipw_priv *priv, u32 ord, void *val, u32 * len)
James Ketrenos43f66a62005-03-25 12:31:53 -0600532{
533 u32 addr, field_info, field_len, field_count, total_len;
534
535 IPW_DEBUG_ORD("ordinal = %i\n", ord);
536
537 if (!priv || !val || !len) {
538 IPW_DEBUG_ORD("Invalid argument\n");
539 return -EINVAL;
540 }
Jeff Garzikbf794512005-07-31 13:07:26 -0400541
James Ketrenos43f66a62005-03-25 12:31:53 -0600542 /* verify device ordinal tables have been initialized */
543 if (!priv->table0_addr || !priv->table1_addr || !priv->table2_addr) {
544 IPW_DEBUG_ORD("Access ordinals before initialization\n");
545 return -EINVAL;
546 }
547
548 switch (IPW_ORD_TABLE_ID_MASK & ord) {
549 case IPW_ORD_TABLE_0_MASK:
550 /*
551 * TABLE 0: Direct access to a table of 32 bit values
552 *
Jeff Garzikbf794512005-07-31 13:07:26 -0400553 * This is a very simple table with the data directly
James Ketrenos43f66a62005-03-25 12:31:53 -0600554 * read from the table
555 */
556
557 /* remove the table id from the ordinal */
558 ord &= IPW_ORD_TABLE_VALUE_MASK;
559
560 /* boundary check */
561 if (ord > priv->table0_len) {
562 IPW_DEBUG_ORD("ordinal value (%i) longer then "
563 "max (%i)\n", ord, priv->table0_len);
564 return -EINVAL;
565 }
566
567 /* verify we have enough room to store the value */
568 if (*len < sizeof(u32)) {
569 IPW_DEBUG_ORD("ordinal buffer length too small, "
Jiri Bencaaa4d302005-06-07 14:58:41 +0200570 "need %zd\n", sizeof(u32));
James Ketrenos43f66a62005-03-25 12:31:53 -0600571 return -EINVAL;
572 }
573
574 IPW_DEBUG_ORD("Reading TABLE0[%i] from offset 0x%08x\n",
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400575 ord, priv->table0_addr + (ord << 2));
James Ketrenos43f66a62005-03-25 12:31:53 -0600576
577 *len = sizeof(u32);
578 ord <<= 2;
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400579 *((u32 *) val) = ipw_read32(priv, priv->table0_addr + ord);
James Ketrenos43f66a62005-03-25 12:31:53 -0600580 break;
581
582 case IPW_ORD_TABLE_1_MASK:
583 /*
584 * TABLE 1: Indirect access to a table of 32 bit values
Jeff Garzikbf794512005-07-31 13:07:26 -0400585 *
586 * This is a fairly large table of u32 values each
James Ketrenos43f66a62005-03-25 12:31:53 -0600587 * representing starting addr for the data (which is
588 * also a u32)
589 */
590
591 /* remove the table id from the ordinal */
592 ord &= IPW_ORD_TABLE_VALUE_MASK;
Jeff Garzikbf794512005-07-31 13:07:26 -0400593
James Ketrenos43f66a62005-03-25 12:31:53 -0600594 /* boundary check */
595 if (ord > priv->table1_len) {
596 IPW_DEBUG_ORD("ordinal value too long\n");
597 return -EINVAL;
598 }
599
600 /* verify we have enough room to store the value */
601 if (*len < sizeof(u32)) {
602 IPW_DEBUG_ORD("ordinal buffer length too small, "
Jiri Bencaaa4d302005-06-07 14:58:41 +0200603 "need %zd\n", sizeof(u32));
James Ketrenos43f66a62005-03-25 12:31:53 -0600604 return -EINVAL;
605 }
606
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400607 *((u32 *) val) =
608 ipw_read_reg32(priv, (priv->table1_addr + (ord << 2)));
James Ketrenos43f66a62005-03-25 12:31:53 -0600609 *len = sizeof(u32);
610 break;
611
612 case IPW_ORD_TABLE_2_MASK:
613 /*
614 * TABLE 2: Indirect access to a table of variable sized values
615 *
616 * This table consist of six values, each containing
617 * - dword containing the starting offset of the data
618 * - dword containing the lengh in the first 16bits
619 * and the count in the second 16bits
620 */
621
622 /* remove the table id from the ordinal */
623 ord &= IPW_ORD_TABLE_VALUE_MASK;
624
625 /* boundary check */
626 if (ord > priv->table2_len) {
627 IPW_DEBUG_ORD("ordinal value too long\n");
628 return -EINVAL;
629 }
630
631 /* get the address of statistic */
632 addr = ipw_read_reg32(priv, priv->table2_addr + (ord << 3));
Jeff Garzikbf794512005-07-31 13:07:26 -0400633
634 /* get the second DW of statistics ;
James Ketrenos43f66a62005-03-25 12:31:53 -0600635 * two 16-bit words - first is length, second is count */
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400636 field_info =
637 ipw_read_reg32(priv,
638 priv->table2_addr + (ord << 3) +
639 sizeof(u32));
Jeff Garzikbf794512005-07-31 13:07:26 -0400640
James Ketrenos43f66a62005-03-25 12:31:53 -0600641 /* get each entry length */
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400642 field_len = *((u16 *) & field_info);
Jeff Garzikbf794512005-07-31 13:07:26 -0400643
James Ketrenos43f66a62005-03-25 12:31:53 -0600644 /* get number of entries */
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400645 field_count = *(((u16 *) & field_info) + 1);
Jeff Garzikbf794512005-07-31 13:07:26 -0400646
James Ketrenos43f66a62005-03-25 12:31:53 -0600647 /* abort if not enought memory */
648 total_len = field_len * field_count;
649 if (total_len > *len) {
650 *len = total_len;
651 return -EINVAL;
652 }
Jeff Garzikbf794512005-07-31 13:07:26 -0400653
James Ketrenos43f66a62005-03-25 12:31:53 -0600654 *len = total_len;
655 if (!total_len)
656 return 0;
657
658 IPW_DEBUG_ORD("addr = 0x%08x, total_len = %i, "
Jeff Garzikbf794512005-07-31 13:07:26 -0400659 "field_info = 0x%08x\n",
James Ketrenos43f66a62005-03-25 12:31:53 -0600660 addr, total_len, field_info);
661 ipw_read_indirect(priv, addr, val, total_len);
662 break;
663
664 default:
665 IPW_DEBUG_ORD("Invalid ordinal!\n");
666 return -EINVAL;
667
668 }
669
James Ketrenos43f66a62005-03-25 12:31:53 -0600670 return 0;
671}
672
673static void ipw_init_ordinals(struct ipw_priv *priv)
674{
675 priv->table0_addr = IPW_ORDINALS_TABLE_LOWER;
Jeff Garzikbf794512005-07-31 13:07:26 -0400676 priv->table0_len = ipw_read32(priv, priv->table0_addr);
James Ketrenos43f66a62005-03-25 12:31:53 -0600677
678 IPW_DEBUG_ORD("table 0 offset at 0x%08x, len = %i\n",
679 priv->table0_addr, priv->table0_len);
680
681 priv->table1_addr = ipw_read32(priv, IPW_ORDINALS_TABLE_1);
682 priv->table1_len = ipw_read_reg32(priv, priv->table1_addr);
683
684 IPW_DEBUG_ORD("table 1 offset at 0x%08x, len = %i\n",
685 priv->table1_addr, priv->table1_len);
686
687 priv->table2_addr = ipw_read32(priv, IPW_ORDINALS_TABLE_2);
688 priv->table2_len = ipw_read_reg32(priv, priv->table2_addr);
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400689 priv->table2_len &= 0x0000ffff; /* use first two bytes */
James Ketrenos43f66a62005-03-25 12:31:53 -0600690
691 IPW_DEBUG_ORD("table 2 offset at 0x%08x, len = %i\n",
692 priv->table2_addr, priv->table2_len);
693
694}
695
James Ketrenosa613bff2005-08-24 21:43:11 -0500696u32 ipw_register_toggle(u32 reg)
697{
James Ketrenosb095c382005-08-24 22:04:42 -0500698 reg &= ~IPW_START_STANDBY;
699 if (reg & IPW_GATE_ODMA)
700 reg &= ~IPW_GATE_ODMA;
701 if (reg & IPW_GATE_IDMA)
702 reg &= ~IPW_GATE_IDMA;
703 if (reg & IPW_GATE_ADMA)
704 reg &= ~IPW_GATE_ADMA;
James Ketrenosa613bff2005-08-24 21:43:11 -0500705 return reg;
706}
707
708/*
709 * LED behavior:
710 * - On radio ON, turn on any LEDs that require to be on during start
711 * - On initialization, start unassociated blink
712 * - On association, disable unassociated blink
713 * - On disassociation, start unassociated blink
714 * - On radio OFF, turn off any LEDs started during radio on
715 *
716 */
717#define LD_TIME_LINK_ON 300
718#define LD_TIME_LINK_OFF 2700
719#define LD_TIME_ACT_ON 250
720
721void ipw_led_link_on(struct ipw_priv *priv)
722{
723 unsigned long flags;
724 u32 led;
725
726 /* If configured to not use LEDs, or nic_type is 1,
727 * then we don't toggle a LINK led */
728 if (priv->config & CFG_NO_LED || priv->nic_type == EEPROM_NIC_TYPE_1)
729 return;
730
731 spin_lock_irqsave(&priv->lock, flags);
732
733 if (!(priv->status & STATUS_RF_KILL_MASK) &&
734 !(priv->status & STATUS_LED_LINK_ON)) {
735 IPW_DEBUG_LED("Link LED On\n");
James Ketrenosb095c382005-08-24 22:04:42 -0500736 led = ipw_read_reg32(priv, IPW_EVENT_REG);
James Ketrenosa613bff2005-08-24 21:43:11 -0500737 led |= priv->led_association_on;
738
739 led = ipw_register_toggle(led);
740
741 IPW_DEBUG_LED("Reg: 0x%08X\n", led);
James Ketrenosb095c382005-08-24 22:04:42 -0500742 ipw_write_reg32(priv, IPW_EVENT_REG, led);
James Ketrenosa613bff2005-08-24 21:43:11 -0500743
744 priv->status |= STATUS_LED_LINK_ON;
745
746 /* If we aren't associated, schedule turning the LED off */
747 if (!(priv->status & STATUS_ASSOCIATED))
748 queue_delayed_work(priv->workqueue,
749 &priv->led_link_off,
750 LD_TIME_LINK_ON);
751 }
752
753 spin_unlock_irqrestore(&priv->lock, flags);
754}
755
James Ketrenosc848d0a2005-08-24 21:56:24 -0500756static void ipw_bg_led_link_on(void *data)
757{
758 struct ipw_priv *priv = data;
759 down(&priv->sem);
760 ipw_led_link_on(data);
761 up(&priv->sem);
762}
763
James Ketrenosa613bff2005-08-24 21:43:11 -0500764void ipw_led_link_off(struct ipw_priv *priv)
765{
766 unsigned long flags;
767 u32 led;
768
769 /* If configured not to use LEDs, or nic type is 1,
770 * then we don't goggle the LINK led. */
771 if (priv->config & CFG_NO_LED || priv->nic_type == EEPROM_NIC_TYPE_1)
772 return;
773
774 spin_lock_irqsave(&priv->lock, flags);
775
776 if (priv->status & STATUS_LED_LINK_ON) {
James Ketrenosb095c382005-08-24 22:04:42 -0500777 led = ipw_read_reg32(priv, IPW_EVENT_REG);
James Ketrenosa613bff2005-08-24 21:43:11 -0500778 led &= priv->led_association_off;
779 led = ipw_register_toggle(led);
780
781 IPW_DEBUG_LED("Reg: 0x%08X\n", led);
James Ketrenosb095c382005-08-24 22:04:42 -0500782 ipw_write_reg32(priv, IPW_EVENT_REG, led);
James Ketrenosa613bff2005-08-24 21:43:11 -0500783
784 IPW_DEBUG_LED("Link LED Off\n");
785
786 priv->status &= ~STATUS_LED_LINK_ON;
787
788 /* If we aren't associated and the radio is on, schedule
789 * turning the LED on (blink while unassociated) */
790 if (!(priv->status & STATUS_RF_KILL_MASK) &&
791 !(priv->status & STATUS_ASSOCIATED))
792 queue_delayed_work(priv->workqueue, &priv->led_link_on,
793 LD_TIME_LINK_OFF);
794
795 }
796
797 spin_unlock_irqrestore(&priv->lock, flags);
798}
799
James Ketrenosc848d0a2005-08-24 21:56:24 -0500800static void ipw_bg_led_link_off(void *data)
801{
802 struct ipw_priv *priv = data;
803 down(&priv->sem);
804 ipw_led_link_off(data);
805 up(&priv->sem);
806}
807
James Ketrenosb095c382005-08-24 22:04:42 -0500808static inline void __ipw_led_activity_on(struct ipw_priv *priv)
James Ketrenosa613bff2005-08-24 21:43:11 -0500809{
James Ketrenosa613bff2005-08-24 21:43:11 -0500810 u32 led;
811
812 if (priv->config & CFG_NO_LED)
813 return;
814
James Ketrenosb095c382005-08-24 22:04:42 -0500815 if (priv->status & STATUS_RF_KILL_MASK)
James Ketrenosa613bff2005-08-24 21:43:11 -0500816 return;
James Ketrenosa613bff2005-08-24 21:43:11 -0500817
818 if (!(priv->status & STATUS_LED_ACT_ON)) {
James Ketrenosb095c382005-08-24 22:04:42 -0500819 led = ipw_read_reg32(priv, IPW_EVENT_REG);
James Ketrenosa613bff2005-08-24 21:43:11 -0500820 led |= priv->led_activity_on;
821
822 led = ipw_register_toggle(led);
823
824 IPW_DEBUG_LED("Reg: 0x%08X\n", led);
James Ketrenosb095c382005-08-24 22:04:42 -0500825 ipw_write_reg32(priv, IPW_EVENT_REG, led);
James Ketrenosa613bff2005-08-24 21:43:11 -0500826
827 IPW_DEBUG_LED("Activity LED On\n");
828
829 priv->status |= STATUS_LED_ACT_ON;
830
James Ketrenosc848d0a2005-08-24 21:56:24 -0500831 cancel_delayed_work(&priv->led_act_off);
James Ketrenosa613bff2005-08-24 21:43:11 -0500832 queue_delayed_work(priv->workqueue, &priv->led_act_off,
833 LD_TIME_ACT_ON);
834 } else {
835 /* Reschedule LED off for full time period */
836 cancel_delayed_work(&priv->led_act_off);
837 queue_delayed_work(priv->workqueue, &priv->led_act_off,
838 LD_TIME_ACT_ON);
839 }
James Ketrenosb095c382005-08-24 22:04:42 -0500840}
James Ketrenosa613bff2005-08-24 21:43:11 -0500841
James Ketrenosb095c382005-08-24 22:04:42 -0500842void ipw_led_activity_on(struct ipw_priv *priv)
843{
844 unsigned long flags;
845 spin_lock_irqsave(&priv->lock, flags);
846 __ipw_led_activity_on(priv);
James Ketrenosa613bff2005-08-24 21:43:11 -0500847 spin_unlock_irqrestore(&priv->lock, flags);
848}
849
850void ipw_led_activity_off(struct ipw_priv *priv)
851{
852 unsigned long flags;
853 u32 led;
854
855 if (priv->config & CFG_NO_LED)
856 return;
857
858 spin_lock_irqsave(&priv->lock, flags);
859
860 if (priv->status & STATUS_LED_ACT_ON) {
James Ketrenosb095c382005-08-24 22:04:42 -0500861 led = ipw_read_reg32(priv, IPW_EVENT_REG);
James Ketrenosa613bff2005-08-24 21:43:11 -0500862 led &= priv->led_activity_off;
863
864 led = ipw_register_toggle(led);
865
866 IPW_DEBUG_LED("Reg: 0x%08X\n", led);
James Ketrenosb095c382005-08-24 22:04:42 -0500867 ipw_write_reg32(priv, IPW_EVENT_REG, led);
James Ketrenosa613bff2005-08-24 21:43:11 -0500868
869 IPW_DEBUG_LED("Activity LED Off\n");
870
871 priv->status &= ~STATUS_LED_ACT_ON;
872 }
873
874 spin_unlock_irqrestore(&priv->lock, flags);
875}
876
James Ketrenosc848d0a2005-08-24 21:56:24 -0500877static void ipw_bg_led_activity_off(void *data)
878{
879 struct ipw_priv *priv = data;
880 down(&priv->sem);
881 ipw_led_activity_off(data);
882 up(&priv->sem);
883}
884
James Ketrenosa613bff2005-08-24 21:43:11 -0500885void ipw_led_band_on(struct ipw_priv *priv)
886{
887 unsigned long flags;
888 u32 led;
889
890 /* Only nic type 1 supports mode LEDs */
James Ketrenosc848d0a2005-08-24 21:56:24 -0500891 if (priv->config & CFG_NO_LED ||
892 priv->nic_type != EEPROM_NIC_TYPE_1 || !priv->assoc_network)
James Ketrenosa613bff2005-08-24 21:43:11 -0500893 return;
894
895 spin_lock_irqsave(&priv->lock, flags);
896
James Ketrenosb095c382005-08-24 22:04:42 -0500897 led = ipw_read_reg32(priv, IPW_EVENT_REG);
James Ketrenosa613bff2005-08-24 21:43:11 -0500898 if (priv->assoc_network->mode == IEEE_A) {
899 led |= priv->led_ofdm_on;
900 led &= priv->led_association_off;
901 IPW_DEBUG_LED("Mode LED On: 802.11a\n");
902 } else if (priv->assoc_network->mode == IEEE_G) {
903 led |= priv->led_ofdm_on;
904 led |= priv->led_association_on;
905 IPW_DEBUG_LED("Mode LED On: 802.11g\n");
906 } else {
907 led &= priv->led_ofdm_off;
908 led |= priv->led_association_on;
909 IPW_DEBUG_LED("Mode LED On: 802.11b\n");
910 }
911
912 led = ipw_register_toggle(led);
913
914 IPW_DEBUG_LED("Reg: 0x%08X\n", led);
James Ketrenosb095c382005-08-24 22:04:42 -0500915 ipw_write_reg32(priv, IPW_EVENT_REG, led);
James Ketrenosa613bff2005-08-24 21:43:11 -0500916
917 spin_unlock_irqrestore(&priv->lock, flags);
918}
919
920void ipw_led_band_off(struct ipw_priv *priv)
921{
922 unsigned long flags;
923 u32 led;
924
925 /* Only nic type 1 supports mode LEDs */
926 if (priv->config & CFG_NO_LED || priv->nic_type != EEPROM_NIC_TYPE_1)
927 return;
928
929 spin_lock_irqsave(&priv->lock, flags);
930
James Ketrenosb095c382005-08-24 22:04:42 -0500931 led = ipw_read_reg32(priv, IPW_EVENT_REG);
James Ketrenosa613bff2005-08-24 21:43:11 -0500932 led &= priv->led_ofdm_off;
933 led &= priv->led_association_off;
934
935 led = ipw_register_toggle(led);
936
937 IPW_DEBUG_LED("Reg: 0x%08X\n", led);
James Ketrenosb095c382005-08-24 22:04:42 -0500938 ipw_write_reg32(priv, IPW_EVENT_REG, led);
James Ketrenosa613bff2005-08-24 21:43:11 -0500939
940 spin_unlock_irqrestore(&priv->lock, flags);
941}
942
943void ipw_led_radio_on(struct ipw_priv *priv)
944{
945 ipw_led_link_on(priv);
946}
947
948void ipw_led_radio_off(struct ipw_priv *priv)
949{
950 ipw_led_activity_off(priv);
951 ipw_led_link_off(priv);
952}
953
954void ipw_led_link_up(struct ipw_priv *priv)
955{
956 /* Set the Link Led on for all nic types */
957 ipw_led_link_on(priv);
958}
959
960void ipw_led_link_down(struct ipw_priv *priv)
961{
962 ipw_led_activity_off(priv);
963 ipw_led_link_off(priv);
964
965 if (priv->status & STATUS_RF_KILL_MASK)
966 ipw_led_radio_off(priv);
967}
968
969void ipw_led_init(struct ipw_priv *priv)
970{
971 priv->nic_type = priv->eeprom[EEPROM_NIC_TYPE];
972
973 /* Set the default PINs for the link and activity leds */
James Ketrenosb095c382005-08-24 22:04:42 -0500974 priv->led_activity_on = IPW_ACTIVITY_LED;
975 priv->led_activity_off = ~(IPW_ACTIVITY_LED);
James Ketrenosa613bff2005-08-24 21:43:11 -0500976
James Ketrenosb095c382005-08-24 22:04:42 -0500977 priv->led_association_on = IPW_ASSOCIATED_LED;
978 priv->led_association_off = ~(IPW_ASSOCIATED_LED);
James Ketrenosa613bff2005-08-24 21:43:11 -0500979
980 /* Set the default PINs for the OFDM leds */
James Ketrenosb095c382005-08-24 22:04:42 -0500981 priv->led_ofdm_on = IPW_OFDM_LED;
982 priv->led_ofdm_off = ~(IPW_OFDM_LED);
James Ketrenosa613bff2005-08-24 21:43:11 -0500983
984 switch (priv->nic_type) {
985 case EEPROM_NIC_TYPE_1:
986 /* In this NIC type, the LEDs are reversed.... */
James Ketrenosb095c382005-08-24 22:04:42 -0500987 priv->led_activity_on = IPW_ASSOCIATED_LED;
988 priv->led_activity_off = ~(IPW_ASSOCIATED_LED);
989 priv->led_association_on = IPW_ACTIVITY_LED;
990 priv->led_association_off = ~(IPW_ACTIVITY_LED);
James Ketrenosa613bff2005-08-24 21:43:11 -0500991
992 if (!(priv->config & CFG_NO_LED))
993 ipw_led_band_on(priv);
994
995 /* And we don't blink link LEDs for this nic, so
996 * just return here */
997 return;
998
999 case EEPROM_NIC_TYPE_3:
1000 case EEPROM_NIC_TYPE_2:
1001 case EEPROM_NIC_TYPE_4:
1002 case EEPROM_NIC_TYPE_0:
1003 break;
1004
1005 default:
1006 IPW_DEBUG_INFO("Unknown NIC type from EEPROM: %d\n",
1007 priv->nic_type);
1008 priv->nic_type = EEPROM_NIC_TYPE_0;
1009 break;
1010 }
1011
1012 if (!(priv->config & CFG_NO_LED)) {
1013 if (priv->status & STATUS_ASSOCIATED)
1014 ipw_led_link_on(priv);
1015 else
1016 ipw_led_link_off(priv);
1017 }
1018}
1019
1020void ipw_led_shutdown(struct ipw_priv *priv)
1021{
James Ketrenosa613bff2005-08-24 21:43:11 -05001022 ipw_led_activity_off(priv);
1023 ipw_led_link_off(priv);
1024 ipw_led_band_off(priv);
James Ketrenosafbf30a2005-08-25 00:05:33 -05001025 cancel_delayed_work(&priv->led_link_on);
1026 cancel_delayed_work(&priv->led_link_off);
1027 cancel_delayed_work(&priv->led_act_off);
James Ketrenosa613bff2005-08-24 21:43:11 -05001028}
1029
James Ketrenos43f66a62005-03-25 12:31:53 -06001030/*
1031 * The following adds a new attribute to the sysfs representation
1032 * of this device driver (i.e. a new file in /sys/bus/pci/drivers/ipw/)
1033 * used for controling the debug level.
Jeff Garzikbf794512005-07-31 13:07:26 -04001034 *
James Ketrenos43f66a62005-03-25 12:31:53 -06001035 * See the level definitions in ipw for details.
1036 */
1037static ssize_t show_debug_level(struct device_driver *d, char *buf)
1038{
1039 return sprintf(buf, "0x%08X\n", ipw_debug_level);
1040}
James Ketrenosa613bff2005-08-24 21:43:11 -05001041
1042static ssize_t store_debug_level(struct device_driver *d, const char *buf,
1043 size_t count)
James Ketrenos43f66a62005-03-25 12:31:53 -06001044{
1045 char *p = (char *)buf;
1046 u32 val;
1047
1048 if (p[1] == 'x' || p[1] == 'X' || p[0] == 'x' || p[0] == 'X') {
1049 p++;
1050 if (p[0] == 'x' || p[0] == 'X')
1051 p++;
1052 val = simple_strtoul(p, &p, 16);
1053 } else
1054 val = simple_strtoul(p, &p, 10);
Jeff Garzikbf794512005-07-31 13:07:26 -04001055 if (p == buf)
1056 printk(KERN_INFO DRV_NAME
James Ketrenos43f66a62005-03-25 12:31:53 -06001057 ": %s is not in hex or decimal form.\n", buf);
1058 else
1059 ipw_debug_level = val;
1060
1061 return strnlen(buf, count);
1062}
1063
Jeff Garzikbf794512005-07-31 13:07:26 -04001064static DRIVER_ATTR(debug_level, S_IWUSR | S_IRUGO,
James Ketrenos43f66a62005-03-25 12:31:53 -06001065 show_debug_level, store_debug_level);
1066
James Ketrenosb39860c2005-08-12 09:36:32 -05001067static inline u32 ipw_get_event_log_len(struct ipw_priv *priv)
1068{
1069 return ipw_read_reg32(priv, ipw_read32(priv, IPW_EVENT_LOG));
1070}
1071
1072static void ipw_capture_event_log(struct ipw_priv *priv,
1073 u32 log_len, struct ipw_event *log)
1074{
1075 u32 base;
1076
1077 if (log_len) {
1078 base = ipw_read32(priv, IPW_EVENT_LOG);
1079 ipw_read_indirect(priv, base + sizeof(base) + sizeof(u32),
1080 (u8 *) log, sizeof(*log) * log_len);
1081 }
1082}
1083
1084static struct ipw_fw_error *ipw_alloc_error_log(struct ipw_priv *priv)
1085{
1086 struct ipw_fw_error *error;
1087 u32 log_len = ipw_get_event_log_len(priv);
1088 u32 base = ipw_read32(priv, IPW_ERROR_LOG);
1089 u32 elem_len = ipw_read_reg32(priv, base);
1090
1091 error = kmalloc(sizeof(*error) +
1092 sizeof(*error->elem) * elem_len +
1093 sizeof(*error->log) * log_len, GFP_ATOMIC);
1094 if (!error) {
1095 IPW_ERROR("Memory allocation for firmware error log "
1096 "failed.\n");
1097 return NULL;
1098 }
James Ketrenosf6c5cb72005-08-25 00:39:09 -05001099 error->jiffies = jiffies;
James Ketrenosb39860c2005-08-12 09:36:32 -05001100 error->status = priv->status;
1101 error->config = priv->config;
1102 error->elem_len = elem_len;
1103 error->log_len = log_len;
1104 error->elem = (struct ipw_error_elem *)error->payload;
1105 error->log = (struct ipw_event *)(error->elem +
1106 (sizeof(*error->elem) * elem_len));
1107
1108 ipw_capture_event_log(priv, log_len, error->log);
1109
1110 if (elem_len)
1111 ipw_read_indirect(priv, base + sizeof(base), (u8 *) error->elem,
1112 sizeof(*error->elem) * elem_len);
1113
1114 return error;
1115}
1116
1117static void ipw_free_error_log(struct ipw_fw_error *error)
1118{
1119 if (error)
1120 kfree(error);
1121}
1122
1123static ssize_t show_event_log(struct device *d,
1124 struct device_attribute *attr, char *buf)
1125{
1126 struct ipw_priv *priv = dev_get_drvdata(d);
1127 u32 log_len = ipw_get_event_log_len(priv);
1128 struct ipw_event log[log_len];
1129 u32 len = 0, i;
1130
1131 ipw_capture_event_log(priv, log_len, log);
1132
1133 len += snprintf(buf + len, PAGE_SIZE - len, "%08X", log_len);
1134 for (i = 0; i < log_len; i++)
1135 len += snprintf(buf + len, PAGE_SIZE - len,
1136 "\n%08X%08X%08X",
1137 log[i].time, log[i].event, log[i].data);
1138 len += snprintf(buf + len, PAGE_SIZE - len, "\n");
1139 return len;
1140}
1141
1142static DEVICE_ATTR(event_log, S_IRUGO, show_event_log, NULL);
1143
1144static ssize_t show_error(struct device *d,
1145 struct device_attribute *attr, char *buf)
1146{
1147 struct ipw_priv *priv = dev_get_drvdata(d);
1148 u32 len = 0, i;
1149 if (!priv->error)
1150 return 0;
1151 len += snprintf(buf + len, PAGE_SIZE - len,
James Ketrenosf6c5cb72005-08-25 00:39:09 -05001152 "%08lX%08X%08X%08X",
1153 priv->error->jiffies,
James Ketrenosb39860c2005-08-12 09:36:32 -05001154 priv->error->status,
1155 priv->error->config, priv->error->elem_len);
1156 for (i = 0; i < priv->error->elem_len; i++)
1157 len += snprintf(buf + len, PAGE_SIZE - len,
1158 "\n%08X%08X%08X%08X%08X%08X%08X",
1159 priv->error->elem[i].time,
1160 priv->error->elem[i].desc,
1161 priv->error->elem[i].blink1,
1162 priv->error->elem[i].blink2,
1163 priv->error->elem[i].link1,
1164 priv->error->elem[i].link2,
1165 priv->error->elem[i].data);
1166
1167 len += snprintf(buf + len, PAGE_SIZE - len,
1168 "\n%08X", priv->error->log_len);
1169 for (i = 0; i < priv->error->log_len; i++)
1170 len += snprintf(buf + len, PAGE_SIZE - len,
1171 "\n%08X%08X%08X",
1172 priv->error->log[i].time,
1173 priv->error->log[i].event,
1174 priv->error->log[i].data);
1175 len += snprintf(buf + len, PAGE_SIZE - len, "\n");
1176 return len;
1177}
1178
1179static ssize_t clear_error(struct device *d,
1180 struct device_attribute *attr,
1181 const char *buf, size_t count)
1182{
1183 struct ipw_priv *priv = dev_get_drvdata(d);
1184 if (priv->error) {
1185 ipw_free_error_log(priv->error);
1186 priv->error = NULL;
1187 }
1188 return count;
1189}
1190
1191static DEVICE_ATTR(error, S_IRUGO | S_IWUSR, show_error, clear_error);
1192
James Ketrenosf6c5cb72005-08-25 00:39:09 -05001193static ssize_t show_cmd_log(struct device *d,
1194 struct device_attribute *attr, char *buf)
1195{
1196 struct ipw_priv *priv = dev_get_drvdata(d);
1197 u32 len = 0, i;
1198 if (!priv->cmdlog)
1199 return 0;
1200 for (i = (priv->cmdlog_pos + 1) % priv->cmdlog_len;
1201 (i != priv->cmdlog_pos) && (PAGE_SIZE - len);
1202 i = (i + 1) % priv->cmdlog_len) {
1203 len +=
1204 snprintf(buf + len, PAGE_SIZE - len,
1205 "\n%08lX%08X%08X%08X\n", priv->cmdlog[i].jiffies,
1206 priv->cmdlog[i].retcode, priv->cmdlog[i].cmd.cmd,
1207 priv->cmdlog[i].cmd.len);
1208 len +=
1209 snprintk_buf(buf + len, PAGE_SIZE - len,
1210 (u8 *) priv->cmdlog[i].cmd.param,
1211 priv->cmdlog[i].cmd.len);
1212 len += snprintf(buf + len, PAGE_SIZE - len, "\n");
1213 }
1214 len += snprintf(buf + len, PAGE_SIZE - len, "\n");
1215 return len;
1216}
1217
1218static DEVICE_ATTR(cmd_log, S_IRUGO, show_cmd_log, NULL);
1219
James Ketrenosa613bff2005-08-24 21:43:11 -05001220static ssize_t show_scan_age(struct device *d, struct device_attribute *attr,
1221 char *buf)
1222{
1223 struct ipw_priv *priv = dev_get_drvdata(d);
1224 return sprintf(buf, "%d\n", priv->ieee->scan_age);
1225}
1226
1227static ssize_t store_scan_age(struct device *d, struct device_attribute *attr,
1228 const char *buf, size_t count)
1229{
1230 struct ipw_priv *priv = dev_get_drvdata(d);
James Ketrenosc848d0a2005-08-24 21:56:24 -05001231#ifdef CONFIG_IPW_DEBUG
James Ketrenosa613bff2005-08-24 21:43:11 -05001232 struct net_device *dev = priv->net_dev;
James Ketrenosc848d0a2005-08-24 21:56:24 -05001233#endif
James Ketrenosa613bff2005-08-24 21:43:11 -05001234 char buffer[] = "00000000";
1235 unsigned long len =
1236 (sizeof(buffer) - 1) > count ? count : sizeof(buffer) - 1;
1237 unsigned long val;
1238 char *p = buffer;
1239
1240 IPW_DEBUG_INFO("enter\n");
1241
1242 strncpy(buffer, buf, len);
1243 buffer[len] = 0;
1244
1245 if (p[1] == 'x' || p[1] == 'X' || p[0] == 'x' || p[0] == 'X') {
1246 p++;
1247 if (p[0] == 'x' || p[0] == 'X')
1248 p++;
1249 val = simple_strtoul(p, &p, 16);
1250 } else
1251 val = simple_strtoul(p, &p, 10);
1252 if (p == buffer) {
1253 IPW_DEBUG_INFO("%s: user supplied invalid value.\n", dev->name);
1254 } else {
1255 priv->ieee->scan_age = val;
1256 IPW_DEBUG_INFO("set scan_age = %u\n", priv->ieee->scan_age);
1257 }
1258
1259 IPW_DEBUG_INFO("exit\n");
1260 return len;
1261}
1262
1263static DEVICE_ATTR(scan_age, S_IWUSR | S_IRUGO, show_scan_age, store_scan_age);
1264
1265static ssize_t show_led(struct device *d, struct device_attribute *attr,
1266 char *buf)
1267{
1268 struct ipw_priv *priv = dev_get_drvdata(d);
1269 return sprintf(buf, "%d\n", (priv->config & CFG_NO_LED) ? 0 : 1);
1270}
1271
1272static ssize_t store_led(struct device *d, struct device_attribute *attr,
1273 const char *buf, size_t count)
1274{
1275 struct ipw_priv *priv = dev_get_drvdata(d);
1276
1277 IPW_DEBUG_INFO("enter\n");
1278
1279 if (count == 0)
1280 return 0;
1281
1282 if (*buf == 0) {
1283 IPW_DEBUG_LED("Disabling LED control.\n");
1284 priv->config |= CFG_NO_LED;
1285 ipw_led_shutdown(priv);
1286 } else {
1287 IPW_DEBUG_LED("Enabling LED control.\n");
1288 priv->config &= ~CFG_NO_LED;
1289 ipw_led_init(priv);
1290 }
1291
1292 IPW_DEBUG_INFO("exit\n");
1293 return count;
1294}
1295
1296static DEVICE_ATTR(led, S_IWUSR | S_IRUGO, show_led, store_led);
1297
Andrew Mortonad3fee52005-06-20 14:30:36 -07001298static ssize_t show_status(struct device *d,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001299 struct device_attribute *attr, char *buf)
James Ketrenos43f66a62005-03-25 12:31:53 -06001300{
Andrew Mortonad3fee52005-06-20 14:30:36 -07001301 struct ipw_priv *p = d->driver_data;
James Ketrenos43f66a62005-03-25 12:31:53 -06001302 return sprintf(buf, "0x%08x\n", (int)p->status);
1303}
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001304
James Ketrenos43f66a62005-03-25 12:31:53 -06001305static DEVICE_ATTR(status, S_IRUGO, show_status, NULL);
1306
Andrew Mortonad3fee52005-06-20 14:30:36 -07001307static ssize_t show_cfg(struct device *d, struct device_attribute *attr,
1308 char *buf)
James Ketrenos43f66a62005-03-25 12:31:53 -06001309{
Andrew Mortonad3fee52005-06-20 14:30:36 -07001310 struct ipw_priv *p = d->driver_data;
James Ketrenos43f66a62005-03-25 12:31:53 -06001311 return sprintf(buf, "0x%08x\n", (int)p->config);
1312}
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001313
James Ketrenos43f66a62005-03-25 12:31:53 -06001314static DEVICE_ATTR(cfg, S_IRUGO, show_cfg, NULL);
1315
Andrew Mortonad3fee52005-06-20 14:30:36 -07001316static ssize_t show_nic_type(struct device *d,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001317 struct device_attribute *attr, char *buf)
James Ketrenos43f66a62005-03-25 12:31:53 -06001318{
James Ketrenosa613bff2005-08-24 21:43:11 -05001319 struct ipw_priv *priv = d->driver_data;
1320 return sprintf(buf, "TYPE: %d\n", priv->nic_type);
James Ketrenos43f66a62005-03-25 12:31:53 -06001321}
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001322
James Ketrenos43f66a62005-03-25 12:31:53 -06001323static DEVICE_ATTR(nic_type, S_IRUGO, show_nic_type, NULL);
1324
Andrew Mortonad3fee52005-06-20 14:30:36 -07001325static ssize_t show_ucode_version(struct device *d,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001326 struct device_attribute *attr, char *buf)
James Ketrenos43f66a62005-03-25 12:31:53 -06001327{
1328 u32 len = sizeof(u32), tmp = 0;
Andrew Mortonad3fee52005-06-20 14:30:36 -07001329 struct ipw_priv *p = d->driver_data;
James Ketrenos43f66a62005-03-25 12:31:53 -06001330
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001331 if (ipw_get_ordinal(p, IPW_ORD_STAT_UCODE_VERSION, &tmp, &len))
James Ketrenos43f66a62005-03-25 12:31:53 -06001332 return 0;
1333
1334 return sprintf(buf, "0x%08x\n", tmp);
1335}
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001336
1337static DEVICE_ATTR(ucode_version, S_IWUSR | S_IRUGO, show_ucode_version, NULL);
James Ketrenos43f66a62005-03-25 12:31:53 -06001338
Andrew Mortonad3fee52005-06-20 14:30:36 -07001339static ssize_t show_rtc(struct device *d, struct device_attribute *attr,
1340 char *buf)
James Ketrenos43f66a62005-03-25 12:31:53 -06001341{
1342 u32 len = sizeof(u32), tmp = 0;
Andrew Mortonad3fee52005-06-20 14:30:36 -07001343 struct ipw_priv *p = d->driver_data;
James Ketrenos43f66a62005-03-25 12:31:53 -06001344
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001345 if (ipw_get_ordinal(p, IPW_ORD_STAT_RTC, &tmp, &len))
James Ketrenos43f66a62005-03-25 12:31:53 -06001346 return 0;
1347
1348 return sprintf(buf, "0x%08x\n", tmp);
1349}
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001350
1351static DEVICE_ATTR(rtc, S_IWUSR | S_IRUGO, show_rtc, NULL);
James Ketrenos43f66a62005-03-25 12:31:53 -06001352
1353/*
1354 * Add a device attribute to view/control the delay between eeprom
1355 * operations.
1356 */
Andrew Mortonad3fee52005-06-20 14:30:36 -07001357static ssize_t show_eeprom_delay(struct device *d,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001358 struct device_attribute *attr, char *buf)
James Ketrenos43f66a62005-03-25 12:31:53 -06001359{
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001360 int n = ((struct ipw_priv *)d->driver_data)->eeprom_delay;
James Ketrenos43f66a62005-03-25 12:31:53 -06001361 return sprintf(buf, "%i\n", n);
1362}
Andrew Mortonad3fee52005-06-20 14:30:36 -07001363static ssize_t store_eeprom_delay(struct device *d,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001364 struct device_attribute *attr,
1365 const char *buf, size_t count)
James Ketrenos43f66a62005-03-25 12:31:53 -06001366{
Andrew Mortonad3fee52005-06-20 14:30:36 -07001367 struct ipw_priv *p = d->driver_data;
James Ketrenos43f66a62005-03-25 12:31:53 -06001368 sscanf(buf, "%i", &p->eeprom_delay);
1369 return strnlen(buf, count);
1370}
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001371
1372static DEVICE_ATTR(eeprom_delay, S_IWUSR | S_IRUGO,
1373 show_eeprom_delay, store_eeprom_delay);
James Ketrenos43f66a62005-03-25 12:31:53 -06001374
Andrew Mortonad3fee52005-06-20 14:30:36 -07001375static ssize_t show_command_event_reg(struct device *d,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001376 struct device_attribute *attr, char *buf)
James Ketrenos43f66a62005-03-25 12:31:53 -06001377{
1378 u32 reg = 0;
Andrew Mortonad3fee52005-06-20 14:30:36 -07001379 struct ipw_priv *p = d->driver_data;
James Ketrenos43f66a62005-03-25 12:31:53 -06001380
James Ketrenosb095c382005-08-24 22:04:42 -05001381 reg = ipw_read_reg32(p, IPW_INTERNAL_CMD_EVENT);
James Ketrenos43f66a62005-03-25 12:31:53 -06001382 return sprintf(buf, "0x%08x\n", reg);
1383}
Andrew Mortonad3fee52005-06-20 14:30:36 -07001384static ssize_t store_command_event_reg(struct device *d,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001385 struct device_attribute *attr,
1386 const char *buf, size_t count)
James Ketrenos43f66a62005-03-25 12:31:53 -06001387{
1388 u32 reg;
Andrew Mortonad3fee52005-06-20 14:30:36 -07001389 struct ipw_priv *p = d->driver_data;
James Ketrenos43f66a62005-03-25 12:31:53 -06001390
1391 sscanf(buf, "%x", &reg);
James Ketrenosb095c382005-08-24 22:04:42 -05001392 ipw_write_reg32(p, IPW_INTERNAL_CMD_EVENT, reg);
James Ketrenos43f66a62005-03-25 12:31:53 -06001393 return strnlen(buf, count);
1394}
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001395
1396static DEVICE_ATTR(command_event_reg, S_IWUSR | S_IRUGO,
1397 show_command_event_reg, store_command_event_reg);
James Ketrenos43f66a62005-03-25 12:31:53 -06001398
Andrew Mortonad3fee52005-06-20 14:30:36 -07001399static ssize_t show_mem_gpio_reg(struct device *d,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001400 struct device_attribute *attr, char *buf)
James Ketrenos43f66a62005-03-25 12:31:53 -06001401{
1402 u32 reg = 0;
Andrew Mortonad3fee52005-06-20 14:30:36 -07001403 struct ipw_priv *p = d->driver_data;
James Ketrenos43f66a62005-03-25 12:31:53 -06001404
1405 reg = ipw_read_reg32(p, 0x301100);
1406 return sprintf(buf, "0x%08x\n", reg);
1407}
Andrew Mortonad3fee52005-06-20 14:30:36 -07001408static ssize_t store_mem_gpio_reg(struct device *d,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001409 struct device_attribute *attr,
1410 const char *buf, size_t count)
James Ketrenos43f66a62005-03-25 12:31:53 -06001411{
1412 u32 reg;
Andrew Mortonad3fee52005-06-20 14:30:36 -07001413 struct ipw_priv *p = d->driver_data;
James Ketrenos43f66a62005-03-25 12:31:53 -06001414
1415 sscanf(buf, "%x", &reg);
1416 ipw_write_reg32(p, 0x301100, reg);
1417 return strnlen(buf, count);
1418}
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001419
1420static DEVICE_ATTR(mem_gpio_reg, S_IWUSR | S_IRUGO,
1421 show_mem_gpio_reg, store_mem_gpio_reg);
James Ketrenos43f66a62005-03-25 12:31:53 -06001422
Andrew Mortonad3fee52005-06-20 14:30:36 -07001423static ssize_t show_indirect_dword(struct device *d,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001424 struct device_attribute *attr, char *buf)
James Ketrenos43f66a62005-03-25 12:31:53 -06001425{
1426 u32 reg = 0;
Andrew Mortonad3fee52005-06-20 14:30:36 -07001427 struct ipw_priv *priv = d->driver_data;
James Ketrenosafbf30a2005-08-25 00:05:33 -05001428
Jeff Garzikbf794512005-07-31 13:07:26 -04001429 if (priv->status & STATUS_INDIRECT_DWORD)
James Ketrenos43f66a62005-03-25 12:31:53 -06001430 reg = ipw_read_reg32(priv, priv->indirect_dword);
Jeff Garzikbf794512005-07-31 13:07:26 -04001431 else
James Ketrenos43f66a62005-03-25 12:31:53 -06001432 reg = 0;
Jeff Garzikbf794512005-07-31 13:07:26 -04001433
James Ketrenos43f66a62005-03-25 12:31:53 -06001434 return sprintf(buf, "0x%08x\n", reg);
1435}
Andrew Mortonad3fee52005-06-20 14:30:36 -07001436static ssize_t store_indirect_dword(struct device *d,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001437 struct device_attribute *attr,
1438 const char *buf, size_t count)
James Ketrenos43f66a62005-03-25 12:31:53 -06001439{
Andrew Mortonad3fee52005-06-20 14:30:36 -07001440 struct ipw_priv *priv = d->driver_data;
James Ketrenos43f66a62005-03-25 12:31:53 -06001441
1442 sscanf(buf, "%x", &priv->indirect_dword);
1443 priv->status |= STATUS_INDIRECT_DWORD;
1444 return strnlen(buf, count);
1445}
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001446
1447static DEVICE_ATTR(indirect_dword, S_IWUSR | S_IRUGO,
1448 show_indirect_dword, store_indirect_dword);
James Ketrenos43f66a62005-03-25 12:31:53 -06001449
Andrew Mortonad3fee52005-06-20 14:30:36 -07001450static ssize_t show_indirect_byte(struct device *d,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001451 struct device_attribute *attr, char *buf)
James Ketrenos43f66a62005-03-25 12:31:53 -06001452{
1453 u8 reg = 0;
Andrew Mortonad3fee52005-06-20 14:30:36 -07001454 struct ipw_priv *priv = d->driver_data;
James Ketrenosafbf30a2005-08-25 00:05:33 -05001455
Jeff Garzikbf794512005-07-31 13:07:26 -04001456 if (priv->status & STATUS_INDIRECT_BYTE)
James Ketrenos43f66a62005-03-25 12:31:53 -06001457 reg = ipw_read_reg8(priv, priv->indirect_byte);
Jeff Garzikbf794512005-07-31 13:07:26 -04001458 else
James Ketrenos43f66a62005-03-25 12:31:53 -06001459 reg = 0;
1460
1461 return sprintf(buf, "0x%02x\n", reg);
1462}
Andrew Mortonad3fee52005-06-20 14:30:36 -07001463static ssize_t store_indirect_byte(struct device *d,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001464 struct device_attribute *attr,
1465 const char *buf, size_t count)
James Ketrenos43f66a62005-03-25 12:31:53 -06001466{
Andrew Mortonad3fee52005-06-20 14:30:36 -07001467 struct ipw_priv *priv = d->driver_data;
James Ketrenos43f66a62005-03-25 12:31:53 -06001468
1469 sscanf(buf, "%x", &priv->indirect_byte);
1470 priv->status |= STATUS_INDIRECT_BYTE;
1471 return strnlen(buf, count);
1472}
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001473
1474static DEVICE_ATTR(indirect_byte, S_IWUSR | S_IRUGO,
James Ketrenos43f66a62005-03-25 12:31:53 -06001475 show_indirect_byte, store_indirect_byte);
1476
Andrew Mortonad3fee52005-06-20 14:30:36 -07001477static ssize_t show_direct_dword(struct device *d,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001478 struct device_attribute *attr, char *buf)
James Ketrenos43f66a62005-03-25 12:31:53 -06001479{
1480 u32 reg = 0;
Andrew Mortonad3fee52005-06-20 14:30:36 -07001481 struct ipw_priv *priv = d->driver_data;
James Ketrenos43f66a62005-03-25 12:31:53 -06001482
Jeff Garzikbf794512005-07-31 13:07:26 -04001483 if (priv->status & STATUS_DIRECT_DWORD)
James Ketrenos43f66a62005-03-25 12:31:53 -06001484 reg = ipw_read32(priv, priv->direct_dword);
Jeff Garzikbf794512005-07-31 13:07:26 -04001485 else
James Ketrenos43f66a62005-03-25 12:31:53 -06001486 reg = 0;
1487
1488 return sprintf(buf, "0x%08x\n", reg);
1489}
Andrew Mortonad3fee52005-06-20 14:30:36 -07001490static ssize_t store_direct_dword(struct device *d,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001491 struct device_attribute *attr,
1492 const char *buf, size_t count)
James Ketrenos43f66a62005-03-25 12:31:53 -06001493{
Andrew Mortonad3fee52005-06-20 14:30:36 -07001494 struct ipw_priv *priv = d->driver_data;
James Ketrenos43f66a62005-03-25 12:31:53 -06001495
1496 sscanf(buf, "%x", &priv->direct_dword);
1497 priv->status |= STATUS_DIRECT_DWORD;
1498 return strnlen(buf, count);
1499}
James Ketrenos43f66a62005-03-25 12:31:53 -06001500
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001501static DEVICE_ATTR(direct_dword, S_IWUSR | S_IRUGO,
1502 show_direct_dword, store_direct_dword);
James Ketrenos43f66a62005-03-25 12:31:53 -06001503
1504static inline int rf_kill_active(struct ipw_priv *priv)
1505{
1506 if (0 == (ipw_read32(priv, 0x30) & 0x10000))
1507 priv->status |= STATUS_RF_KILL_HW;
1508 else
1509 priv->status &= ~STATUS_RF_KILL_HW;
1510
1511 return (priv->status & STATUS_RF_KILL_HW) ? 1 : 0;
1512}
1513
Andrew Mortonad3fee52005-06-20 14:30:36 -07001514static ssize_t show_rf_kill(struct device *d, struct device_attribute *attr,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001515 char *buf)
James Ketrenos43f66a62005-03-25 12:31:53 -06001516{
1517 /* 0 - RF kill not enabled
Jeff Garzikbf794512005-07-31 13:07:26 -04001518 1 - SW based RF kill active (sysfs)
James Ketrenos43f66a62005-03-25 12:31:53 -06001519 2 - HW based RF kill active
1520 3 - Both HW and SW baed RF kill active */
Andrew Mortonad3fee52005-06-20 14:30:36 -07001521 struct ipw_priv *priv = d->driver_data;
James Ketrenos43f66a62005-03-25 12:31:53 -06001522 int val = ((priv->status & STATUS_RF_KILL_SW) ? 0x1 : 0x0) |
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001523 (rf_kill_active(priv) ? 0x2 : 0x0);
James Ketrenos43f66a62005-03-25 12:31:53 -06001524 return sprintf(buf, "%i\n", val);
1525}
1526
1527static int ipw_radio_kill_sw(struct ipw_priv *priv, int disable_radio)
1528{
Jeff Garzikbf794512005-07-31 13:07:26 -04001529 if ((disable_radio ? 1 : 0) ==
James Ketrenosea2b26e2005-08-24 21:25:16 -05001530 ((priv->status & STATUS_RF_KILL_SW) ? 1 : 0))
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001531 return 0;
James Ketrenos43f66a62005-03-25 12:31:53 -06001532
1533 IPW_DEBUG_RF_KILL("Manual SW RF Kill set to: RADIO %s\n",
1534 disable_radio ? "OFF" : "ON");
1535
1536 if (disable_radio) {
1537 priv->status |= STATUS_RF_KILL_SW;
1538
James Ketrenosa613bff2005-08-24 21:43:11 -05001539 if (priv->workqueue)
James Ketrenos43f66a62005-03-25 12:31:53 -06001540 cancel_delayed_work(&priv->request_scan);
James Ketrenos43f66a62005-03-25 12:31:53 -06001541 queue_work(priv->workqueue, &priv->down);
1542 } else {
1543 priv->status &= ~STATUS_RF_KILL_SW;
1544 if (rf_kill_active(priv)) {
1545 IPW_DEBUG_RF_KILL("Can not turn radio back on - "
1546 "disabled by HW switch\n");
1547 /* Make sure the RF_KILL check timer is running */
1548 cancel_delayed_work(&priv->rf_kill);
Jeff Garzikbf794512005-07-31 13:07:26 -04001549 queue_delayed_work(priv->workqueue, &priv->rf_kill,
James Ketrenos43f66a62005-03-25 12:31:53 -06001550 2 * HZ);
Jeff Garzikbf794512005-07-31 13:07:26 -04001551 } else
James Ketrenos43f66a62005-03-25 12:31:53 -06001552 queue_work(priv->workqueue, &priv->up);
1553 }
1554
1555 return 1;
1556}
1557
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001558static ssize_t store_rf_kill(struct device *d, struct device_attribute *attr,
1559 const char *buf, size_t count)
James Ketrenos43f66a62005-03-25 12:31:53 -06001560{
Andrew Mortonad3fee52005-06-20 14:30:36 -07001561 struct ipw_priv *priv = d->driver_data;
Jeff Garzikbf794512005-07-31 13:07:26 -04001562
James Ketrenos43f66a62005-03-25 12:31:53 -06001563 ipw_radio_kill_sw(priv, buf[0] == '1');
1564
1565 return count;
1566}
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001567
1568static DEVICE_ATTR(rf_kill, S_IWUSR | S_IRUGO, show_rf_kill, store_rf_kill);
James Ketrenos43f66a62005-03-25 12:31:53 -06001569
James Ketrenosb095c382005-08-24 22:04:42 -05001570static ssize_t show_speed_scan(struct device *d, struct device_attribute *attr,
1571 char *buf)
1572{
1573 struct ipw_priv *priv = (struct ipw_priv *)d->driver_data;
1574 int pos = 0, len = 0;
1575 if (priv->config & CFG_SPEED_SCAN) {
1576 while (priv->speed_scan[pos] != 0)
1577 len += sprintf(&buf[len], "%d ",
1578 priv->speed_scan[pos++]);
1579 return len + sprintf(&buf[len], "\n");
1580 }
1581
1582 return sprintf(buf, "0\n");
1583}
1584
1585static ssize_t store_speed_scan(struct device *d, struct device_attribute *attr,
1586 const char *buf, size_t count)
1587{
1588 struct ipw_priv *priv = (struct ipw_priv *)d->driver_data;
1589 int channel, pos = 0;
1590 const char *p = buf;
1591
1592 /* list of space separated channels to scan, optionally ending with 0 */
1593 while ((channel = simple_strtol(p, NULL, 0))) {
1594 if (pos == MAX_SPEED_SCAN - 1) {
1595 priv->speed_scan[pos] = 0;
1596 break;
1597 }
1598
1599 if (ieee80211_is_valid_channel(priv->ieee, channel))
1600 priv->speed_scan[pos++] = channel;
1601 else
1602 IPW_WARNING("Skipping invalid channel request: %d\n",
1603 channel);
1604 p = strchr(p, ' ');
1605 if (!p)
1606 break;
1607 while (*p == ' ' || *p == '\t')
1608 p++;
1609 }
1610
1611 if (pos == 0)
1612 priv->config &= ~CFG_SPEED_SCAN;
1613 else {
1614 priv->speed_scan_pos = 0;
1615 priv->config |= CFG_SPEED_SCAN;
1616 }
1617
1618 return count;
1619}
1620
1621static DEVICE_ATTR(speed_scan, S_IWUSR | S_IRUGO, show_speed_scan,
1622 store_speed_scan);
1623
1624static ssize_t show_net_stats(struct device *d, struct device_attribute *attr,
1625 char *buf)
1626{
1627 struct ipw_priv *priv = (struct ipw_priv *)d->driver_data;
1628 return sprintf(buf, "%c\n", (priv->config & CFG_NET_STATS) ? '1' : '0');
1629}
1630
1631static ssize_t store_net_stats(struct device *d, struct device_attribute *attr,
1632 const char *buf, size_t count)
1633{
1634 struct ipw_priv *priv = (struct ipw_priv *)d->driver_data;
1635 if (buf[0] == '1')
1636 priv->config |= CFG_NET_STATS;
1637 else
1638 priv->config &= ~CFG_NET_STATS;
1639
1640 return count;
1641}
1642
James Ketrenosafbf30a2005-08-25 00:05:33 -05001643static DEVICE_ATTR(net_stats, S_IWUSR | S_IRUGO,
1644 show_net_stats, store_net_stats);
James Ketrenosb095c382005-08-24 22:04:42 -05001645
James Ketrenosea2b26e2005-08-24 21:25:16 -05001646static void notify_wx_assoc_event(struct ipw_priv *priv)
1647{
1648 union iwreq_data wrqu;
1649 wrqu.ap_addr.sa_family = ARPHRD_ETHER;
1650 if (priv->status & STATUS_ASSOCIATED)
1651 memcpy(wrqu.ap_addr.sa_data, priv->bssid, ETH_ALEN);
1652 else
1653 memset(wrqu.ap_addr.sa_data, 0, ETH_ALEN);
1654 wireless_send_event(priv->net_dev, SIOCGIWAP, &wrqu, NULL);
1655}
1656
James Ketrenos43f66a62005-03-25 12:31:53 -06001657static void ipw_irq_tasklet(struct ipw_priv *priv)
1658{
1659 u32 inta, inta_mask, handled = 0;
1660 unsigned long flags;
1661 int rc = 0;
1662
1663 spin_lock_irqsave(&priv->lock, flags);
1664
James Ketrenosb095c382005-08-24 22:04:42 -05001665 inta = ipw_read32(priv, IPW_INTA_RW);
1666 inta_mask = ipw_read32(priv, IPW_INTA_MASK_R);
1667 inta &= (IPW_INTA_MASK_ALL & inta_mask);
James Ketrenos43f66a62005-03-25 12:31:53 -06001668
1669 /* Add any cached INTA values that need to be handled */
1670 inta |= priv->isr_inta;
1671
1672 /* handle all the justifications for the interrupt */
James Ketrenosb095c382005-08-24 22:04:42 -05001673 if (inta & IPW_INTA_BIT_RX_TRANSFER) {
James Ketrenos43f66a62005-03-25 12:31:53 -06001674 ipw_rx(priv);
James Ketrenosb095c382005-08-24 22:04:42 -05001675 handled |= IPW_INTA_BIT_RX_TRANSFER;
James Ketrenos43f66a62005-03-25 12:31:53 -06001676 }
1677
James Ketrenosb095c382005-08-24 22:04:42 -05001678 if (inta & IPW_INTA_BIT_TX_CMD_QUEUE) {
James Ketrenos43f66a62005-03-25 12:31:53 -06001679 IPW_DEBUG_HC("Command completed.\n");
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001680 rc = ipw_queue_tx_reclaim(priv, &priv->txq_cmd, -1);
James Ketrenos43f66a62005-03-25 12:31:53 -06001681 priv->status &= ~STATUS_HCMD_ACTIVE;
1682 wake_up_interruptible(&priv->wait_command_queue);
James Ketrenosb095c382005-08-24 22:04:42 -05001683 handled |= IPW_INTA_BIT_TX_CMD_QUEUE;
James Ketrenos43f66a62005-03-25 12:31:53 -06001684 }
1685
James Ketrenosb095c382005-08-24 22:04:42 -05001686 if (inta & IPW_INTA_BIT_TX_QUEUE_1) {
James Ketrenos43f66a62005-03-25 12:31:53 -06001687 IPW_DEBUG_TX("TX_QUEUE_1\n");
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001688 rc = ipw_queue_tx_reclaim(priv, &priv->txq[0], 0);
James Ketrenosb095c382005-08-24 22:04:42 -05001689 handled |= IPW_INTA_BIT_TX_QUEUE_1;
James Ketrenos43f66a62005-03-25 12:31:53 -06001690 }
1691
James Ketrenosb095c382005-08-24 22:04:42 -05001692 if (inta & IPW_INTA_BIT_TX_QUEUE_2) {
James Ketrenos43f66a62005-03-25 12:31:53 -06001693 IPW_DEBUG_TX("TX_QUEUE_2\n");
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001694 rc = ipw_queue_tx_reclaim(priv, &priv->txq[1], 1);
James Ketrenosb095c382005-08-24 22:04:42 -05001695 handled |= IPW_INTA_BIT_TX_QUEUE_2;
James Ketrenos43f66a62005-03-25 12:31:53 -06001696 }
1697
James Ketrenosb095c382005-08-24 22:04:42 -05001698 if (inta & IPW_INTA_BIT_TX_QUEUE_3) {
James Ketrenos43f66a62005-03-25 12:31:53 -06001699 IPW_DEBUG_TX("TX_QUEUE_3\n");
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001700 rc = ipw_queue_tx_reclaim(priv, &priv->txq[2], 2);
James Ketrenosb095c382005-08-24 22:04:42 -05001701 handled |= IPW_INTA_BIT_TX_QUEUE_3;
James Ketrenos43f66a62005-03-25 12:31:53 -06001702 }
1703
James Ketrenosb095c382005-08-24 22:04:42 -05001704 if (inta & IPW_INTA_BIT_TX_QUEUE_4) {
James Ketrenos43f66a62005-03-25 12:31:53 -06001705 IPW_DEBUG_TX("TX_QUEUE_4\n");
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001706 rc = ipw_queue_tx_reclaim(priv, &priv->txq[3], 3);
James Ketrenosb095c382005-08-24 22:04:42 -05001707 handled |= IPW_INTA_BIT_TX_QUEUE_4;
James Ketrenos43f66a62005-03-25 12:31:53 -06001708 }
1709
James Ketrenosb095c382005-08-24 22:04:42 -05001710 if (inta & IPW_INTA_BIT_STATUS_CHANGE) {
James Ketrenos43f66a62005-03-25 12:31:53 -06001711 IPW_WARNING("STATUS_CHANGE\n");
James Ketrenosb095c382005-08-24 22:04:42 -05001712 handled |= IPW_INTA_BIT_STATUS_CHANGE;
James Ketrenos43f66a62005-03-25 12:31:53 -06001713 }
1714
James Ketrenosb095c382005-08-24 22:04:42 -05001715 if (inta & IPW_INTA_BIT_BEACON_PERIOD_EXPIRED) {
James Ketrenos43f66a62005-03-25 12:31:53 -06001716 IPW_WARNING("TX_PERIOD_EXPIRED\n");
James Ketrenosb095c382005-08-24 22:04:42 -05001717 handled |= IPW_INTA_BIT_BEACON_PERIOD_EXPIRED;
James Ketrenos43f66a62005-03-25 12:31:53 -06001718 }
1719
James Ketrenosb095c382005-08-24 22:04:42 -05001720 if (inta & IPW_INTA_BIT_SLAVE_MODE_HOST_CMD_DONE) {
James Ketrenos43f66a62005-03-25 12:31:53 -06001721 IPW_WARNING("HOST_CMD_DONE\n");
James Ketrenosb095c382005-08-24 22:04:42 -05001722 handled |= IPW_INTA_BIT_SLAVE_MODE_HOST_CMD_DONE;
James Ketrenos43f66a62005-03-25 12:31:53 -06001723 }
1724
James Ketrenosb095c382005-08-24 22:04:42 -05001725 if (inta & IPW_INTA_BIT_FW_INITIALIZATION_DONE) {
James Ketrenos43f66a62005-03-25 12:31:53 -06001726 IPW_WARNING("FW_INITIALIZATION_DONE\n");
James Ketrenosb095c382005-08-24 22:04:42 -05001727 handled |= IPW_INTA_BIT_FW_INITIALIZATION_DONE;
James Ketrenos43f66a62005-03-25 12:31:53 -06001728 }
1729
James Ketrenosb095c382005-08-24 22:04:42 -05001730 if (inta & IPW_INTA_BIT_FW_CARD_DISABLE_PHY_OFF_DONE) {
James Ketrenos43f66a62005-03-25 12:31:53 -06001731 IPW_WARNING("PHY_OFF_DONE\n");
James Ketrenosb095c382005-08-24 22:04:42 -05001732 handled |= IPW_INTA_BIT_FW_CARD_DISABLE_PHY_OFF_DONE;
James Ketrenos43f66a62005-03-25 12:31:53 -06001733 }
1734
James Ketrenosb095c382005-08-24 22:04:42 -05001735 if (inta & IPW_INTA_BIT_RF_KILL_DONE) {
James Ketrenos43f66a62005-03-25 12:31:53 -06001736 IPW_DEBUG_RF_KILL("RF_KILL_DONE\n");
1737 priv->status |= STATUS_RF_KILL_HW;
1738 wake_up_interruptible(&priv->wait_command_queue);
James Ketrenosea2b26e2005-08-24 21:25:16 -05001739 priv->status &= ~(STATUS_ASSOCIATED | STATUS_ASSOCIATING);
James Ketrenos43f66a62005-03-25 12:31:53 -06001740 cancel_delayed_work(&priv->request_scan);
James Ketrenosa613bff2005-08-24 21:43:11 -05001741 schedule_work(&priv->link_down);
James Ketrenos43f66a62005-03-25 12:31:53 -06001742 queue_delayed_work(priv->workqueue, &priv->rf_kill, 2 * HZ);
James Ketrenosb095c382005-08-24 22:04:42 -05001743 handled |= IPW_INTA_BIT_RF_KILL_DONE;
James Ketrenos43f66a62005-03-25 12:31:53 -06001744 }
Jeff Garzikbf794512005-07-31 13:07:26 -04001745
James Ketrenosb095c382005-08-24 22:04:42 -05001746 if (inta & IPW_INTA_BIT_FATAL_ERROR) {
James Ketrenos43f66a62005-03-25 12:31:53 -06001747 IPW_ERROR("Firmware error detected. Restarting.\n");
James Ketrenosb39860c2005-08-12 09:36:32 -05001748 if (priv->error) {
1749 IPW_ERROR("Sysfs 'error' log already exists.\n");
James Ketrenos43f66a62005-03-25 12:31:53 -06001750#ifdef CONFIG_IPW_DEBUG
James Ketrenosb39860c2005-08-12 09:36:32 -05001751 if (ipw_debug_level & IPW_DL_FW_ERRORS) {
1752 struct ipw_fw_error *error =
1753 ipw_alloc_error_log(priv);
1754 ipw_dump_error_log(priv, error);
1755 if (error)
1756 ipw_free_error_log(error);
1757 }
James Ketrenos43f66a62005-03-25 12:31:53 -06001758#endif
James Ketrenosb39860c2005-08-12 09:36:32 -05001759 } else {
1760 priv->error = ipw_alloc_error_log(priv);
1761 if (priv->error)
1762 IPW_ERROR("Sysfs 'error' log captured.\n");
1763 else
1764 IPW_ERROR("Error allocating sysfs 'error' "
1765 "log.\n");
1766#ifdef CONFIG_IPW_DEBUG
1767 if (ipw_debug_level & IPW_DL_FW_ERRORS)
1768 ipw_dump_error_log(priv, priv->error);
1769#endif
1770 }
1771
James Ketrenosb095c382005-08-24 22:04:42 -05001772 /* XXX: If hardware encryption is for WPA/WPA2,
1773 * we have to notify the supplicant. */
1774 if (priv->ieee->sec.encrypt) {
1775 priv->status &= ~STATUS_ASSOCIATED;
1776 notify_wx_assoc_event(priv);
1777 }
1778
1779 /* Keep the restart process from trying to send host
1780 * commands by clearing the INIT status bit */
1781 priv->status &= ~STATUS_INIT;
James Ketrenosafbf30a2005-08-25 00:05:33 -05001782
1783 /* Cancel currently queued command. */
1784 priv->status &= ~STATUS_HCMD_ACTIVE;
1785 wake_up_interruptible(&priv->wait_command_queue);
1786
James Ketrenos43f66a62005-03-25 12:31:53 -06001787 queue_work(priv->workqueue, &priv->adapter_restart);
James Ketrenosb095c382005-08-24 22:04:42 -05001788 handled |= IPW_INTA_BIT_FATAL_ERROR;
James Ketrenos43f66a62005-03-25 12:31:53 -06001789 }
1790
James Ketrenosb095c382005-08-24 22:04:42 -05001791 if (inta & IPW_INTA_BIT_PARITY_ERROR) {
James Ketrenos43f66a62005-03-25 12:31:53 -06001792 IPW_ERROR("Parity error\n");
James Ketrenosb095c382005-08-24 22:04:42 -05001793 handled |= IPW_INTA_BIT_PARITY_ERROR;
James Ketrenos43f66a62005-03-25 12:31:53 -06001794 }
1795
1796 if (handled != inta) {
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001797 IPW_ERROR("Unhandled INTA bits 0x%08x\n", inta & ~handled);
James Ketrenos43f66a62005-03-25 12:31:53 -06001798 }
1799
1800 /* enable all interrupts */
1801 ipw_enable_interrupts(priv);
1802
1803 spin_unlock_irqrestore(&priv->lock, flags);
1804}
Jeff Garzikbf794512005-07-31 13:07:26 -04001805
James Ketrenos43f66a62005-03-25 12:31:53 -06001806#ifdef CONFIG_IPW_DEBUG
1807#define IPW_CMD(x) case IPW_CMD_ ## x : return #x
1808static char *get_cmd_string(u8 cmd)
1809{
1810 switch (cmd) {
1811 IPW_CMD(HOST_COMPLETE);
Jeff Garzikbf794512005-07-31 13:07:26 -04001812 IPW_CMD(POWER_DOWN);
1813 IPW_CMD(SYSTEM_CONFIG);
1814 IPW_CMD(MULTICAST_ADDRESS);
1815 IPW_CMD(SSID);
1816 IPW_CMD(ADAPTER_ADDRESS);
1817 IPW_CMD(PORT_TYPE);
1818 IPW_CMD(RTS_THRESHOLD);
1819 IPW_CMD(FRAG_THRESHOLD);
1820 IPW_CMD(POWER_MODE);
1821 IPW_CMD(WEP_KEY);
1822 IPW_CMD(TGI_TX_KEY);
1823 IPW_CMD(SCAN_REQUEST);
1824 IPW_CMD(SCAN_REQUEST_EXT);
1825 IPW_CMD(ASSOCIATE);
1826 IPW_CMD(SUPPORTED_RATES);
1827 IPW_CMD(SCAN_ABORT);
1828 IPW_CMD(TX_FLUSH);
1829 IPW_CMD(QOS_PARAMETERS);
1830 IPW_CMD(DINO_CONFIG);
1831 IPW_CMD(RSN_CAPABILITIES);
1832 IPW_CMD(RX_KEY);
1833 IPW_CMD(CARD_DISABLE);
1834 IPW_CMD(SEED_NUMBER);
1835 IPW_CMD(TX_POWER);
1836 IPW_CMD(COUNTRY_INFO);
1837 IPW_CMD(AIRONET_INFO);
1838 IPW_CMD(AP_TX_POWER);
1839 IPW_CMD(CCKM_INFO);
1840 IPW_CMD(CCX_VER_INFO);
1841 IPW_CMD(SET_CALIBRATION);
1842 IPW_CMD(SENSITIVITY_CALIB);
1843 IPW_CMD(RETRY_LIMIT);
1844 IPW_CMD(IPW_PRE_POWER_DOWN);
1845 IPW_CMD(VAP_BEACON_TEMPLATE);
1846 IPW_CMD(VAP_DTIM_PERIOD);
1847 IPW_CMD(EXT_SUPPORTED_RATES);
1848 IPW_CMD(VAP_LOCAL_TX_PWR_CONSTRAINT);
1849 IPW_CMD(VAP_QUIET_INTERVALS);
1850 IPW_CMD(VAP_CHANNEL_SWITCH);
1851 IPW_CMD(VAP_MANDATORY_CHANNELS);
1852 IPW_CMD(VAP_CELL_PWR_LIMIT);
1853 IPW_CMD(VAP_CF_PARAM_SET);
1854 IPW_CMD(VAP_SET_BEACONING_STATE);
1855 IPW_CMD(MEASUREMENT);
1856 IPW_CMD(POWER_CAPABILITY);
1857 IPW_CMD(SUPPORTED_CHANNELS);
1858 IPW_CMD(TPC_REPORT);
1859 IPW_CMD(WME_INFO);
1860 IPW_CMD(PRODUCTION_COMMAND);
1861 default:
James Ketrenos43f66a62005-03-25 12:31:53 -06001862 return "UNKNOWN";
1863 }
1864}
James Ketrenosea2b26e2005-08-24 21:25:16 -05001865#endif
James Ketrenos43f66a62005-03-25 12:31:53 -06001866
1867#define HOST_COMPLETE_TIMEOUT HZ
1868static int ipw_send_cmd(struct ipw_priv *priv, struct host_cmd *cmd)
1869{
1870 int rc = 0;
James Ketrenosa613bff2005-08-24 21:43:11 -05001871 unsigned long flags;
James Ketrenos43f66a62005-03-25 12:31:53 -06001872
James Ketrenosa613bff2005-08-24 21:43:11 -05001873 spin_lock_irqsave(&priv->lock, flags);
James Ketrenos43f66a62005-03-25 12:31:53 -06001874 if (priv->status & STATUS_HCMD_ACTIVE) {
James Ketrenos9ddf84f2005-08-16 17:07:11 -05001875 IPW_ERROR("Failed to send %s: Already sending a command.\n",
1876 get_cmd_string(cmd->cmd));
James Ketrenosa613bff2005-08-24 21:43:11 -05001877 spin_unlock_irqrestore(&priv->lock, flags);
James Ketrenos9ddf84f2005-08-16 17:07:11 -05001878 return -EAGAIN;
James Ketrenos43f66a62005-03-25 12:31:53 -06001879 }
1880
1881 priv->status |= STATUS_HCMD_ACTIVE;
Jeff Garzikbf794512005-07-31 13:07:26 -04001882
James Ketrenosf6c5cb72005-08-25 00:39:09 -05001883 if (priv->cmdlog) {
1884 priv->cmdlog[priv->cmdlog_pos].jiffies = jiffies;
1885 priv->cmdlog[priv->cmdlog_pos].cmd.cmd = cmd->cmd;
1886 priv->cmdlog[priv->cmdlog_pos].cmd.len = cmd->len;
1887 memcpy(priv->cmdlog[priv->cmdlog_pos].cmd.param, cmd->param,
1888 cmd->len);
1889 priv->cmdlog[priv->cmdlog_pos].retcode = -1;
1890 }
1891
James Ketrenosb095c382005-08-24 22:04:42 -05001892 IPW_DEBUG_HC("%s command (#%d) %d bytes: 0x%08X\n",
1893 get_cmd_string(cmd->cmd), cmd->cmd, cmd->len,
1894 priv->status);
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001895 printk_buf(IPW_DL_HOST_COMMAND, (u8 *) cmd->param, cmd->len);
James Ketrenos43f66a62005-03-25 12:31:53 -06001896
1897 rc = ipw_queue_tx_hcmd(priv, cmd->cmd, &cmd->param, cmd->len, 0);
James Ketrenosa613bff2005-08-24 21:43:11 -05001898 if (rc) {
1899 priv->status &= ~STATUS_HCMD_ACTIVE;
James Ketrenos9ddf84f2005-08-16 17:07:11 -05001900 IPW_ERROR("Failed to send %s: Reason %d\n",
1901 get_cmd_string(cmd->cmd), rc);
James Ketrenosa613bff2005-08-24 21:43:11 -05001902 spin_unlock_irqrestore(&priv->lock, flags);
James Ketrenosf6c5cb72005-08-25 00:39:09 -05001903 goto exit;
James Ketrenosa613bff2005-08-24 21:43:11 -05001904 }
1905 spin_unlock_irqrestore(&priv->lock, flags);
James Ketrenos43f66a62005-03-25 12:31:53 -06001906
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001907 rc = wait_event_interruptible_timeout(priv->wait_command_queue,
1908 !(priv->
1909 status & STATUS_HCMD_ACTIVE),
1910 HOST_COMPLETE_TIMEOUT);
James Ketrenos43f66a62005-03-25 12:31:53 -06001911 if (rc == 0) {
James Ketrenosa613bff2005-08-24 21:43:11 -05001912 spin_lock_irqsave(&priv->lock, flags);
1913 if (priv->status & STATUS_HCMD_ACTIVE) {
James Ketrenos9ddf84f2005-08-16 17:07:11 -05001914 IPW_ERROR("Failed to send %s: Command timed out.\n",
1915 get_cmd_string(cmd->cmd));
James Ketrenosa613bff2005-08-24 21:43:11 -05001916 priv->status &= ~STATUS_HCMD_ACTIVE;
1917 spin_unlock_irqrestore(&priv->lock, flags);
James Ketrenosf6c5cb72005-08-25 00:39:09 -05001918 rc = -EIO;
1919 goto exit;
James Ketrenosa613bff2005-08-24 21:43:11 -05001920 }
1921 spin_unlock_irqrestore(&priv->lock, flags);
James Ketrenos43f66a62005-03-25 12:31:53 -06001922 }
James Ketrenosa613bff2005-08-24 21:43:11 -05001923
James Ketrenosb095c382005-08-24 22:04:42 -05001924 if (priv->status & STATUS_RF_KILL_HW) {
James Ketrenos9ddf84f2005-08-16 17:07:11 -05001925 IPW_ERROR("Failed to send %s: Aborted due to RF kill switch.\n",
1926 get_cmd_string(cmd->cmd));
James Ketrenosf6c5cb72005-08-25 00:39:09 -05001927 rc = -EIO;
1928 goto exit;
James Ketrenos43f66a62005-03-25 12:31:53 -06001929 }
1930
James Ketrenosf6c5cb72005-08-25 00:39:09 -05001931 exit:
1932 if (priv->cmdlog) {
1933 priv->cmdlog[priv->cmdlog_pos++].retcode = rc;
1934 priv->cmdlog_pos %= priv->cmdlog_len;
1935 }
1936 return rc;
James Ketrenos43f66a62005-03-25 12:31:53 -06001937}
1938
1939static int ipw_send_host_complete(struct ipw_priv *priv)
1940{
1941 struct host_cmd cmd = {
1942 .cmd = IPW_CMD_HOST_COMPLETE,
1943 .len = 0
1944 };
1945
1946 if (!priv) {
1947 IPW_ERROR("Invalid args\n");
1948 return -1;
1949 }
1950
James Ketrenos9ddf84f2005-08-16 17:07:11 -05001951 return ipw_send_cmd(priv, &cmd);
James Ketrenos43f66a62005-03-25 12:31:53 -06001952}
1953
Jeff Garzikbf794512005-07-31 13:07:26 -04001954static int ipw_send_system_config(struct ipw_priv *priv,
James Ketrenos43f66a62005-03-25 12:31:53 -06001955 struct ipw_sys_config *config)
1956{
1957 struct host_cmd cmd = {
1958 .cmd = IPW_CMD_SYSTEM_CONFIG,
1959 .len = sizeof(*config)
1960 };
1961
1962 if (!priv || !config) {
1963 IPW_ERROR("Invalid args\n");
1964 return -1;
1965 }
1966
James Ketrenosafbf30a2005-08-25 00:05:33 -05001967 memcpy(cmd.param, config, sizeof(*config));
James Ketrenos9ddf84f2005-08-16 17:07:11 -05001968 return ipw_send_cmd(priv, &cmd);
James Ketrenos43f66a62005-03-25 12:31:53 -06001969}
1970
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001971static int ipw_send_ssid(struct ipw_priv *priv, u8 * ssid, int len)
James Ketrenos43f66a62005-03-25 12:31:53 -06001972{
1973 struct host_cmd cmd = {
1974 .cmd = IPW_CMD_SSID,
1975 .len = min(len, IW_ESSID_MAX_SIZE)
1976 };
1977
1978 if (!priv || !ssid) {
1979 IPW_ERROR("Invalid args\n");
1980 return -1;
1981 }
1982
James Ketrenosafbf30a2005-08-25 00:05:33 -05001983 memcpy(cmd.param, ssid, cmd.len);
James Ketrenos9ddf84f2005-08-16 17:07:11 -05001984 return ipw_send_cmd(priv, &cmd);
James Ketrenos43f66a62005-03-25 12:31:53 -06001985}
1986
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001987static int ipw_send_adapter_address(struct ipw_priv *priv, u8 * mac)
James Ketrenos43f66a62005-03-25 12:31:53 -06001988{
1989 struct host_cmd cmd = {
1990 .cmd = IPW_CMD_ADAPTER_ADDRESS,
1991 .len = ETH_ALEN
1992 };
1993
1994 if (!priv || !mac) {
1995 IPW_ERROR("Invalid args\n");
1996 return -1;
1997 }
1998
1999 IPW_DEBUG_INFO("%s: Setting MAC to " MAC_FMT "\n",
2000 priv->net_dev->name, MAC_ARG(mac));
2001
James Ketrenosafbf30a2005-08-25 00:05:33 -05002002 memcpy(cmd.param, mac, ETH_ALEN);
James Ketrenos9ddf84f2005-08-16 17:07:11 -05002003 return ipw_send_cmd(priv, &cmd);
James Ketrenos43f66a62005-03-25 12:31:53 -06002004}
2005
James Ketrenosa613bff2005-08-24 21:43:11 -05002006/*
2007 * NOTE: This must be executed from our workqueue as it results in udelay
2008 * being called which may corrupt the keyboard if executed on default
2009 * workqueue
2010 */
James Ketrenos43f66a62005-03-25 12:31:53 -06002011static void ipw_adapter_restart(void *adapter)
2012{
2013 struct ipw_priv *priv = adapter;
2014
2015 if (priv->status & STATUS_RF_KILL_MASK)
2016 return;
2017
2018 ipw_down(priv);
James Ketrenosb095c382005-08-24 22:04:42 -05002019
2020 if (priv->assoc_network &&
2021 (priv->assoc_network->capability & WLAN_CAPABILITY_IBSS))
2022 ipw_remove_current_network(priv);
2023
James Ketrenos43f66a62005-03-25 12:31:53 -06002024 if (ipw_up(priv)) {
2025 IPW_ERROR("Failed to up device\n");
2026 return;
2027 }
2028}
2029
James Ketrenosc848d0a2005-08-24 21:56:24 -05002030static void ipw_bg_adapter_restart(void *data)
2031{
2032 struct ipw_priv *priv = data;
2033 down(&priv->sem);
2034 ipw_adapter_restart(data);
2035 up(&priv->sem);
2036}
2037
James Ketrenos43f66a62005-03-25 12:31:53 -06002038#define IPW_SCAN_CHECK_WATCHDOG (5 * HZ)
2039
2040static void ipw_scan_check(void *data)
2041{
2042 struct ipw_priv *priv = data;
2043 if (priv->status & (STATUS_SCANNING | STATUS_SCAN_ABORTING)) {
2044 IPW_DEBUG_SCAN("Scan completion watchdog resetting "
Jeff Garzikbf794512005-07-31 13:07:26 -04002045 "adapter (%dms).\n",
James Ketrenos43f66a62005-03-25 12:31:53 -06002046 IPW_SCAN_CHECK_WATCHDOG / 100);
James Ketrenosa613bff2005-08-24 21:43:11 -05002047 queue_work(priv->workqueue, &priv->adapter_restart);
James Ketrenos43f66a62005-03-25 12:31:53 -06002048 }
2049}
2050
James Ketrenosc848d0a2005-08-24 21:56:24 -05002051static void ipw_bg_scan_check(void *data)
2052{
2053 struct ipw_priv *priv = data;
2054 down(&priv->sem);
2055 ipw_scan_check(data);
2056 up(&priv->sem);
2057}
2058
James Ketrenos43f66a62005-03-25 12:31:53 -06002059static int ipw_send_scan_request_ext(struct ipw_priv *priv,
2060 struct ipw_scan_request_ext *request)
2061{
2062 struct host_cmd cmd = {
2063 .cmd = IPW_CMD_SCAN_REQUEST_EXT,
2064 .len = sizeof(*request)
2065 };
2066
James Ketrenosafbf30a2005-08-25 00:05:33 -05002067 memcpy(cmd.param, request, sizeof(*request));
James Ketrenos9ddf84f2005-08-16 17:07:11 -05002068 return ipw_send_cmd(priv, &cmd);
James Ketrenos43f66a62005-03-25 12:31:53 -06002069}
2070
2071static int ipw_send_scan_abort(struct ipw_priv *priv)
2072{
2073 struct host_cmd cmd = {
2074 .cmd = IPW_CMD_SCAN_ABORT,
2075 .len = 0
2076 };
2077
2078 if (!priv) {
2079 IPW_ERROR("Invalid args\n");
2080 return -1;
2081 }
2082
James Ketrenos9ddf84f2005-08-16 17:07:11 -05002083 return ipw_send_cmd(priv, &cmd);
James Ketrenos43f66a62005-03-25 12:31:53 -06002084}
2085
2086static int ipw_set_sensitivity(struct ipw_priv *priv, u16 sens)
2087{
2088 struct host_cmd cmd = {
2089 .cmd = IPW_CMD_SENSITIVITY_CALIB,
2090 .len = sizeof(struct ipw_sensitivity_calib)
2091 };
2092 struct ipw_sensitivity_calib *calib = (struct ipw_sensitivity_calib *)
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002093 &cmd.param;
James Ketrenos43f66a62005-03-25 12:31:53 -06002094 calib->beacon_rssi_raw = sens;
James Ketrenos9ddf84f2005-08-16 17:07:11 -05002095 return ipw_send_cmd(priv, &cmd);
James Ketrenos43f66a62005-03-25 12:31:53 -06002096}
2097
2098static int ipw_send_associate(struct ipw_priv *priv,
2099 struct ipw_associate *associate)
2100{
2101 struct host_cmd cmd = {
2102 .cmd = IPW_CMD_ASSOCIATE,
2103 .len = sizeof(*associate)
2104 };
2105
James Ketrenosa613bff2005-08-24 21:43:11 -05002106 struct ipw_associate tmp_associate;
2107 memcpy(&tmp_associate, associate, sizeof(*associate));
2108 tmp_associate.policy_support =
2109 cpu_to_le16(tmp_associate.policy_support);
2110 tmp_associate.assoc_tsf_msw = cpu_to_le32(tmp_associate.assoc_tsf_msw);
2111 tmp_associate.assoc_tsf_lsw = cpu_to_le32(tmp_associate.assoc_tsf_lsw);
2112 tmp_associate.capability = cpu_to_le16(tmp_associate.capability);
2113 tmp_associate.listen_interval =
2114 cpu_to_le16(tmp_associate.listen_interval);
2115 tmp_associate.beacon_interval =
2116 cpu_to_le16(tmp_associate.beacon_interval);
2117 tmp_associate.atim_window = cpu_to_le16(tmp_associate.atim_window);
2118
James Ketrenos43f66a62005-03-25 12:31:53 -06002119 if (!priv || !associate) {
2120 IPW_ERROR("Invalid args\n");
2121 return -1;
2122 }
2123
James Ketrenosafbf30a2005-08-25 00:05:33 -05002124 memcpy(cmd.param, &tmp_associate, sizeof(*associate));
James Ketrenos9ddf84f2005-08-16 17:07:11 -05002125 return ipw_send_cmd(priv, &cmd);
James Ketrenos43f66a62005-03-25 12:31:53 -06002126}
2127
2128static int ipw_send_supported_rates(struct ipw_priv *priv,
2129 struct ipw_supported_rates *rates)
2130{
2131 struct host_cmd cmd = {
2132 .cmd = IPW_CMD_SUPPORTED_RATES,
2133 .len = sizeof(*rates)
2134 };
2135
2136 if (!priv || !rates) {
2137 IPW_ERROR("Invalid args\n");
2138 return -1;
2139 }
2140
James Ketrenosafbf30a2005-08-25 00:05:33 -05002141 memcpy(cmd.param, rates, sizeof(*rates));
James Ketrenos9ddf84f2005-08-16 17:07:11 -05002142 return ipw_send_cmd(priv, &cmd);
James Ketrenos43f66a62005-03-25 12:31:53 -06002143}
2144
2145static int ipw_set_random_seed(struct ipw_priv *priv)
2146{
2147 struct host_cmd cmd = {
2148 .cmd = IPW_CMD_SEED_NUMBER,
2149 .len = sizeof(u32)
2150 };
2151
2152 if (!priv) {
2153 IPW_ERROR("Invalid args\n");
2154 return -1;
2155 }
2156
2157 get_random_bytes(&cmd.param, sizeof(u32));
2158
James Ketrenos9ddf84f2005-08-16 17:07:11 -05002159 return ipw_send_cmd(priv, &cmd);
James Ketrenos43f66a62005-03-25 12:31:53 -06002160}
2161
James Ketrenos43f66a62005-03-25 12:31:53 -06002162static int ipw_send_card_disable(struct ipw_priv *priv, u32 phy_off)
2163{
2164 struct host_cmd cmd = {
2165 .cmd = IPW_CMD_CARD_DISABLE,
2166 .len = sizeof(u32)
2167 };
2168
2169 if (!priv) {
2170 IPW_ERROR("Invalid args\n");
2171 return -1;
2172 }
2173
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002174 *((u32 *) & cmd.param) = phy_off;
James Ketrenos43f66a62005-03-25 12:31:53 -06002175
James Ketrenos9ddf84f2005-08-16 17:07:11 -05002176 return ipw_send_cmd(priv, &cmd);
James Ketrenos43f66a62005-03-25 12:31:53 -06002177}
James Ketrenos43f66a62005-03-25 12:31:53 -06002178
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002179static int ipw_send_tx_power(struct ipw_priv *priv, struct ipw_tx_power *power)
James Ketrenos43f66a62005-03-25 12:31:53 -06002180{
2181 struct host_cmd cmd = {
2182 .cmd = IPW_CMD_TX_POWER,
2183 .len = sizeof(*power)
2184 };
2185
2186 if (!priv || !power) {
2187 IPW_ERROR("Invalid args\n");
2188 return -1;
2189 }
2190
James Ketrenosafbf30a2005-08-25 00:05:33 -05002191 memcpy(cmd.param, power, sizeof(*power));
James Ketrenos9ddf84f2005-08-16 17:07:11 -05002192 return ipw_send_cmd(priv, &cmd);
James Ketrenos43f66a62005-03-25 12:31:53 -06002193}
2194
Zhu Yi6de9f7f2005-08-11 14:39:33 +08002195static int ipw_set_tx_power(struct ipw_priv *priv)
2196{
2197 const struct ieee80211_geo *geo = ieee80211_get_geo(priv->ieee);
2198 struct ipw_tx_power tx_power;
2199 s8 max_power;
2200 int i;
2201
2202 memset(&tx_power, 0, sizeof(tx_power));
2203
2204 /* configure device for 'G' band */
2205 tx_power.ieee_mode = IPW_G_MODE;
2206 tx_power.num_channels = geo->bg_channels;
2207 for (i = 0; i < geo->bg_channels; i++) {
2208 max_power = geo->bg[i].max_power;
2209 tx_power.channels_tx_power[i].channel_number =
2210 geo->bg[i].channel;
2211 tx_power.channels_tx_power[i].tx_power = max_power ?
2212 min(max_power, priv->tx_power) : priv->tx_power;
2213 }
2214 if (ipw_send_tx_power(priv, &tx_power))
2215 return -EIO;
2216
2217 /* configure device to also handle 'B' band */
2218 tx_power.ieee_mode = IPW_B_MODE;
2219 if (ipw_send_tx_power(priv, &tx_power))
2220 return -EIO;
2221
2222 /* configure device to also handle 'A' band */
2223 if (priv->ieee->abg_true) {
2224 tx_power.ieee_mode = IPW_A_MODE;
2225 tx_power.num_channels = geo->a_channels;
2226 for (i = 0; i < tx_power.num_channels; i++) {
2227 max_power = geo->a[i].max_power;
2228 tx_power.channels_tx_power[i].channel_number =
2229 geo->a[i].channel;
2230 tx_power.channels_tx_power[i].tx_power = max_power ?
2231 min(max_power, priv->tx_power) : priv->tx_power;
2232 }
2233 if (ipw_send_tx_power(priv, &tx_power))
2234 return -EIO;
2235 }
2236 return 0;
2237}
2238
James Ketrenos43f66a62005-03-25 12:31:53 -06002239static int ipw_send_rts_threshold(struct ipw_priv *priv, u16 rts)
2240{
2241 struct ipw_rts_threshold rts_threshold = {
2242 .rts_threshold = rts,
2243 };
2244 struct host_cmd cmd = {
2245 .cmd = IPW_CMD_RTS_THRESHOLD,
2246 .len = sizeof(rts_threshold)
2247 };
2248
2249 if (!priv) {
2250 IPW_ERROR("Invalid args\n");
2251 return -1;
2252 }
2253
James Ketrenosafbf30a2005-08-25 00:05:33 -05002254 memcpy(cmd.param, &rts_threshold, sizeof(rts_threshold));
James Ketrenos9ddf84f2005-08-16 17:07:11 -05002255 return ipw_send_cmd(priv, &cmd);
James Ketrenos43f66a62005-03-25 12:31:53 -06002256}
2257
2258static int ipw_send_frag_threshold(struct ipw_priv *priv, u16 frag)
2259{
2260 struct ipw_frag_threshold frag_threshold = {
2261 .frag_threshold = frag,
2262 };
2263 struct host_cmd cmd = {
2264 .cmd = IPW_CMD_FRAG_THRESHOLD,
2265 .len = sizeof(frag_threshold)
2266 };
2267
2268 if (!priv) {
2269 IPW_ERROR("Invalid args\n");
2270 return -1;
2271 }
2272
James Ketrenosafbf30a2005-08-25 00:05:33 -05002273 memcpy(cmd.param, &frag_threshold, sizeof(frag_threshold));
James Ketrenos9ddf84f2005-08-16 17:07:11 -05002274 return ipw_send_cmd(priv, &cmd);
James Ketrenos43f66a62005-03-25 12:31:53 -06002275}
2276
2277static int ipw_send_power_mode(struct ipw_priv *priv, u32 mode)
2278{
2279 struct host_cmd cmd = {
2280 .cmd = IPW_CMD_POWER_MODE,
2281 .len = sizeof(u32)
2282 };
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002283 u32 *param = (u32 *) (&cmd.param);
James Ketrenos43f66a62005-03-25 12:31:53 -06002284
2285 if (!priv) {
2286 IPW_ERROR("Invalid args\n");
2287 return -1;
2288 }
Jeff Garzikbf794512005-07-31 13:07:26 -04002289
James Ketrenos43f66a62005-03-25 12:31:53 -06002290 /* If on battery, set to 3, if AC set to CAM, else user
2291 * level */
2292 switch (mode) {
2293 case IPW_POWER_BATTERY:
2294 *param = IPW_POWER_INDEX_3;
2295 break;
2296 case IPW_POWER_AC:
2297 *param = IPW_POWER_MODE_CAM;
2298 break;
2299 default:
2300 *param = mode;
2301 break;
2302 }
2303
James Ketrenos9ddf84f2005-08-16 17:07:11 -05002304 return ipw_send_cmd(priv, &cmd);
James Ketrenos43f66a62005-03-25 12:31:53 -06002305}
2306
James Ketrenosafbf30a2005-08-25 00:05:33 -05002307static int ipw_send_retry_limit(struct ipw_priv *priv, u8 slimit, u8 llimit)
2308{
2309 struct ipw_retry_limit retry_limit = {
2310 .short_retry_limit = slimit,
2311 .long_retry_limit = llimit
2312 };
2313 struct host_cmd cmd = {
2314 .cmd = IPW_CMD_RETRY_LIMIT,
2315 .len = sizeof(retry_limit)
2316 };
2317
2318 if (!priv) {
2319 IPW_ERROR("Invalid args\n");
2320 return -1;
2321 }
2322
2323 memcpy(cmd.param, &retry_limit, sizeof(retry_limit));
James Ketrenos9ddf84f2005-08-16 17:07:11 -05002324 return ipw_send_cmd(priv, &cmd);
James Ketrenosafbf30a2005-08-25 00:05:33 -05002325}
2326
James Ketrenos43f66a62005-03-25 12:31:53 -06002327/*
2328 * The IPW device contains a Microwire compatible EEPROM that stores
2329 * various data like the MAC address. Usually the firmware has exclusive
2330 * access to the eeprom, but during device initialization (before the
2331 * device driver has sent the HostComplete command to the firmware) the
2332 * device driver has read access to the EEPROM by way of indirect addressing
2333 * through a couple of memory mapped registers.
2334 *
2335 * The following is a simplified implementation for pulling data out of the
2336 * the eeprom, along with some helper functions to find information in
2337 * the per device private data's copy of the eeprom.
2338 *
2339 * NOTE: To better understand how these functions work (i.e what is a chip
2340 * select and why do have to keep driving the eeprom clock?), read
2341 * just about any data sheet for a Microwire compatible EEPROM.
2342 */
2343
2344/* write a 32 bit value into the indirect accessor register */
2345static inline void eeprom_write_reg(struct ipw_priv *p, u32 data)
2346{
2347 ipw_write_reg32(p, FW_MEM_REG_EEPROM_ACCESS, data);
Jeff Garzikbf794512005-07-31 13:07:26 -04002348
James Ketrenos43f66a62005-03-25 12:31:53 -06002349 /* the eeprom requires some time to complete the operation */
2350 udelay(p->eeprom_delay);
2351
2352 return;
2353}
2354
2355/* perform a chip select operation */
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002356static inline void eeprom_cs(struct ipw_priv *priv)
James Ketrenos43f66a62005-03-25 12:31:53 -06002357{
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002358 eeprom_write_reg(priv, 0);
2359 eeprom_write_reg(priv, EEPROM_BIT_CS);
2360 eeprom_write_reg(priv, EEPROM_BIT_CS | EEPROM_BIT_SK);
2361 eeprom_write_reg(priv, EEPROM_BIT_CS);
James Ketrenos43f66a62005-03-25 12:31:53 -06002362}
2363
2364/* perform a chip select operation */
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002365static inline void eeprom_disable_cs(struct ipw_priv *priv)
James Ketrenos43f66a62005-03-25 12:31:53 -06002366{
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002367 eeprom_write_reg(priv, EEPROM_BIT_CS);
2368 eeprom_write_reg(priv, 0);
2369 eeprom_write_reg(priv, EEPROM_BIT_SK);
James Ketrenos43f66a62005-03-25 12:31:53 -06002370}
2371
2372/* push a single bit down to the eeprom */
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002373static inline void eeprom_write_bit(struct ipw_priv *p, u8 bit)
James Ketrenos43f66a62005-03-25 12:31:53 -06002374{
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002375 int d = (bit ? EEPROM_BIT_DI : 0);
2376 eeprom_write_reg(p, EEPROM_BIT_CS | d);
2377 eeprom_write_reg(p, EEPROM_BIT_CS | d | EEPROM_BIT_SK);
James Ketrenos43f66a62005-03-25 12:31:53 -06002378}
2379
2380/* push an opcode followed by an address down to the eeprom */
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002381static void eeprom_op(struct ipw_priv *priv, u8 op, u8 addr)
James Ketrenos43f66a62005-03-25 12:31:53 -06002382{
2383 int i;
2384
2385 eeprom_cs(priv);
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002386 eeprom_write_bit(priv, 1);
2387 eeprom_write_bit(priv, op & 2);
2388 eeprom_write_bit(priv, op & 1);
2389 for (i = 7; i >= 0; i--) {
2390 eeprom_write_bit(priv, addr & (1 << i));
James Ketrenos43f66a62005-03-25 12:31:53 -06002391 }
2392}
2393
2394/* pull 16 bits off the eeprom, one bit at a time */
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002395static u16 eeprom_read_u16(struct ipw_priv *priv, u8 addr)
James Ketrenos43f66a62005-03-25 12:31:53 -06002396{
2397 int i;
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002398 u16 r = 0;
Jeff Garzikbf794512005-07-31 13:07:26 -04002399
James Ketrenos43f66a62005-03-25 12:31:53 -06002400 /* Send READ Opcode */
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002401 eeprom_op(priv, EEPROM_CMD_READ, addr);
James Ketrenos43f66a62005-03-25 12:31:53 -06002402
2403 /* Send dummy bit */
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002404 eeprom_write_reg(priv, EEPROM_BIT_CS);
James Ketrenos43f66a62005-03-25 12:31:53 -06002405
2406 /* Read the byte off the eeprom one bit at a time */
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002407 for (i = 0; i < 16; i++) {
James Ketrenos43f66a62005-03-25 12:31:53 -06002408 u32 data = 0;
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002409 eeprom_write_reg(priv, EEPROM_BIT_CS | EEPROM_BIT_SK);
2410 eeprom_write_reg(priv, EEPROM_BIT_CS);
2411 data = ipw_read_reg32(priv, FW_MEM_REG_EEPROM_ACCESS);
2412 r = (r << 1) | ((data & EEPROM_BIT_DO) ? 1 : 0);
James Ketrenos43f66a62005-03-25 12:31:53 -06002413 }
Jeff Garzikbf794512005-07-31 13:07:26 -04002414
James Ketrenos43f66a62005-03-25 12:31:53 -06002415 /* Send another dummy bit */
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002416 eeprom_write_reg(priv, 0);
James Ketrenos43f66a62005-03-25 12:31:53 -06002417 eeprom_disable_cs(priv);
Jeff Garzikbf794512005-07-31 13:07:26 -04002418
James Ketrenos43f66a62005-03-25 12:31:53 -06002419 return r;
2420}
2421
2422/* helper function for pulling the mac address out of the private */
2423/* data's copy of the eeprom data */
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002424static void eeprom_parse_mac(struct ipw_priv *priv, u8 * mac)
James Ketrenos43f66a62005-03-25 12:31:53 -06002425{
James Ketrenosafbf30a2005-08-25 00:05:33 -05002426 memcpy(mac, &priv->eeprom[EEPROM_MAC_ADDRESS], 6);
James Ketrenos43f66a62005-03-25 12:31:53 -06002427}
2428
2429/*
2430 * Either the device driver (i.e. the host) or the firmware can
2431 * load eeprom data into the designated region in SRAM. If neither
2432 * happens then the FW will shutdown with a fatal error.
2433 *
2434 * In order to signal the FW to load the EEPROM, the EEPROM_LOAD_DISABLE
2435 * bit needs region of shared SRAM needs to be non-zero.
2436 */
2437static void ipw_eeprom_init_sram(struct ipw_priv *priv)
2438{
2439 int i;
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002440 u16 *eeprom = (u16 *) priv->eeprom;
Jeff Garzikbf794512005-07-31 13:07:26 -04002441
James Ketrenos43f66a62005-03-25 12:31:53 -06002442 IPW_DEBUG_TRACE(">>\n");
2443
2444 /* read entire contents of eeprom into private buffer */
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002445 for (i = 0; i < 128; i++)
James Ketrenosa613bff2005-08-24 21:43:11 -05002446 eeprom[i] = le16_to_cpu(eeprom_read_u16(priv, (u8) i));
James Ketrenos43f66a62005-03-25 12:31:53 -06002447
Jeff Garzikbf794512005-07-31 13:07:26 -04002448 /*
2449 If the data looks correct, then copy it to our private
James Ketrenos43f66a62005-03-25 12:31:53 -06002450 copy. Otherwise let the firmware know to perform the operation
2451 on it's own
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002452 */
James Ketrenos43f66a62005-03-25 12:31:53 -06002453 if ((priv->eeprom + EEPROM_VERSION) != 0) {
2454 IPW_DEBUG_INFO("Writing EEPROM data into SRAM\n");
2455
2456 /* write the eeprom data to sram */
James Ketrenosb095c382005-08-24 22:04:42 -05002457 for (i = 0; i < IPW_EEPROM_IMAGE_SIZE; i++)
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002458 ipw_write8(priv, IPW_EEPROM_DATA + i, priv->eeprom[i]);
James Ketrenos43f66a62005-03-25 12:31:53 -06002459
2460 /* Do not load eeprom data on fatal error or suspend */
2461 ipw_write32(priv, IPW_EEPROM_LOAD_DISABLE, 0);
2462 } else {
2463 IPW_DEBUG_INFO("Enabling FW initializationg of SRAM\n");
2464
2465 /* Load eeprom data on fatal error or suspend */
2466 ipw_write32(priv, IPW_EEPROM_LOAD_DISABLE, 1);
2467 }
2468
2469 IPW_DEBUG_TRACE("<<\n");
2470}
2471
James Ketrenos43f66a62005-03-25 12:31:53 -06002472static inline void ipw_zero_memory(struct ipw_priv *priv, u32 start, u32 count)
2473{
2474 count >>= 2;
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002475 if (!count)
2476 return;
James Ketrenosb095c382005-08-24 22:04:42 -05002477 _ipw_write32(priv, IPW_AUTOINC_ADDR, start);
Jeff Garzikbf794512005-07-31 13:07:26 -04002478 while (count--)
James Ketrenosb095c382005-08-24 22:04:42 -05002479 _ipw_write32(priv, IPW_AUTOINC_DATA, 0);
James Ketrenos43f66a62005-03-25 12:31:53 -06002480}
2481
2482static inline void ipw_fw_dma_reset_command_blocks(struct ipw_priv *priv)
2483{
James Ketrenosb095c382005-08-24 22:04:42 -05002484 ipw_zero_memory(priv, IPW_SHARED_SRAM_DMA_CONTROL,
Jeff Garzikbf794512005-07-31 13:07:26 -04002485 CB_NUMBER_OF_ELEMENTS_SMALL *
James Ketrenos43f66a62005-03-25 12:31:53 -06002486 sizeof(struct command_block));
2487}
2488
2489static int ipw_fw_dma_enable(struct ipw_priv *priv)
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002490{ /* start dma engine but no transfers yet */
James Ketrenos43f66a62005-03-25 12:31:53 -06002491
2492 IPW_DEBUG_FW(">> : \n");
Jeff Garzikbf794512005-07-31 13:07:26 -04002493
James Ketrenos43f66a62005-03-25 12:31:53 -06002494 /* Start the dma */
2495 ipw_fw_dma_reset_command_blocks(priv);
Jeff Garzikbf794512005-07-31 13:07:26 -04002496
James Ketrenos43f66a62005-03-25 12:31:53 -06002497 /* Write CB base address */
James Ketrenosb095c382005-08-24 22:04:42 -05002498 ipw_write_reg32(priv, IPW_DMA_I_CB_BASE, IPW_SHARED_SRAM_DMA_CONTROL);
James Ketrenos43f66a62005-03-25 12:31:53 -06002499
2500 IPW_DEBUG_FW("<< : \n");
2501 return 0;
2502}
2503
2504static void ipw_fw_dma_abort(struct ipw_priv *priv)
2505{
2506 u32 control = 0;
2507
2508 IPW_DEBUG_FW(">> :\n");
Jeff Garzikbf794512005-07-31 13:07:26 -04002509
2510 //set the Stop and Abort bit
James Ketrenos43f66a62005-03-25 12:31:53 -06002511 control = DMA_CONTROL_SMALL_CB_CONST_VALUE | DMA_CB_STOP_AND_ABORT;
James Ketrenosb095c382005-08-24 22:04:42 -05002512 ipw_write_reg32(priv, IPW_DMA_I_DMA_CONTROL, control);
James Ketrenos43f66a62005-03-25 12:31:53 -06002513 priv->sram_desc.last_cb_index = 0;
Jeff Garzikbf794512005-07-31 13:07:26 -04002514
James Ketrenos43f66a62005-03-25 12:31:53 -06002515 IPW_DEBUG_FW("<< \n");
2516}
2517
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002518static int ipw_fw_dma_write_command_block(struct ipw_priv *priv, int index,
2519 struct command_block *cb)
James Ketrenos43f66a62005-03-25 12:31:53 -06002520{
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002521 u32 address =
James Ketrenosb095c382005-08-24 22:04:42 -05002522 IPW_SHARED_SRAM_DMA_CONTROL +
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002523 (sizeof(struct command_block) * index);
James Ketrenos43f66a62005-03-25 12:31:53 -06002524 IPW_DEBUG_FW(">> :\n");
2525
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002526 ipw_write_indirect(priv, address, (u8 *) cb,
2527 (int)sizeof(struct command_block));
James Ketrenos43f66a62005-03-25 12:31:53 -06002528
2529 IPW_DEBUG_FW("<< :\n");
2530 return 0;
2531
2532}
2533
2534static int ipw_fw_dma_kick(struct ipw_priv *priv)
2535{
2536 u32 control = 0;
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002537 u32 index = 0;
James Ketrenos43f66a62005-03-25 12:31:53 -06002538
2539 IPW_DEBUG_FW(">> :\n");
Jeff Garzikbf794512005-07-31 13:07:26 -04002540
James Ketrenos43f66a62005-03-25 12:31:53 -06002541 for (index = 0; index < priv->sram_desc.last_cb_index; index++)
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002542 ipw_fw_dma_write_command_block(priv, index,
2543 &priv->sram_desc.cb_list[index]);
James Ketrenos43f66a62005-03-25 12:31:53 -06002544
2545 /* Enable the DMA in the CSR register */
James Ketrenosb095c382005-08-24 22:04:42 -05002546 ipw_clear_bit(priv, IPW_RESET_REG,
2547 IPW_RESET_REG_MASTER_DISABLED |
2548 IPW_RESET_REG_STOP_MASTER);
Jeff Garzikbf794512005-07-31 13:07:26 -04002549
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002550 /* Set the Start bit. */
James Ketrenos43f66a62005-03-25 12:31:53 -06002551 control = DMA_CONTROL_SMALL_CB_CONST_VALUE | DMA_CB_START;
James Ketrenosb095c382005-08-24 22:04:42 -05002552 ipw_write_reg32(priv, IPW_DMA_I_DMA_CONTROL, control);
James Ketrenos43f66a62005-03-25 12:31:53 -06002553
2554 IPW_DEBUG_FW("<< :\n");
2555 return 0;
2556}
2557
2558static void ipw_fw_dma_dump_command_block(struct ipw_priv *priv)
2559{
2560 u32 address;
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002561 u32 register_value = 0;
2562 u32 cb_fields_address = 0;
James Ketrenos43f66a62005-03-25 12:31:53 -06002563
2564 IPW_DEBUG_FW(">> :\n");
James Ketrenosb095c382005-08-24 22:04:42 -05002565 address = ipw_read_reg32(priv, IPW_DMA_I_CURRENT_CB);
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002566 IPW_DEBUG_FW_INFO("Current CB is 0x%x \n", address);
James Ketrenos43f66a62005-03-25 12:31:53 -06002567
2568 /* Read the DMA Controlor register */
James Ketrenosb095c382005-08-24 22:04:42 -05002569 register_value = ipw_read_reg32(priv, IPW_DMA_I_DMA_CONTROL);
2570 IPW_DEBUG_FW_INFO("IPW_DMA_I_DMA_CONTROL is 0x%x \n", register_value);
James Ketrenos43f66a62005-03-25 12:31:53 -06002571
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002572 /* Print the CB values */
James Ketrenos43f66a62005-03-25 12:31:53 -06002573 cb_fields_address = address;
2574 register_value = ipw_read_reg32(priv, cb_fields_address);
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002575 IPW_DEBUG_FW_INFO("Current CB ControlField is 0x%x \n", register_value);
James Ketrenos43f66a62005-03-25 12:31:53 -06002576
2577 cb_fields_address += sizeof(u32);
2578 register_value = ipw_read_reg32(priv, cb_fields_address);
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002579 IPW_DEBUG_FW_INFO("Current CB Source Field is 0x%x \n", register_value);
James Ketrenos43f66a62005-03-25 12:31:53 -06002580
2581 cb_fields_address += sizeof(u32);
2582 register_value = ipw_read_reg32(priv, cb_fields_address);
2583 IPW_DEBUG_FW_INFO("Current CB Destination Field is 0x%x \n",
2584 register_value);
2585
2586 cb_fields_address += sizeof(u32);
2587 register_value = ipw_read_reg32(priv, cb_fields_address);
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002588 IPW_DEBUG_FW_INFO("Current CB Status Field is 0x%x \n", register_value);
James Ketrenos43f66a62005-03-25 12:31:53 -06002589
2590 IPW_DEBUG_FW(">> :\n");
2591}
2592
2593static int ipw_fw_dma_command_block_index(struct ipw_priv *priv)
2594{
2595 u32 current_cb_address = 0;
2596 u32 current_cb_index = 0;
2597
2598 IPW_DEBUG_FW("<< :\n");
James Ketrenosb095c382005-08-24 22:04:42 -05002599 current_cb_address = ipw_read_reg32(priv, IPW_DMA_I_CURRENT_CB);
Jeff Garzikbf794512005-07-31 13:07:26 -04002600
James Ketrenosb095c382005-08-24 22:04:42 -05002601 current_cb_index = (current_cb_address - IPW_SHARED_SRAM_DMA_CONTROL) /
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002602 sizeof(struct command_block);
Jeff Garzikbf794512005-07-31 13:07:26 -04002603
James Ketrenos43f66a62005-03-25 12:31:53 -06002604 IPW_DEBUG_FW_INFO("Current CB index 0x%x address = 0x%X \n",
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002605 current_cb_index, current_cb_address);
James Ketrenos43f66a62005-03-25 12:31:53 -06002606
2607 IPW_DEBUG_FW(">> :\n");
2608 return current_cb_index;
2609
2610}
2611
2612static int ipw_fw_dma_add_command_block(struct ipw_priv *priv,
2613 u32 src_address,
2614 u32 dest_address,
2615 u32 length,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002616 int interrupt_enabled, int is_last)
James Ketrenos43f66a62005-03-25 12:31:53 -06002617{
2618
Jeff Garzikbf794512005-07-31 13:07:26 -04002619 u32 control = CB_VALID | CB_SRC_LE | CB_DEST_LE | CB_SRC_AUTOINC |
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002620 CB_SRC_IO_GATED | CB_DEST_AUTOINC | CB_SRC_SIZE_LONG |
2621 CB_DEST_SIZE_LONG;
James Ketrenos43f66a62005-03-25 12:31:53 -06002622 struct command_block *cb;
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002623 u32 last_cb_element = 0;
James Ketrenos43f66a62005-03-25 12:31:53 -06002624
2625 IPW_DEBUG_FW_INFO("src_address=0x%x dest_address=0x%x length=0x%x\n",
2626 src_address, dest_address, length);
2627
2628 if (priv->sram_desc.last_cb_index >= CB_NUMBER_OF_ELEMENTS_SMALL)
2629 return -1;
2630
2631 last_cb_element = priv->sram_desc.last_cb_index;
2632 cb = &priv->sram_desc.cb_list[last_cb_element];
2633 priv->sram_desc.last_cb_index++;
2634
2635 /* Calculate the new CB control word */
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002636 if (interrupt_enabled)
James Ketrenos43f66a62005-03-25 12:31:53 -06002637 control |= CB_INT_ENABLED;
2638
2639 if (is_last)
2640 control |= CB_LAST_VALID;
Jeff Garzikbf794512005-07-31 13:07:26 -04002641
James Ketrenos43f66a62005-03-25 12:31:53 -06002642 control |= length;
2643
2644 /* Calculate the CB Element's checksum value */
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002645 cb->status = control ^ src_address ^ dest_address;
James Ketrenos43f66a62005-03-25 12:31:53 -06002646
2647 /* Copy the Source and Destination addresses */
2648 cb->dest_addr = dest_address;
2649 cb->source_addr = src_address;
2650
2651 /* Copy the Control Word last */
2652 cb->control = control;
2653
2654 return 0;
2655}
2656
2657static int ipw_fw_dma_add_buffer(struct ipw_priv *priv,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002658 u32 src_phys, u32 dest_address, u32 length)
James Ketrenos43f66a62005-03-25 12:31:53 -06002659{
2660 u32 bytes_left = length;
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002661 u32 src_offset = 0;
2662 u32 dest_offset = 0;
James Ketrenos43f66a62005-03-25 12:31:53 -06002663 int status = 0;
2664 IPW_DEBUG_FW(">> \n");
2665 IPW_DEBUG_FW_INFO("src_phys=0x%x dest_address=0x%x length=0x%x\n",
2666 src_phys, dest_address, length);
2667 while (bytes_left > CB_MAX_LENGTH) {
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002668 status = ipw_fw_dma_add_command_block(priv,
2669 src_phys + src_offset,
2670 dest_address +
2671 dest_offset,
2672 CB_MAX_LENGTH, 0, 0);
James Ketrenos43f66a62005-03-25 12:31:53 -06002673 if (status) {
2674 IPW_DEBUG_FW_INFO(": Failed\n");
2675 return -1;
Jeff Garzikbf794512005-07-31 13:07:26 -04002676 } else
James Ketrenos43f66a62005-03-25 12:31:53 -06002677 IPW_DEBUG_FW_INFO(": Added new cb\n");
2678
2679 src_offset += CB_MAX_LENGTH;
2680 dest_offset += CB_MAX_LENGTH;
2681 bytes_left -= CB_MAX_LENGTH;
2682 }
2683
2684 /* add the buffer tail */
2685 if (bytes_left > 0) {
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002686 status =
2687 ipw_fw_dma_add_command_block(priv, src_phys + src_offset,
2688 dest_address + dest_offset,
2689 bytes_left, 0, 0);
James Ketrenos43f66a62005-03-25 12:31:53 -06002690 if (status) {
2691 IPW_DEBUG_FW_INFO(": Failed on the buffer tail\n");
2692 return -1;
Jeff Garzikbf794512005-07-31 13:07:26 -04002693 } else
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002694 IPW_DEBUG_FW_INFO
2695 (": Adding new cb - the buffer tail\n");
James Ketrenos43f66a62005-03-25 12:31:53 -06002696 }
Jeff Garzikbf794512005-07-31 13:07:26 -04002697
James Ketrenos43f66a62005-03-25 12:31:53 -06002698 IPW_DEBUG_FW("<< \n");
2699 return 0;
2700}
2701
2702static int ipw_fw_dma_wait(struct ipw_priv *priv)
2703{
2704 u32 current_index = 0;
2705 u32 watchdog = 0;
2706
2707 IPW_DEBUG_FW(">> : \n");
2708
2709 current_index = ipw_fw_dma_command_block_index(priv);
Jeff Garzikbf794512005-07-31 13:07:26 -04002710 IPW_DEBUG_FW_INFO("sram_desc.last_cb_index:0x%8X\n",
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002711 (int)priv->sram_desc.last_cb_index);
James Ketrenos43f66a62005-03-25 12:31:53 -06002712
2713 while (current_index < priv->sram_desc.last_cb_index) {
2714 udelay(50);
2715 current_index = ipw_fw_dma_command_block_index(priv);
2716
2717 watchdog++;
2718
2719 if (watchdog > 400) {
2720 IPW_DEBUG_FW_INFO("Timeout\n");
2721 ipw_fw_dma_dump_command_block(priv);
2722 ipw_fw_dma_abort(priv);
2723 return -1;
2724 }
2725 }
2726
2727 ipw_fw_dma_abort(priv);
2728
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002729 /*Disable the DMA in the CSR register */
James Ketrenosb095c382005-08-24 22:04:42 -05002730 ipw_set_bit(priv, IPW_RESET_REG,
2731 IPW_RESET_REG_MASTER_DISABLED | IPW_RESET_REG_STOP_MASTER);
James Ketrenos43f66a62005-03-25 12:31:53 -06002732
2733 IPW_DEBUG_FW("<< dmaWaitSync \n");
2734 return 0;
2735}
2736
Jeff Garzikbf794512005-07-31 13:07:26 -04002737static void ipw_remove_current_network(struct ipw_priv *priv)
James Ketrenos43f66a62005-03-25 12:31:53 -06002738{
2739 struct list_head *element, *safe;
Jeff Garzikbf794512005-07-31 13:07:26 -04002740 struct ieee80211_network *network = NULL;
James Ketrenosa613bff2005-08-24 21:43:11 -05002741 unsigned long flags;
2742
2743 spin_lock_irqsave(&priv->ieee->lock, flags);
James Ketrenos43f66a62005-03-25 12:31:53 -06002744 list_for_each_safe(element, safe, &priv->ieee->network_list) {
2745 network = list_entry(element, struct ieee80211_network, list);
2746 if (!memcmp(network->bssid, priv->bssid, ETH_ALEN)) {
2747 list_del(element);
Jeff Garzikbf794512005-07-31 13:07:26 -04002748 list_add_tail(&network->list,
James Ketrenos43f66a62005-03-25 12:31:53 -06002749 &priv->ieee->network_free_list);
2750 }
2751 }
James Ketrenosa613bff2005-08-24 21:43:11 -05002752 spin_unlock_irqrestore(&priv->ieee->lock, flags);
James Ketrenos43f66a62005-03-25 12:31:53 -06002753}
2754
2755/**
Jeff Garzikbf794512005-07-31 13:07:26 -04002756 * Check that card is still alive.
James Ketrenos43f66a62005-03-25 12:31:53 -06002757 * Reads debug register from domain0.
2758 * If card is present, pre-defined value should
2759 * be found there.
Jeff Garzikbf794512005-07-31 13:07:26 -04002760 *
James Ketrenos43f66a62005-03-25 12:31:53 -06002761 * @param priv
2762 * @return 1 if card is present, 0 otherwise
2763 */
2764static inline int ipw_alive(struct ipw_priv *priv)
2765{
2766 return ipw_read32(priv, 0x90) == 0xd55555d5;
2767}
2768
2769static inline int ipw_poll_bit(struct ipw_priv *priv, u32 addr, u32 mask,
2770 int timeout)
2771{
2772 int i = 0;
2773
2774 do {
Jeff Garzikbf794512005-07-31 13:07:26 -04002775 if ((ipw_read32(priv, addr) & mask) == mask)
James Ketrenos43f66a62005-03-25 12:31:53 -06002776 return i;
2777 mdelay(10);
2778 i += 10;
2779 } while (i < timeout);
Jeff Garzikbf794512005-07-31 13:07:26 -04002780
James Ketrenos43f66a62005-03-25 12:31:53 -06002781 return -ETIME;
2782}
2783
Jeff Garzikbf794512005-07-31 13:07:26 -04002784/* These functions load the firmware and micro code for the operation of
James Ketrenos43f66a62005-03-25 12:31:53 -06002785 * the ipw hardware. It assumes the buffer has all the bits for the
2786 * image and the caller is handling the memory allocation and clean up.
2787 */
2788
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002789static int ipw_stop_master(struct ipw_priv *priv)
James Ketrenos43f66a62005-03-25 12:31:53 -06002790{
2791 int rc;
Jeff Garzikbf794512005-07-31 13:07:26 -04002792
James Ketrenos43f66a62005-03-25 12:31:53 -06002793 IPW_DEBUG_TRACE(">> \n");
2794 /* stop master. typical delay - 0 */
James Ketrenosb095c382005-08-24 22:04:42 -05002795 ipw_set_bit(priv, IPW_RESET_REG, IPW_RESET_REG_STOP_MASTER);
James Ketrenos43f66a62005-03-25 12:31:53 -06002796
James Ketrenosb095c382005-08-24 22:04:42 -05002797 rc = ipw_poll_bit(priv, IPW_RESET_REG,
2798 IPW_RESET_REG_MASTER_DISABLED, 100);
James Ketrenos43f66a62005-03-25 12:31:53 -06002799 if (rc < 0) {
2800 IPW_ERROR("stop master failed in 10ms\n");
2801 return -1;
2802 }
2803
2804 IPW_DEBUG_INFO("stop master %dms\n", rc);
2805
2806 return rc;
2807}
2808
2809static void ipw_arc_release(struct ipw_priv *priv)
2810{
2811 IPW_DEBUG_TRACE(">> \n");
2812 mdelay(5);
2813
James Ketrenosb095c382005-08-24 22:04:42 -05002814 ipw_clear_bit(priv, IPW_RESET_REG, CBD_RESET_REG_PRINCETON_RESET);
James Ketrenos43f66a62005-03-25 12:31:53 -06002815
2816 /* no one knows timing, for safety add some delay */
2817 mdelay(5);
2818}
2819
2820struct fw_header {
2821 u32 version;
2822 u32 mode;
2823};
2824
2825struct fw_chunk {
2826 u32 address;
2827 u32 length;
2828};
2829
2830#define IPW_FW_MAJOR_VERSION 2
James Ketrenosb095c382005-08-24 22:04:42 -05002831#define IPW_FW_MINOR_VERSION 3
James Ketrenos43f66a62005-03-25 12:31:53 -06002832
2833#define IPW_FW_MINOR(x) ((x & 0xff) >> 8)
2834#define IPW_FW_MAJOR(x) (x & 0xff)
2835
James Ketrenosafbf30a2005-08-25 00:05:33 -05002836#define IPW_FW_VERSION ((IPW_FW_MINOR_VERSION << 8) | IPW_FW_MAJOR_VERSION)
James Ketrenos43f66a62005-03-25 12:31:53 -06002837
2838#define IPW_FW_PREFIX "ipw-" __stringify(IPW_FW_MAJOR_VERSION) \
2839"." __stringify(IPW_FW_MINOR_VERSION) "-"
2840
2841#if IPW_FW_MAJOR_VERSION >= 2 && IPW_FW_MINOR_VERSION > 0
2842#define IPW_FW_NAME(x) IPW_FW_PREFIX "" x ".fw"
2843#else
2844#define IPW_FW_NAME(x) "ipw2200_" x ".fw"
2845#endif
2846
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002847static int ipw_load_ucode(struct ipw_priv *priv, u8 * data, size_t len)
James Ketrenos43f66a62005-03-25 12:31:53 -06002848{
2849 int rc = 0, i, addr;
2850 u8 cr = 0;
2851 u16 *image;
2852
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002853 image = (u16 *) data;
Jeff Garzikbf794512005-07-31 13:07:26 -04002854
James Ketrenos43f66a62005-03-25 12:31:53 -06002855 IPW_DEBUG_TRACE(">> \n");
2856
2857 rc = ipw_stop_master(priv);
2858
2859 if (rc < 0)
2860 return rc;
Jeff Garzikbf794512005-07-31 13:07:26 -04002861
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002862// spin_lock_irqsave(&priv->lock, flags);
Jeff Garzikbf794512005-07-31 13:07:26 -04002863
James Ketrenosb095c382005-08-24 22:04:42 -05002864 for (addr = IPW_SHARED_LOWER_BOUND;
2865 addr < IPW_REGISTER_DOMAIN1_END; addr += 4) {
James Ketrenos43f66a62005-03-25 12:31:53 -06002866 ipw_write32(priv, addr, 0);
2867 }
2868
2869 /* no ucode (yet) */
2870 memset(&priv->dino_alive, 0, sizeof(priv->dino_alive));
2871 /* destroy DMA queues */
2872 /* reset sequence */
2873
James Ketrenosb095c382005-08-24 22:04:42 -05002874 ipw_write_reg32(priv, IPW_MEM_HALT_AND_RESET, IPW_BIT_HALT_RESET_ON);
James Ketrenos43f66a62005-03-25 12:31:53 -06002875 ipw_arc_release(priv);
James Ketrenosb095c382005-08-24 22:04:42 -05002876 ipw_write_reg32(priv, IPW_MEM_HALT_AND_RESET, IPW_BIT_HALT_RESET_OFF);
James Ketrenos43f66a62005-03-25 12:31:53 -06002877 mdelay(1);
2878
2879 /* reset PHY */
James Ketrenosb095c382005-08-24 22:04:42 -05002880 ipw_write_reg32(priv, IPW_INTERNAL_CMD_EVENT, IPW_BASEBAND_POWER_DOWN);
James Ketrenos43f66a62005-03-25 12:31:53 -06002881 mdelay(1);
Jeff Garzikbf794512005-07-31 13:07:26 -04002882
James Ketrenosb095c382005-08-24 22:04:42 -05002883 ipw_write_reg32(priv, IPW_INTERNAL_CMD_EVENT, 0);
James Ketrenos43f66a62005-03-25 12:31:53 -06002884 mdelay(1);
Jeff Garzikbf794512005-07-31 13:07:26 -04002885
James Ketrenos43f66a62005-03-25 12:31:53 -06002886 /* enable ucode store */
2887 ipw_write_reg8(priv, DINO_CONTROL_REG, 0x0);
2888 ipw_write_reg8(priv, DINO_CONTROL_REG, DINO_ENABLE_CS);
2889 mdelay(1);
2890
2891 /* write ucode */
2892 /**
2893 * @bug
2894 * Do NOT set indirect address register once and then
2895 * store data to indirect data register in the loop.
2896 * It seems very reasonable, but in this case DINO do not
2897 * accept ucode. It is essential to set address each time.
2898 */
2899 /* load new ipw uCode */
2900 for (i = 0; i < len / 2; i++)
James Ketrenosb095c382005-08-24 22:04:42 -05002901 ipw_write_reg16(priv, IPW_BASEBAND_CONTROL_STORE,
James Ketrenosa613bff2005-08-24 21:43:11 -05002902 cpu_to_le16(image[i]));
James Ketrenos43f66a62005-03-25 12:31:53 -06002903
James Ketrenos43f66a62005-03-25 12:31:53 -06002904 /* enable DINO */
James Ketrenosb095c382005-08-24 22:04:42 -05002905 ipw_write_reg8(priv, IPW_BASEBAND_CONTROL_STATUS, 0);
2906 ipw_write_reg8(priv, IPW_BASEBAND_CONTROL_STATUS, DINO_ENABLE_SYSTEM);
James Ketrenos43f66a62005-03-25 12:31:53 -06002907
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002908 /* this is where the igx / win driver deveates from the VAP driver. */
James Ketrenos43f66a62005-03-25 12:31:53 -06002909
2910 /* wait for alive response */
2911 for (i = 0; i < 100; i++) {
2912 /* poll for incoming data */
James Ketrenosb095c382005-08-24 22:04:42 -05002913 cr = ipw_read_reg8(priv, IPW_BASEBAND_CONTROL_STATUS);
James Ketrenos43f66a62005-03-25 12:31:53 -06002914 if (cr & DINO_RXFIFO_DATA)
2915 break;
2916 mdelay(1);
2917 }
2918
2919 if (cr & DINO_RXFIFO_DATA) {
2920 /* alive_command_responce size is NOT multiple of 4 */
2921 u32 response_buffer[(sizeof(priv->dino_alive) + 3) / 4];
Jeff Garzikbf794512005-07-31 13:07:26 -04002922
2923 for (i = 0; i < ARRAY_SIZE(response_buffer); i++)
James Ketrenos43f66a62005-03-25 12:31:53 -06002924 response_buffer[i] =
James Ketrenosa613bff2005-08-24 21:43:11 -05002925 le32_to_cpu(ipw_read_reg32(priv,
James Ketrenosb095c382005-08-24 22:04:42 -05002926 IPW_BASEBAND_RX_FIFO_READ));
James Ketrenos43f66a62005-03-25 12:31:53 -06002927 memcpy(&priv->dino_alive, response_buffer,
2928 sizeof(priv->dino_alive));
2929 if (priv->dino_alive.alive_command == 1
2930 && priv->dino_alive.ucode_valid == 1) {
2931 rc = 0;
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002932 IPW_DEBUG_INFO
2933 ("Microcode OK, rev. %d (0x%x) dev. %d (0x%x) "
2934 "of %02d/%02d/%02d %02d:%02d\n",
2935 priv->dino_alive.software_revision,
2936 priv->dino_alive.software_revision,
2937 priv->dino_alive.device_identifier,
2938 priv->dino_alive.device_identifier,
2939 priv->dino_alive.time_stamp[0],
2940 priv->dino_alive.time_stamp[1],
2941 priv->dino_alive.time_stamp[2],
2942 priv->dino_alive.time_stamp[3],
2943 priv->dino_alive.time_stamp[4]);
James Ketrenos43f66a62005-03-25 12:31:53 -06002944 } else {
2945 IPW_DEBUG_INFO("Microcode is not alive\n");
2946 rc = -EINVAL;
2947 }
2948 } else {
2949 IPW_DEBUG_INFO("No alive response from DINO\n");
2950 rc = -ETIME;
2951 }
2952
2953 /* disable DINO, otherwise for some reason
2954 firmware have problem getting alive resp. */
James Ketrenosb095c382005-08-24 22:04:42 -05002955 ipw_write_reg8(priv, IPW_BASEBAND_CONTROL_STATUS, 0);
James Ketrenos43f66a62005-03-25 12:31:53 -06002956
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002957// spin_unlock_irqrestore(&priv->lock, flags);
James Ketrenos43f66a62005-03-25 12:31:53 -06002958
2959 return rc;
2960}
2961
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002962static int ipw_load_firmware(struct ipw_priv *priv, u8 * data, size_t len)
James Ketrenos43f66a62005-03-25 12:31:53 -06002963{
2964 int rc = -1;
2965 int offset = 0;
2966 struct fw_chunk *chunk;
2967 dma_addr_t shared_phys;
2968 u8 *shared_virt;
2969
2970 IPW_DEBUG_TRACE("<< : \n");
2971 shared_virt = pci_alloc_consistent(priv->pci_dev, len, &shared_phys);
2972
2973 if (!shared_virt)
2974 return -ENOMEM;
2975
2976 memmove(shared_virt, data, len);
2977
2978 /* Start the Dma */
2979 rc = ipw_fw_dma_enable(priv);
2980
2981 if (priv->sram_desc.last_cb_index > 0) {
2982 /* the DMA is already ready this would be a bug. */
2983 BUG();
2984 goto out;
2985 }
2986
2987 do {
2988 chunk = (struct fw_chunk *)(data + offset);
2989 offset += sizeof(struct fw_chunk);
2990 /* build DMA packet and queue up for sending */
Jeff Garzikbf794512005-07-31 13:07:26 -04002991 /* dma to chunk->address, the chunk->length bytes from data +
James Ketrenos43f66a62005-03-25 12:31:53 -06002992 * offeset*/
2993 /* Dma loading */
2994 rc = ipw_fw_dma_add_buffer(priv, shared_phys + offset,
James Ketrenosa613bff2005-08-24 21:43:11 -05002995 le32_to_cpu(chunk->address),
2996 le32_to_cpu(chunk->length));
James Ketrenos43f66a62005-03-25 12:31:53 -06002997 if (rc) {
2998 IPW_DEBUG_INFO("dmaAddBuffer Failed\n");
2999 goto out;
3000 }
Jeff Garzikbf794512005-07-31 13:07:26 -04003001
James Ketrenosa613bff2005-08-24 21:43:11 -05003002 offset += le32_to_cpu(chunk->length);
James Ketrenos43f66a62005-03-25 12:31:53 -06003003 } while (offset < len);
3004
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003005 /* Run the DMA and wait for the answer */
James Ketrenos43f66a62005-03-25 12:31:53 -06003006 rc = ipw_fw_dma_kick(priv);
3007 if (rc) {
3008 IPW_ERROR("dmaKick Failed\n");
3009 goto out;
3010 }
3011
3012 rc = ipw_fw_dma_wait(priv);
3013 if (rc) {
3014 IPW_ERROR("dmaWaitSync Failed\n");
3015 goto out;
3016 }
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003017 out:
3018 pci_free_consistent(priv->pci_dev, len, shared_virt, shared_phys);
James Ketrenos43f66a62005-03-25 12:31:53 -06003019 return rc;
3020}
3021
3022/* stop nic */
3023static int ipw_stop_nic(struct ipw_priv *priv)
3024{
3025 int rc = 0;
3026
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003027 /* stop */
James Ketrenosb095c382005-08-24 22:04:42 -05003028 ipw_write32(priv, IPW_RESET_REG, IPW_RESET_REG_STOP_MASTER);
Jeff Garzikbf794512005-07-31 13:07:26 -04003029
James Ketrenosb095c382005-08-24 22:04:42 -05003030 rc = ipw_poll_bit(priv, IPW_RESET_REG,
3031 IPW_RESET_REG_MASTER_DISABLED, 500);
James Ketrenos43f66a62005-03-25 12:31:53 -06003032 if (rc < 0) {
3033 IPW_ERROR("wait for reg master disabled failed\n");
3034 return rc;
Jeff Garzikbf794512005-07-31 13:07:26 -04003035 }
James Ketrenos43f66a62005-03-25 12:31:53 -06003036
James Ketrenosb095c382005-08-24 22:04:42 -05003037 ipw_set_bit(priv, IPW_RESET_REG, CBD_RESET_REG_PRINCETON_RESET);
Jeff Garzikbf794512005-07-31 13:07:26 -04003038
James Ketrenos43f66a62005-03-25 12:31:53 -06003039 return rc;
3040}
3041
3042static void ipw_start_nic(struct ipw_priv *priv)
3043{
3044 IPW_DEBUG_TRACE(">>\n");
3045
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003046 /* prvHwStartNic release ARC */
James Ketrenosb095c382005-08-24 22:04:42 -05003047 ipw_clear_bit(priv, IPW_RESET_REG,
3048 IPW_RESET_REG_MASTER_DISABLED |
3049 IPW_RESET_REG_STOP_MASTER |
James Ketrenos43f66a62005-03-25 12:31:53 -06003050 CBD_RESET_REG_PRINCETON_RESET);
Jeff Garzikbf794512005-07-31 13:07:26 -04003051
James Ketrenos43f66a62005-03-25 12:31:53 -06003052 /* enable power management */
James Ketrenosb095c382005-08-24 22:04:42 -05003053 ipw_set_bit(priv, IPW_GP_CNTRL_RW,
3054 IPW_GP_CNTRL_BIT_HOST_ALLOWS_STANDBY);
James Ketrenos43f66a62005-03-25 12:31:53 -06003055
3056 IPW_DEBUG_TRACE("<<\n");
3057}
Jeff Garzikbf794512005-07-31 13:07:26 -04003058
James Ketrenos43f66a62005-03-25 12:31:53 -06003059static int ipw_init_nic(struct ipw_priv *priv)
3060{
3061 int rc;
3062
3063 IPW_DEBUG_TRACE(">>\n");
Jeff Garzikbf794512005-07-31 13:07:26 -04003064 /* reset */
James Ketrenos43f66a62005-03-25 12:31:53 -06003065 /*prvHwInitNic */
3066 /* set "initialization complete" bit to move adapter to D0 state */
James Ketrenosb095c382005-08-24 22:04:42 -05003067 ipw_set_bit(priv, IPW_GP_CNTRL_RW, IPW_GP_CNTRL_BIT_INIT_DONE);
James Ketrenos43f66a62005-03-25 12:31:53 -06003068
3069 /* low-level PLL activation */
James Ketrenosb095c382005-08-24 22:04:42 -05003070 ipw_write32(priv, IPW_READ_INT_REGISTER,
3071 IPW_BIT_INT_HOST_SRAM_READ_INT_REGISTER);
James Ketrenos43f66a62005-03-25 12:31:53 -06003072
3073 /* wait for clock stabilization */
James Ketrenosb095c382005-08-24 22:04:42 -05003074 rc = ipw_poll_bit(priv, IPW_GP_CNTRL_RW,
3075 IPW_GP_CNTRL_BIT_CLOCK_READY, 250);
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003076 if (rc < 0)
James Ketrenos43f66a62005-03-25 12:31:53 -06003077 IPW_DEBUG_INFO("FAILED wait for clock stablization\n");
3078
3079 /* assert SW reset */
James Ketrenosb095c382005-08-24 22:04:42 -05003080 ipw_set_bit(priv, IPW_RESET_REG, IPW_RESET_REG_SW_RESET);
James Ketrenos43f66a62005-03-25 12:31:53 -06003081
3082 udelay(10);
3083
3084 /* set "initialization complete" bit to move adapter to D0 state */
James Ketrenosb095c382005-08-24 22:04:42 -05003085 ipw_set_bit(priv, IPW_GP_CNTRL_RW, IPW_GP_CNTRL_BIT_INIT_DONE);
James Ketrenos43f66a62005-03-25 12:31:53 -06003086
3087 IPW_DEBUG_TRACE(">>\n");
3088 return 0;
3089}
3090
Jeff Garzikbf794512005-07-31 13:07:26 -04003091/* Call this function from process context, it will sleep in request_firmware.
James Ketrenos43f66a62005-03-25 12:31:53 -06003092 * Probe is an ok place to call this from.
3093 */
3094static int ipw_reset_nic(struct ipw_priv *priv)
3095{
3096 int rc = 0;
James Ketrenosa613bff2005-08-24 21:43:11 -05003097 unsigned long flags;
James Ketrenos43f66a62005-03-25 12:31:53 -06003098
3099 IPW_DEBUG_TRACE(">>\n");
Jeff Garzikbf794512005-07-31 13:07:26 -04003100
James Ketrenos43f66a62005-03-25 12:31:53 -06003101 rc = ipw_init_nic(priv);
Jeff Garzikbf794512005-07-31 13:07:26 -04003102
James Ketrenosa613bff2005-08-24 21:43:11 -05003103 spin_lock_irqsave(&priv->lock, flags);
James Ketrenos43f66a62005-03-25 12:31:53 -06003104 /* Clear the 'host command active' bit... */
3105 priv->status &= ~STATUS_HCMD_ACTIVE;
3106 wake_up_interruptible(&priv->wait_command_queue);
James Ketrenosafbf30a2005-08-25 00:05:33 -05003107 priv->status &= ~(STATUS_SCANNING | STATUS_SCAN_ABORTING);
3108 wake_up_interruptible(&priv->wait_state);
James Ketrenosa613bff2005-08-24 21:43:11 -05003109 spin_unlock_irqrestore(&priv->lock, flags);
James Ketrenos43f66a62005-03-25 12:31:53 -06003110
3111 IPW_DEBUG_TRACE("<<\n");
3112 return rc;
Jeff Garzikbf794512005-07-31 13:07:26 -04003113}
James Ketrenos43f66a62005-03-25 12:31:53 -06003114
Jeff Garzikbf794512005-07-31 13:07:26 -04003115static int ipw_get_fw(struct ipw_priv *priv,
James Ketrenos43f66a62005-03-25 12:31:53 -06003116 const struct firmware **fw, const char *name)
3117{
3118 struct fw_header *header;
3119 int rc;
3120
3121 /* ask firmware_class module to get the boot firmware off disk */
3122 rc = request_firmware(fw, name, &priv->pci_dev->dev);
3123 if (rc < 0) {
3124 IPW_ERROR("%s load failed: Reason %d\n", name, rc);
3125 return rc;
Jeff Garzikbf794512005-07-31 13:07:26 -04003126 }
James Ketrenos43f66a62005-03-25 12:31:53 -06003127
3128 header = (struct fw_header *)(*fw)->data;
James Ketrenosa613bff2005-08-24 21:43:11 -05003129 if (IPW_FW_MAJOR(le32_to_cpu(header->version)) != IPW_FW_MAJOR_VERSION) {
James Ketrenos43f66a62005-03-25 12:31:53 -06003130 IPW_ERROR("'%s' firmware version not compatible (%d != %d)\n",
3131 name,
James Ketrenosa613bff2005-08-24 21:43:11 -05003132 IPW_FW_MAJOR(le32_to_cpu(header->version)),
3133 IPW_FW_MAJOR_VERSION);
James Ketrenos43f66a62005-03-25 12:31:53 -06003134 return -EINVAL;
3135 }
3136
Jiri Bencaaa4d302005-06-07 14:58:41 +02003137 IPW_DEBUG_INFO("Loading firmware '%s' file v%d.%d (%zd bytes)\n",
James Ketrenos43f66a62005-03-25 12:31:53 -06003138 name,
James Ketrenosa613bff2005-08-24 21:43:11 -05003139 IPW_FW_MAJOR(le32_to_cpu(header->version)),
3140 IPW_FW_MINOR(le32_to_cpu(header->version)),
James Ketrenos43f66a62005-03-25 12:31:53 -06003141 (*fw)->size - sizeof(struct fw_header));
3142 return 0;
3143}
3144
James Ketrenosb095c382005-08-24 22:04:42 -05003145#define IPW_RX_BUF_SIZE (3000)
James Ketrenos43f66a62005-03-25 12:31:53 -06003146
3147static inline void ipw_rx_queue_reset(struct ipw_priv *priv,
3148 struct ipw_rx_queue *rxq)
3149{
3150 unsigned long flags;
3151 int i;
3152
3153 spin_lock_irqsave(&rxq->lock, flags);
3154
3155 INIT_LIST_HEAD(&rxq->rx_free);
3156 INIT_LIST_HEAD(&rxq->rx_used);
3157
3158 /* Fill the rx_used queue with _all_ of the Rx buffers */
3159 for (i = 0; i < RX_FREE_BUFFERS + RX_QUEUE_SIZE; i++) {
3160 /* In the reset function, these buffers may have been allocated
3161 * to an SKB, so we need to unmap and free potential storage */
3162 if (rxq->pool[i].skb != NULL) {
3163 pci_unmap_single(priv->pci_dev, rxq->pool[i].dma_addr,
James Ketrenosb095c382005-08-24 22:04:42 -05003164 IPW_RX_BUF_SIZE, PCI_DMA_FROMDEVICE);
James Ketrenos43f66a62005-03-25 12:31:53 -06003165 dev_kfree_skb(rxq->pool[i].skb);
James Ketrenosa613bff2005-08-24 21:43:11 -05003166 rxq->pool[i].skb = NULL;
James Ketrenos43f66a62005-03-25 12:31:53 -06003167 }
3168 list_add_tail(&rxq->pool[i].list, &rxq->rx_used);
3169 }
Jeff Garzikbf794512005-07-31 13:07:26 -04003170
James Ketrenos43f66a62005-03-25 12:31:53 -06003171 /* Set us so that we have processed and used all buffers, but have
3172 * not restocked the Rx queue with fresh buffers */
3173 rxq->read = rxq->write = 0;
3174 rxq->processed = RX_QUEUE_SIZE - 1;
3175 rxq->free_count = 0;
3176 spin_unlock_irqrestore(&rxq->lock, flags);
3177}
3178
3179#ifdef CONFIG_PM
3180static int fw_loaded = 0;
3181static const struct firmware *bootfw = NULL;
3182static const struct firmware *firmware = NULL;
3183static const struct firmware *ucode = NULL;
James Ketrenosafbf30a2005-08-25 00:05:33 -05003184
3185static void free_firmware(void)
3186{
3187 if (fw_loaded) {
3188 release_firmware(bootfw);
3189 release_firmware(ucode);
3190 release_firmware(firmware);
3191 bootfw = ucode = firmware = NULL;
3192 fw_loaded = 0;
3193 }
3194}
3195#else
3196#define free_firmware() do {} while (0)
James Ketrenos43f66a62005-03-25 12:31:53 -06003197#endif
3198
3199static int ipw_load(struct ipw_priv *priv)
3200{
3201#ifndef CONFIG_PM
3202 const struct firmware *bootfw = NULL;
3203 const struct firmware *firmware = NULL;
3204 const struct firmware *ucode = NULL;
3205#endif
3206 int rc = 0, retries = 3;
3207
3208#ifdef CONFIG_PM
3209 if (!fw_loaded) {
3210#endif
3211 rc = ipw_get_fw(priv, &bootfw, IPW_FW_NAME("boot"));
Jeff Garzikbf794512005-07-31 13:07:26 -04003212 if (rc)
James Ketrenos43f66a62005-03-25 12:31:53 -06003213 goto error;
Jeff Garzikbf794512005-07-31 13:07:26 -04003214
James Ketrenos43f66a62005-03-25 12:31:53 -06003215 switch (priv->ieee->iw_mode) {
3216 case IW_MODE_ADHOC:
Jeff Garzikbf794512005-07-31 13:07:26 -04003217 rc = ipw_get_fw(priv, &ucode,
James Ketrenos43f66a62005-03-25 12:31:53 -06003218 IPW_FW_NAME("ibss_ucode"));
Jeff Garzikbf794512005-07-31 13:07:26 -04003219 if (rc)
James Ketrenos43f66a62005-03-25 12:31:53 -06003220 goto error;
Jeff Garzikbf794512005-07-31 13:07:26 -04003221
James Ketrenos43f66a62005-03-25 12:31:53 -06003222 rc = ipw_get_fw(priv, &firmware, IPW_FW_NAME("ibss"));
3223 break;
Jeff Garzikbf794512005-07-31 13:07:26 -04003224
James Ketrenosb095c382005-08-24 22:04:42 -05003225#ifdef CONFIG_IPW2200_MONITOR
James Ketrenos43f66a62005-03-25 12:31:53 -06003226 case IW_MODE_MONITOR:
Jeff Garzikbf794512005-07-31 13:07:26 -04003227 rc = ipw_get_fw(priv, &ucode,
James Ketrenosea2b26e2005-08-24 21:25:16 -05003228 IPW_FW_NAME("sniffer_ucode"));
Jeff Garzikbf794512005-07-31 13:07:26 -04003229 if (rc)
James Ketrenos43f66a62005-03-25 12:31:53 -06003230 goto error;
Jeff Garzikbf794512005-07-31 13:07:26 -04003231
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003232 rc = ipw_get_fw(priv, &firmware,
3233 IPW_FW_NAME("sniffer"));
James Ketrenos43f66a62005-03-25 12:31:53 -06003234 break;
3235#endif
3236 case IW_MODE_INFRA:
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003237 rc = ipw_get_fw(priv, &ucode, IPW_FW_NAME("bss_ucode"));
Jeff Garzikbf794512005-07-31 13:07:26 -04003238 if (rc)
James Ketrenos43f66a62005-03-25 12:31:53 -06003239 goto error;
Jeff Garzikbf794512005-07-31 13:07:26 -04003240
James Ketrenos43f66a62005-03-25 12:31:53 -06003241 rc = ipw_get_fw(priv, &firmware, IPW_FW_NAME("bss"));
3242 break;
Jeff Garzikbf794512005-07-31 13:07:26 -04003243
James Ketrenos43f66a62005-03-25 12:31:53 -06003244 default:
3245 rc = -EINVAL;
3246 }
3247
Jeff Garzikbf794512005-07-31 13:07:26 -04003248 if (rc)
James Ketrenos43f66a62005-03-25 12:31:53 -06003249 goto error;
3250
3251#ifdef CONFIG_PM
3252 fw_loaded = 1;
3253 }
3254#endif
3255
3256 if (!priv->rxq)
3257 priv->rxq = ipw_rx_queue_alloc(priv);
3258 else
3259 ipw_rx_queue_reset(priv, priv->rxq);
3260 if (!priv->rxq) {
3261 IPW_ERROR("Unable to initialize Rx queue\n");
3262 goto error;
3263 }
3264
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003265 retry:
James Ketrenos43f66a62005-03-25 12:31:53 -06003266 /* Ensure interrupts are disabled */
James Ketrenosb095c382005-08-24 22:04:42 -05003267 ipw_write32(priv, IPW_INTA_MASK_R, ~IPW_INTA_MASK_ALL);
James Ketrenos43f66a62005-03-25 12:31:53 -06003268 priv->status &= ~STATUS_INT_ENABLED;
3269
3270 /* ack pending interrupts */
James Ketrenosb095c382005-08-24 22:04:42 -05003271 ipw_write32(priv, IPW_INTA_RW, IPW_INTA_MASK_ALL);
Jeff Garzikbf794512005-07-31 13:07:26 -04003272
James Ketrenos43f66a62005-03-25 12:31:53 -06003273 ipw_stop_nic(priv);
3274
3275 rc = ipw_reset_nic(priv);
3276 if (rc) {
3277 IPW_ERROR("Unable to reset NIC\n");
3278 goto error;
3279 }
3280
James Ketrenosb095c382005-08-24 22:04:42 -05003281 ipw_zero_memory(priv, IPW_NIC_SRAM_LOWER_BOUND,
3282 IPW_NIC_SRAM_UPPER_BOUND - IPW_NIC_SRAM_LOWER_BOUND);
James Ketrenos43f66a62005-03-25 12:31:53 -06003283
3284 /* DMA the initial boot firmware into the device */
Jeff Garzikbf794512005-07-31 13:07:26 -04003285 rc = ipw_load_firmware(priv, bootfw->data + sizeof(struct fw_header),
James Ketrenos43f66a62005-03-25 12:31:53 -06003286 bootfw->size - sizeof(struct fw_header));
3287 if (rc < 0) {
3288 IPW_ERROR("Unable to load boot firmware\n");
3289 goto error;
3290 }
3291
3292 /* kick start the device */
3293 ipw_start_nic(priv);
3294
3295 /* wait for the device to finish it's initial startup sequence */
James Ketrenosb095c382005-08-24 22:04:42 -05003296 rc = ipw_poll_bit(priv, IPW_INTA_RW,
3297 IPW_INTA_BIT_FW_INITIALIZATION_DONE, 500);
James Ketrenos43f66a62005-03-25 12:31:53 -06003298 if (rc < 0) {
3299 IPW_ERROR("device failed to boot initial fw image\n");
3300 goto error;
3301 }
3302 IPW_DEBUG_INFO("initial device response after %dms\n", rc);
3303
Jeff Garzikbf794512005-07-31 13:07:26 -04003304 /* ack fw init done interrupt */
James Ketrenosb095c382005-08-24 22:04:42 -05003305 ipw_write32(priv, IPW_INTA_RW, IPW_INTA_BIT_FW_INITIALIZATION_DONE);
James Ketrenos43f66a62005-03-25 12:31:53 -06003306
3307 /* DMA the ucode into the device */
Jeff Garzikbf794512005-07-31 13:07:26 -04003308 rc = ipw_load_ucode(priv, ucode->data + sizeof(struct fw_header),
James Ketrenos43f66a62005-03-25 12:31:53 -06003309 ucode->size - sizeof(struct fw_header));
3310 if (rc < 0) {
3311 IPW_ERROR("Unable to load ucode\n");
3312 goto error;
3313 }
Jeff Garzikbf794512005-07-31 13:07:26 -04003314
James Ketrenos43f66a62005-03-25 12:31:53 -06003315 /* stop nic */
3316 ipw_stop_nic(priv);
3317
3318 /* DMA bss firmware into the device */
Jeff Garzikbf794512005-07-31 13:07:26 -04003319 rc = ipw_load_firmware(priv, firmware->data +
3320 sizeof(struct fw_header),
James Ketrenos43f66a62005-03-25 12:31:53 -06003321 firmware->size - sizeof(struct fw_header));
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003322 if (rc < 0) {
James Ketrenos43f66a62005-03-25 12:31:53 -06003323 IPW_ERROR("Unable to load firmware\n");
3324 goto error;
3325 }
3326
3327 ipw_write32(priv, IPW_EEPROM_LOAD_DISABLE, 0);
3328
3329 rc = ipw_queue_reset(priv);
3330 if (rc) {
3331 IPW_ERROR("Unable to initialize queues\n");
3332 goto error;
3333 }
3334
3335 /* Ensure interrupts are disabled */
James Ketrenosb095c382005-08-24 22:04:42 -05003336 ipw_write32(priv, IPW_INTA_MASK_R, ~IPW_INTA_MASK_ALL);
James Ketrenosc848d0a2005-08-24 21:56:24 -05003337 /* ack pending interrupts */
James Ketrenosb095c382005-08-24 22:04:42 -05003338 ipw_write32(priv, IPW_INTA_RW, IPW_INTA_MASK_ALL);
Jeff Garzikbf794512005-07-31 13:07:26 -04003339
James Ketrenos43f66a62005-03-25 12:31:53 -06003340 /* kick start the device */
3341 ipw_start_nic(priv);
3342
James Ketrenosb095c382005-08-24 22:04:42 -05003343 if (ipw_read32(priv, IPW_INTA_RW) & IPW_INTA_BIT_PARITY_ERROR) {
James Ketrenos43f66a62005-03-25 12:31:53 -06003344 if (retries > 0) {
3345 IPW_WARNING("Parity error. Retrying init.\n");
3346 retries--;
3347 goto retry;
3348 }
3349
3350 IPW_ERROR("TODO: Handle parity error -- schedule restart?\n");
3351 rc = -EIO;
3352 goto error;
3353 }
3354
3355 /* wait for the device */
James Ketrenosb095c382005-08-24 22:04:42 -05003356 rc = ipw_poll_bit(priv, IPW_INTA_RW,
3357 IPW_INTA_BIT_FW_INITIALIZATION_DONE, 500);
James Ketrenos43f66a62005-03-25 12:31:53 -06003358 if (rc < 0) {
3359 IPW_ERROR("device failed to start after 500ms\n");
3360 goto error;
3361 }
3362 IPW_DEBUG_INFO("device response after %dms\n", rc);
3363
3364 /* ack fw init done interrupt */
James Ketrenosb095c382005-08-24 22:04:42 -05003365 ipw_write32(priv, IPW_INTA_RW, IPW_INTA_BIT_FW_INITIALIZATION_DONE);
James Ketrenos43f66a62005-03-25 12:31:53 -06003366
3367 /* read eeprom data and initialize the eeprom region of sram */
3368 priv->eeprom_delay = 1;
Jeff Garzikbf794512005-07-31 13:07:26 -04003369 ipw_eeprom_init_sram(priv);
James Ketrenos43f66a62005-03-25 12:31:53 -06003370
3371 /* enable interrupts */
3372 ipw_enable_interrupts(priv);
3373
3374 /* Ensure our queue has valid packets */
3375 ipw_rx_queue_replenish(priv);
3376
James Ketrenosb095c382005-08-24 22:04:42 -05003377 ipw_write32(priv, IPW_RX_READ_INDEX, priv->rxq->read);
James Ketrenos43f66a62005-03-25 12:31:53 -06003378
3379 /* ack pending interrupts */
James Ketrenosb095c382005-08-24 22:04:42 -05003380 ipw_write32(priv, IPW_INTA_RW, IPW_INTA_MASK_ALL);
James Ketrenos43f66a62005-03-25 12:31:53 -06003381
3382#ifndef CONFIG_PM
3383 release_firmware(bootfw);
3384 release_firmware(ucode);
3385 release_firmware(firmware);
3386#endif
3387 return 0;
3388
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003389 error:
James Ketrenos43f66a62005-03-25 12:31:53 -06003390 if (priv->rxq) {
3391 ipw_rx_queue_free(priv, priv->rxq);
3392 priv->rxq = NULL;
3393 }
3394 ipw_tx_queue_free(priv);
3395 if (bootfw)
3396 release_firmware(bootfw);
3397 if (ucode)
3398 release_firmware(ucode);
3399 if (firmware)
3400 release_firmware(firmware);
3401#ifdef CONFIG_PM
3402 fw_loaded = 0;
3403 bootfw = ucode = firmware = NULL;
3404#endif
3405
3406 return rc;
3407}
3408
Jeff Garzikbf794512005-07-31 13:07:26 -04003409/**
James Ketrenos43f66a62005-03-25 12:31:53 -06003410 * DMA services
3411 *
3412 * Theory of operation
3413 *
3414 * A queue is a circular buffers with 'Read' and 'Write' pointers.
3415 * 2 empty entries always kept in the buffer to protect from overflow.
3416 *
3417 * For Tx queue, there are low mark and high mark limits. If, after queuing
Jeff Garzikbf794512005-07-31 13:07:26 -04003418 * the packet for Tx, free space become < low mark, Tx queue stopped. When
3419 * reclaiming packets (on 'tx done IRQ), if free space become > high mark,
James Ketrenos43f66a62005-03-25 12:31:53 -06003420 * Tx queue resumed.
3421 *
3422 * The IPW operates with six queues, one receive queue in the device's
3423 * sram, one transmit queue for sending commands to the device firmware,
Jeff Garzikbf794512005-07-31 13:07:26 -04003424 * and four transmit queues for data.
James Ketrenos43f66a62005-03-25 12:31:53 -06003425 *
Jeff Garzikbf794512005-07-31 13:07:26 -04003426 * The four transmit queues allow for performing quality of service (qos)
James Ketrenos43f66a62005-03-25 12:31:53 -06003427 * transmissions as per the 802.11 protocol. Currently Linux does not
Jeff Garzikbf794512005-07-31 13:07:26 -04003428 * provide a mechanism to the user for utilizing prioritized queues, so
James Ketrenos43f66a62005-03-25 12:31:53 -06003429 * we only utilize the first data transmit queue (queue1).
3430 */
3431
3432/**
3433 * Driver allocates buffers of this size for Rx
3434 */
3435
3436static inline int ipw_queue_space(const struct clx2_queue *q)
3437{
3438 int s = q->last_used - q->first_empty;
3439 if (s <= 0)
3440 s += q->n_bd;
3441 s -= 2; /* keep some reserve to not confuse empty and full situations */
3442 if (s < 0)
3443 s = 0;
3444 return s;
3445}
3446
3447static inline int ipw_queue_inc_wrap(int index, int n_bd)
3448{
3449 return (++index == n_bd) ? 0 : index;
3450}
3451
3452/**
3453 * Initialize common DMA queue structure
Jeff Garzikbf794512005-07-31 13:07:26 -04003454 *
James Ketrenos43f66a62005-03-25 12:31:53 -06003455 * @param q queue to init
3456 * @param count Number of BD's to allocate. Should be power of 2
3457 * @param read_register Address for 'read' register
3458 * (not offset within BAR, full address)
3459 * @param write_register Address for 'write' register
3460 * (not offset within BAR, full address)
3461 * @param base_register Address for 'base' register
3462 * (not offset within BAR, full address)
3463 * @param size Address for 'size' register
3464 * (not offset within BAR, full address)
3465 */
Jeff Garzikbf794512005-07-31 13:07:26 -04003466static void ipw_queue_init(struct ipw_priv *priv, struct clx2_queue *q,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003467 int count, u32 read, u32 write, u32 base, u32 size)
James Ketrenos43f66a62005-03-25 12:31:53 -06003468{
3469 q->n_bd = count;
3470
3471 q->low_mark = q->n_bd / 4;
3472 if (q->low_mark < 4)
3473 q->low_mark = 4;
3474
3475 q->high_mark = q->n_bd / 8;
3476 if (q->high_mark < 2)
3477 q->high_mark = 2;
3478
3479 q->first_empty = q->last_used = 0;
3480 q->reg_r = read;
3481 q->reg_w = write;
3482
3483 ipw_write32(priv, base, q->dma_addr);
3484 ipw_write32(priv, size, count);
3485 ipw_write32(priv, read, 0);
3486 ipw_write32(priv, write, 0);
3487
3488 _ipw_read32(priv, 0x90);
3489}
3490
Jeff Garzikbf794512005-07-31 13:07:26 -04003491static int ipw_queue_tx_init(struct ipw_priv *priv,
James Ketrenos43f66a62005-03-25 12:31:53 -06003492 struct clx2_tx_queue *q,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003493 int count, u32 read, u32 write, u32 base, u32 size)
James Ketrenos43f66a62005-03-25 12:31:53 -06003494{
3495 struct pci_dev *dev = priv->pci_dev;
3496
3497 q->txb = kmalloc(sizeof(q->txb[0]) * count, GFP_KERNEL);
3498 if (!q->txb) {
3499 IPW_ERROR("vmalloc for auxilary BD structures failed\n");
3500 return -ENOMEM;
3501 }
3502
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003503 q->bd =
3504 pci_alloc_consistent(dev, sizeof(q->bd[0]) * count, &q->q.dma_addr);
James Ketrenos43f66a62005-03-25 12:31:53 -06003505 if (!q->bd) {
Jiri Bencaaa4d302005-06-07 14:58:41 +02003506 IPW_ERROR("pci_alloc_consistent(%zd) failed\n",
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003507 sizeof(q->bd[0]) * count);
James Ketrenos43f66a62005-03-25 12:31:53 -06003508 kfree(q->txb);
3509 q->txb = NULL;
3510 return -ENOMEM;
3511 }
3512
3513 ipw_queue_init(priv, &q->q, count, read, write, base, size);
3514 return 0;
3515}
3516
3517/**
3518 * Free one TFD, those at index [txq->q.last_used].
3519 * Do NOT advance any indexes
Jeff Garzikbf794512005-07-31 13:07:26 -04003520 *
James Ketrenos43f66a62005-03-25 12:31:53 -06003521 * @param dev
3522 * @param txq
3523 */
3524static void ipw_queue_tx_free_tfd(struct ipw_priv *priv,
3525 struct clx2_tx_queue *txq)
3526{
3527 struct tfd_frame *bd = &txq->bd[txq->q.last_used];
3528 struct pci_dev *dev = priv->pci_dev;
3529 int i;
Jeff Garzikbf794512005-07-31 13:07:26 -04003530
James Ketrenos43f66a62005-03-25 12:31:53 -06003531 /* classify bd */
3532 if (bd->control_flags.message_type == TX_HOST_COMMAND_TYPE)
3533 /* nothing to cleanup after for host commands */
3534 return;
3535
3536 /* sanity check */
James Ketrenosa613bff2005-08-24 21:43:11 -05003537 if (le32_to_cpu(bd->u.data.num_chunks) > NUM_TFD_CHUNKS) {
3538 IPW_ERROR("Too many chunks: %i\n",
3539 le32_to_cpu(bd->u.data.num_chunks));
James Ketrenos43f66a62005-03-25 12:31:53 -06003540 /** @todo issue fatal error, it is quite serious situation */
3541 return;
3542 }
3543
3544 /* unmap chunks if any */
James Ketrenosa613bff2005-08-24 21:43:11 -05003545 for (i = 0; i < le32_to_cpu(bd->u.data.num_chunks); i++) {
3546 pci_unmap_single(dev, le32_to_cpu(bd->u.data.chunk_ptr[i]),
3547 le16_to_cpu(bd->u.data.chunk_len[i]),
3548 PCI_DMA_TODEVICE);
James Ketrenos43f66a62005-03-25 12:31:53 -06003549 if (txq->txb[txq->q.last_used]) {
3550 ieee80211_txb_free(txq->txb[txq->q.last_used]);
3551 txq->txb[txq->q.last_used] = NULL;
3552 }
3553 }
3554}
3555
3556/**
3557 * Deallocate DMA queue.
Jeff Garzikbf794512005-07-31 13:07:26 -04003558 *
James Ketrenos43f66a62005-03-25 12:31:53 -06003559 * Empty queue by removing and destroying all BD's.
3560 * Free all buffers.
Jeff Garzikbf794512005-07-31 13:07:26 -04003561 *
James Ketrenos43f66a62005-03-25 12:31:53 -06003562 * @param dev
3563 * @param q
3564 */
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003565static void ipw_queue_tx_free(struct ipw_priv *priv, struct clx2_tx_queue *txq)
James Ketrenos43f66a62005-03-25 12:31:53 -06003566{
3567 struct clx2_queue *q = &txq->q;
3568 struct pci_dev *dev = priv->pci_dev;
3569
Jeff Garzikbf794512005-07-31 13:07:26 -04003570 if (q->n_bd == 0)
3571 return;
James Ketrenos43f66a62005-03-25 12:31:53 -06003572
3573 /* first, empty all BD's */
3574 for (; q->first_empty != q->last_used;
3575 q->last_used = ipw_queue_inc_wrap(q->last_used, q->n_bd)) {
3576 ipw_queue_tx_free_tfd(priv, txq);
3577 }
Jeff Garzikbf794512005-07-31 13:07:26 -04003578
James Ketrenos43f66a62005-03-25 12:31:53 -06003579 /* free buffers belonging to queue itself */
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003580 pci_free_consistent(dev, sizeof(txq->bd[0]) * q->n_bd, txq->bd,
James Ketrenos43f66a62005-03-25 12:31:53 -06003581 q->dma_addr);
3582 kfree(txq->txb);
3583
3584 /* 0 fill whole structure */
3585 memset(txq, 0, sizeof(*txq));
3586}
3587
James Ketrenos43f66a62005-03-25 12:31:53 -06003588/**
3589 * Destroy all DMA queues and structures
Jeff Garzikbf794512005-07-31 13:07:26 -04003590 *
James Ketrenos43f66a62005-03-25 12:31:53 -06003591 * @param priv
3592 */
3593static void ipw_tx_queue_free(struct ipw_priv *priv)
3594{
3595 /* Tx CMD queue */
3596 ipw_queue_tx_free(priv, &priv->txq_cmd);
3597
3598 /* Tx queues */
3599 ipw_queue_tx_free(priv, &priv->txq[0]);
3600 ipw_queue_tx_free(priv, &priv->txq[1]);
3601 ipw_queue_tx_free(priv, &priv->txq[2]);
3602 ipw_queue_tx_free(priv, &priv->txq[3]);
3603}
3604
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003605static inline void ipw_create_bssid(struct ipw_priv *priv, u8 * bssid)
James Ketrenos43f66a62005-03-25 12:31:53 -06003606{
3607 /* First 3 bytes are manufacturer */
3608 bssid[0] = priv->mac_addr[0];
3609 bssid[1] = priv->mac_addr[1];
3610 bssid[2] = priv->mac_addr[2];
3611
3612 /* Last bytes are random */
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003613 get_random_bytes(&bssid[3], ETH_ALEN - 3);
James Ketrenos43f66a62005-03-25 12:31:53 -06003614
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003615 bssid[0] &= 0xfe; /* clear multicast bit */
3616 bssid[0] |= 0x02; /* set local assignment bit (IEEE802) */
James Ketrenos43f66a62005-03-25 12:31:53 -06003617}
3618
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003619static inline u8 ipw_add_station(struct ipw_priv *priv, u8 * bssid)
James Ketrenos43f66a62005-03-25 12:31:53 -06003620{
3621 struct ipw_station_entry entry;
3622 int i;
3623
3624 for (i = 0; i < priv->num_stations; i++) {
3625 if (!memcmp(priv->stations[i], bssid, ETH_ALEN)) {
3626 /* Another node is active in network */
3627 priv->missed_adhoc_beacons = 0;
3628 if (!(priv->config & CFG_STATIC_CHANNEL))
3629 /* when other nodes drop out, we drop out */
3630 priv->config &= ~CFG_ADHOC_PERSIST;
3631
3632 return i;
3633 }
3634 }
3635
3636 if (i == MAX_STATIONS)
3637 return IPW_INVALID_STATION;
3638
3639 IPW_DEBUG_SCAN("Adding AdHoc station: " MAC_FMT "\n", MAC_ARG(bssid));
3640
3641 entry.reserved = 0;
3642 entry.support_mode = 0;
3643 memcpy(entry.mac_addr, bssid, ETH_ALEN);
3644 memcpy(priv->stations[i], bssid, ETH_ALEN);
3645 ipw_write_direct(priv, IPW_STATION_TABLE_LOWER + i * sizeof(entry),
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003646 &entry, sizeof(entry));
James Ketrenos43f66a62005-03-25 12:31:53 -06003647 priv->num_stations++;
3648
3649 return i;
3650}
3651
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003652static inline u8 ipw_find_station(struct ipw_priv *priv, u8 * bssid)
James Ketrenos43f66a62005-03-25 12:31:53 -06003653{
3654 int i;
3655
Jeff Garzikbf794512005-07-31 13:07:26 -04003656 for (i = 0; i < priv->num_stations; i++)
3657 if (!memcmp(priv->stations[i], bssid, ETH_ALEN))
James Ketrenos43f66a62005-03-25 12:31:53 -06003658 return i;
3659
3660 return IPW_INVALID_STATION;
3661}
3662
3663static void ipw_send_disassociate(struct ipw_priv *priv, int quiet)
3664{
3665 int err;
3666
3667 if (!(priv->status & (STATUS_ASSOCIATING | STATUS_ASSOCIATED))) {
3668 IPW_DEBUG_ASSOC("Disassociating while not associated.\n");
3669 return;
3670 }
3671
3672 IPW_DEBUG_ASSOC("Disassocation attempt from " MAC_FMT " "
3673 "on channel %d.\n",
Jeff Garzikbf794512005-07-31 13:07:26 -04003674 MAC_ARG(priv->assoc_request.bssid),
James Ketrenos43f66a62005-03-25 12:31:53 -06003675 priv->assoc_request.channel);
3676
3677 priv->status &= ~(STATUS_ASSOCIATING | STATUS_ASSOCIATED);
3678 priv->status |= STATUS_DISASSOCIATING;
3679
3680 if (quiet)
3681 priv->assoc_request.assoc_type = HC_DISASSOC_QUIET;
3682 else
3683 priv->assoc_request.assoc_type = HC_DISASSOCIATE;
3684 err = ipw_send_associate(priv, &priv->assoc_request);
3685 if (err) {
3686 IPW_DEBUG_HC("Attempt to send [dis]associate command "
3687 "failed.\n");
3688 return;
3689 }
3690
3691}
3692
James Ketrenosc848d0a2005-08-24 21:56:24 -05003693static int ipw_disassociate(void *data)
James Ketrenos43f66a62005-03-25 12:31:53 -06003694{
James Ketrenosc848d0a2005-08-24 21:56:24 -05003695 struct ipw_priv *priv = data;
3696 if (!(priv->status & (STATUS_ASSOCIATED | STATUS_ASSOCIATING)))
3697 return 0;
James Ketrenos43f66a62005-03-25 12:31:53 -06003698 ipw_send_disassociate(data, 0);
James Ketrenosc848d0a2005-08-24 21:56:24 -05003699 return 1;
3700}
3701
3702static void ipw_bg_disassociate(void *data)
3703{
3704 struct ipw_priv *priv = data;
3705 down(&priv->sem);
3706 ipw_disassociate(data);
3707 up(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06003708}
3709
Zhu Yid8bad6d2005-07-13 12:25:38 -05003710static void ipw_system_config(void *data)
3711{
3712 struct ipw_priv *priv = data;
3713 ipw_send_system_config(priv, &priv->sys_config);
3714}
3715
James Ketrenos43f66a62005-03-25 12:31:53 -06003716struct ipw_status_code {
3717 u16 status;
3718 const char *reason;
3719};
3720
3721static const struct ipw_status_code ipw_status_codes[] = {
3722 {0x00, "Successful"},
3723 {0x01, "Unspecified failure"},
3724 {0x0A, "Cannot support all requested capabilities in the "
3725 "Capability information field"},
3726 {0x0B, "Reassociation denied due to inability to confirm that "
3727 "association exists"},
3728 {0x0C, "Association denied due to reason outside the scope of this "
3729 "standard"},
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003730 {0x0D,
3731 "Responding station does not support the specified authentication "
James Ketrenos43f66a62005-03-25 12:31:53 -06003732 "algorithm"},
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003733 {0x0E,
3734 "Received an Authentication frame with authentication sequence "
James Ketrenos43f66a62005-03-25 12:31:53 -06003735 "transaction sequence number out of expected sequence"},
3736 {0x0F, "Authentication rejected because of challenge failure"},
3737 {0x10, "Authentication rejected due to timeout waiting for next "
3738 "frame in sequence"},
3739 {0x11, "Association denied because AP is unable to handle additional "
3740 "associated stations"},
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003741 {0x12,
3742 "Association denied due to requesting station not supporting all "
James Ketrenos43f66a62005-03-25 12:31:53 -06003743 "of the datarates in the BSSBasicServiceSet Parameter"},
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003744 {0x13,
3745 "Association denied due to requesting station not supporting "
James Ketrenos43f66a62005-03-25 12:31:53 -06003746 "short preamble operation"},
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003747 {0x14,
3748 "Association denied due to requesting station not supporting "
James Ketrenos43f66a62005-03-25 12:31:53 -06003749 "PBCC encoding"},
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003750 {0x15,
3751 "Association denied due to requesting station not supporting "
James Ketrenos43f66a62005-03-25 12:31:53 -06003752 "channel agility"},
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003753 {0x19,
3754 "Association denied due to requesting station not supporting "
James Ketrenos43f66a62005-03-25 12:31:53 -06003755 "short slot operation"},
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003756 {0x1A,
3757 "Association denied due to requesting station not supporting "
James Ketrenos43f66a62005-03-25 12:31:53 -06003758 "DSSS-OFDM operation"},
3759 {0x28, "Invalid Information Element"},
3760 {0x29, "Group Cipher is not valid"},
3761 {0x2A, "Pairwise Cipher is not valid"},
3762 {0x2B, "AKMP is not valid"},
3763 {0x2C, "Unsupported RSN IE version"},
3764 {0x2D, "Invalid RSN IE Capabilities"},
3765 {0x2E, "Cipher suite is rejected per security policy"},
3766};
3767
3768#ifdef CONFIG_IPW_DEBUG
Jeff Garzikbf794512005-07-31 13:07:26 -04003769static const char *ipw_get_status_code(u16 status)
James Ketrenos43f66a62005-03-25 12:31:53 -06003770{
3771 int i;
Jeff Garzikbf794512005-07-31 13:07:26 -04003772 for (i = 0; i < ARRAY_SIZE(ipw_status_codes); i++)
James Ketrenosea2b26e2005-08-24 21:25:16 -05003773 if (ipw_status_codes[i].status == (status & 0xff))
James Ketrenos43f66a62005-03-25 12:31:53 -06003774 return ipw_status_codes[i].reason;
3775 return "Unknown status value.";
3776}
3777#endif
3778
3779static void inline average_init(struct average *avg)
3780{
3781 memset(avg, 0, sizeof(*avg));
3782}
3783
3784static void inline average_add(struct average *avg, s16 val)
3785{
3786 avg->sum -= avg->entries[avg->pos];
3787 avg->sum += val;
3788 avg->entries[avg->pos++] = val;
3789 if (unlikely(avg->pos == AVG_ENTRIES)) {
3790 avg->init = 1;
3791 avg->pos = 0;
3792 }
3793}
3794
3795static s16 inline average_value(struct average *avg)
3796{
3797 if (!unlikely(avg->init)) {
3798 if (avg->pos)
3799 return avg->sum / avg->pos;
3800 return 0;
3801 }
3802
3803 return avg->sum / AVG_ENTRIES;
3804}
3805
3806static void ipw_reset_stats(struct ipw_priv *priv)
3807{
3808 u32 len = sizeof(u32);
3809
3810 priv->quality = 0;
3811
3812 average_init(&priv->average_missed_beacons);
3813 average_init(&priv->average_rssi);
3814 average_init(&priv->average_noise);
3815
3816 priv->last_rate = 0;
3817 priv->last_missed_beacons = 0;
3818 priv->last_rx_packets = 0;
3819 priv->last_tx_packets = 0;
3820 priv->last_tx_failures = 0;
Jeff Garzikbf794512005-07-31 13:07:26 -04003821
James Ketrenos43f66a62005-03-25 12:31:53 -06003822 /* Firmware managed, reset only when NIC is restarted, so we have to
3823 * normalize on the current value */
Jeff Garzikbf794512005-07-31 13:07:26 -04003824 ipw_get_ordinal(priv, IPW_ORD_STAT_RX_ERR_CRC,
James Ketrenos43f66a62005-03-25 12:31:53 -06003825 &priv->last_rx_err, &len);
Jeff Garzikbf794512005-07-31 13:07:26 -04003826 ipw_get_ordinal(priv, IPW_ORD_STAT_TX_FAILURE,
James Ketrenos43f66a62005-03-25 12:31:53 -06003827 &priv->last_tx_failures, &len);
3828
3829 /* Driver managed, reset with each association */
3830 priv->missed_adhoc_beacons = 0;
3831 priv->missed_beacons = 0;
3832 priv->tx_packets = 0;
3833 priv->rx_packets = 0;
3834
3835}
3836
James Ketrenos43f66a62005-03-25 12:31:53 -06003837static inline u32 ipw_get_max_rate(struct ipw_priv *priv)
3838{
3839 u32 i = 0x80000000;
3840 u32 mask = priv->rates_mask;
3841 /* If currently associated in B mode, restrict the maximum
3842 * rate match to B rates */
3843 if (priv->assoc_request.ieee_mode == IPW_B_MODE)
3844 mask &= IEEE80211_CCK_RATES_MASK;
3845
3846 /* TODO: Verify that the rate is supported by the current rates
3847 * list. */
3848
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003849 while (i && !(mask & i))
3850 i >>= 1;
James Ketrenos43f66a62005-03-25 12:31:53 -06003851 switch (i) {
James Ketrenosea2b26e2005-08-24 21:25:16 -05003852 case IEEE80211_CCK_RATE_1MB_MASK:
3853 return 1000000;
3854 case IEEE80211_CCK_RATE_2MB_MASK:
3855 return 2000000;
3856 case IEEE80211_CCK_RATE_5MB_MASK:
3857 return 5500000;
3858 case IEEE80211_OFDM_RATE_6MB_MASK:
3859 return 6000000;
3860 case IEEE80211_OFDM_RATE_9MB_MASK:
3861 return 9000000;
3862 case IEEE80211_CCK_RATE_11MB_MASK:
3863 return 11000000;
3864 case IEEE80211_OFDM_RATE_12MB_MASK:
3865 return 12000000;
3866 case IEEE80211_OFDM_RATE_18MB_MASK:
3867 return 18000000;
3868 case IEEE80211_OFDM_RATE_24MB_MASK:
3869 return 24000000;
3870 case IEEE80211_OFDM_RATE_36MB_MASK:
3871 return 36000000;
3872 case IEEE80211_OFDM_RATE_48MB_MASK:
3873 return 48000000;
3874 case IEEE80211_OFDM_RATE_54MB_MASK:
3875 return 54000000;
James Ketrenos43f66a62005-03-25 12:31:53 -06003876 }
3877
Jeff Garzikbf794512005-07-31 13:07:26 -04003878 if (priv->ieee->mode == IEEE_B)
James Ketrenos43f66a62005-03-25 12:31:53 -06003879 return 11000000;
3880 else
3881 return 54000000;
3882}
3883
3884static u32 ipw_get_current_rate(struct ipw_priv *priv)
3885{
3886 u32 rate, len = sizeof(rate);
3887 int err;
3888
Jeff Garzikbf794512005-07-31 13:07:26 -04003889 if (!(priv->status & STATUS_ASSOCIATED))
James Ketrenos43f66a62005-03-25 12:31:53 -06003890 return 0;
3891
3892 if (priv->tx_packets > IPW_REAL_RATE_RX_PACKET_THRESHOLD) {
Jeff Garzikbf794512005-07-31 13:07:26 -04003893 err = ipw_get_ordinal(priv, IPW_ORD_STAT_TX_CURR_RATE, &rate,
James Ketrenos43f66a62005-03-25 12:31:53 -06003894 &len);
3895 if (err) {
3896 IPW_DEBUG_INFO("failed querying ordinals.\n");
3897 return 0;
3898 }
Jeff Garzikbf794512005-07-31 13:07:26 -04003899 } else
James Ketrenos43f66a62005-03-25 12:31:53 -06003900 return ipw_get_max_rate(priv);
3901
3902 switch (rate) {
James Ketrenosea2b26e2005-08-24 21:25:16 -05003903 case IPW_TX_RATE_1MB:
3904 return 1000000;
3905 case IPW_TX_RATE_2MB:
3906 return 2000000;
3907 case IPW_TX_RATE_5MB:
3908 return 5500000;
3909 case IPW_TX_RATE_6MB:
3910 return 6000000;
3911 case IPW_TX_RATE_9MB:
3912 return 9000000;
3913 case IPW_TX_RATE_11MB:
3914 return 11000000;
3915 case IPW_TX_RATE_12MB:
3916 return 12000000;
3917 case IPW_TX_RATE_18MB:
3918 return 18000000;
3919 case IPW_TX_RATE_24MB:
3920 return 24000000;
3921 case IPW_TX_RATE_36MB:
3922 return 36000000;
3923 case IPW_TX_RATE_48MB:
3924 return 48000000;
3925 case IPW_TX_RATE_54MB:
3926 return 54000000;
James Ketrenos43f66a62005-03-25 12:31:53 -06003927 }
3928
3929 return 0;
3930}
3931
James Ketrenos43f66a62005-03-25 12:31:53 -06003932#define IPW_STATS_INTERVAL (2 * HZ)
3933static void ipw_gather_stats(struct ipw_priv *priv)
3934{
3935 u32 rx_err, rx_err_delta, rx_packets_delta;
3936 u32 tx_failures, tx_failures_delta, tx_packets_delta;
3937 u32 missed_beacons_percent, missed_beacons_delta;
3938 u32 quality = 0;
3939 u32 len = sizeof(u32);
3940 s16 rssi;
Jeff Garzikbf794512005-07-31 13:07:26 -04003941 u32 beacon_quality, signal_quality, tx_quality, rx_quality,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003942 rate_quality;
James Ketrenosea2b26e2005-08-24 21:25:16 -05003943 u32 max_rate;
James Ketrenos43f66a62005-03-25 12:31:53 -06003944
3945 if (!(priv->status & STATUS_ASSOCIATED)) {
3946 priv->quality = 0;
3947 return;
3948 }
3949
3950 /* Update the statistics */
Jeff Garzikbf794512005-07-31 13:07:26 -04003951 ipw_get_ordinal(priv, IPW_ORD_STAT_MISSED_BEACONS,
James Ketrenos43f66a62005-03-25 12:31:53 -06003952 &priv->missed_beacons, &len);
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003953 missed_beacons_delta = priv->missed_beacons - priv->last_missed_beacons;
James Ketrenos43f66a62005-03-25 12:31:53 -06003954 priv->last_missed_beacons = priv->missed_beacons;
3955 if (priv->assoc_request.beacon_interval) {
3956 missed_beacons_percent = missed_beacons_delta *
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003957 (HZ * priv->assoc_request.beacon_interval) /
3958 (IPW_STATS_INTERVAL * 10);
James Ketrenos43f66a62005-03-25 12:31:53 -06003959 } else {
3960 missed_beacons_percent = 0;
3961 }
3962 average_add(&priv->average_missed_beacons, missed_beacons_percent);
3963
3964 ipw_get_ordinal(priv, IPW_ORD_STAT_RX_ERR_CRC, &rx_err, &len);
3965 rx_err_delta = rx_err - priv->last_rx_err;
3966 priv->last_rx_err = rx_err;
3967
3968 ipw_get_ordinal(priv, IPW_ORD_STAT_TX_FAILURE, &tx_failures, &len);
3969 tx_failures_delta = tx_failures - priv->last_tx_failures;
3970 priv->last_tx_failures = tx_failures;
3971
3972 rx_packets_delta = priv->rx_packets - priv->last_rx_packets;
3973 priv->last_rx_packets = priv->rx_packets;
3974
3975 tx_packets_delta = priv->tx_packets - priv->last_tx_packets;
3976 priv->last_tx_packets = priv->tx_packets;
3977
3978 /* Calculate quality based on the following:
Jeff Garzikbf794512005-07-31 13:07:26 -04003979 *
James Ketrenos43f66a62005-03-25 12:31:53 -06003980 * Missed beacon: 100% = 0, 0% = 70% missed
3981 * Rate: 60% = 1Mbs, 100% = Max
3982 * Rx and Tx errors represent a straight % of total Rx/Tx
3983 * RSSI: 100% = > -50, 0% = < -80
3984 * Rx errors: 100% = 0, 0% = 50% missed
Jeff Garzikbf794512005-07-31 13:07:26 -04003985 *
James Ketrenos43f66a62005-03-25 12:31:53 -06003986 * The lowest computed quality is used.
3987 *
3988 */
3989#define BEACON_THRESHOLD 5
3990 beacon_quality = 100 - missed_beacons_percent;
3991 if (beacon_quality < BEACON_THRESHOLD)
3992 beacon_quality = 0;
3993 else
Jeff Garzikbf794512005-07-31 13:07:26 -04003994 beacon_quality = (beacon_quality - BEACON_THRESHOLD) * 100 /
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003995 (100 - BEACON_THRESHOLD);
Jeff Garzikbf794512005-07-31 13:07:26 -04003996 IPW_DEBUG_STATS("Missed beacon: %3d%% (%d%%)\n",
James Ketrenos43f66a62005-03-25 12:31:53 -06003997 beacon_quality, missed_beacons_percent);
Jeff Garzikbf794512005-07-31 13:07:26 -04003998
James Ketrenos43f66a62005-03-25 12:31:53 -06003999 priv->last_rate = ipw_get_current_rate(priv);
James Ketrenosea2b26e2005-08-24 21:25:16 -05004000 max_rate = ipw_get_max_rate(priv);
4001 rate_quality = priv->last_rate * 40 / max_rate + 60;
James Ketrenos43f66a62005-03-25 12:31:53 -06004002 IPW_DEBUG_STATS("Rate quality : %3d%% (%dMbs)\n",
4003 rate_quality, priv->last_rate / 1000000);
Jeff Garzikbf794512005-07-31 13:07:26 -04004004
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004005 if (rx_packets_delta > 100 && rx_packets_delta + rx_err_delta)
Jeff Garzikbf794512005-07-31 13:07:26 -04004006 rx_quality = 100 - (rx_err_delta * 100) /
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004007 (rx_packets_delta + rx_err_delta);
James Ketrenos43f66a62005-03-25 12:31:53 -06004008 else
4009 rx_quality = 100;
4010 IPW_DEBUG_STATS("Rx quality : %3d%% (%u errors, %u packets)\n",
4011 rx_quality, rx_err_delta, rx_packets_delta);
Jeff Garzikbf794512005-07-31 13:07:26 -04004012
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004013 if (tx_packets_delta > 100 && tx_packets_delta + tx_failures_delta)
Jeff Garzikbf794512005-07-31 13:07:26 -04004014 tx_quality = 100 - (tx_failures_delta * 100) /
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004015 (tx_packets_delta + tx_failures_delta);
James Ketrenos43f66a62005-03-25 12:31:53 -06004016 else
4017 tx_quality = 100;
4018 IPW_DEBUG_STATS("Tx quality : %3d%% (%u errors, %u packets)\n",
4019 tx_quality, tx_failures_delta, tx_packets_delta);
Jeff Garzikbf794512005-07-31 13:07:26 -04004020
James Ketrenos43f66a62005-03-25 12:31:53 -06004021 rssi = average_value(&priv->average_rssi);
James Ketrenosc848d0a2005-08-24 21:56:24 -05004022 signal_quality =
4023 (100 *
4024 (priv->ieee->perfect_rssi - priv->ieee->worst_rssi) *
4025 (priv->ieee->perfect_rssi - priv->ieee->worst_rssi) -
4026 (priv->ieee->perfect_rssi - rssi) *
4027 (15 * (priv->ieee->perfect_rssi - priv->ieee->worst_rssi) +
4028 62 * (priv->ieee->perfect_rssi - rssi))) /
4029 ((priv->ieee->perfect_rssi - priv->ieee->worst_rssi) *
4030 (priv->ieee->perfect_rssi - priv->ieee->worst_rssi));
4031 if (signal_quality > 100)
James Ketrenos43f66a62005-03-25 12:31:53 -06004032 signal_quality = 100;
James Ketrenosc848d0a2005-08-24 21:56:24 -05004033 else if (signal_quality < 1)
James Ketrenos43f66a62005-03-25 12:31:53 -06004034 signal_quality = 0;
James Ketrenosea2b26e2005-08-24 21:25:16 -05004035
James Ketrenos43f66a62005-03-25 12:31:53 -06004036 IPW_DEBUG_STATS("Signal level : %3d%% (%d dBm)\n",
4037 signal_quality, rssi);
Jeff Garzikbf794512005-07-31 13:07:26 -04004038
4039 quality = min(beacon_quality,
James Ketrenos43f66a62005-03-25 12:31:53 -06004040 min(rate_quality,
4041 min(tx_quality, min(rx_quality, signal_quality))));
4042 if (quality == beacon_quality)
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004043 IPW_DEBUG_STATS("Quality (%d%%): Clamped to missed beacons.\n",
4044 quality);
James Ketrenos43f66a62005-03-25 12:31:53 -06004045 if (quality == rate_quality)
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004046 IPW_DEBUG_STATS("Quality (%d%%): Clamped to rate quality.\n",
4047 quality);
James Ketrenos43f66a62005-03-25 12:31:53 -06004048 if (quality == tx_quality)
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004049 IPW_DEBUG_STATS("Quality (%d%%): Clamped to Tx quality.\n",
4050 quality);
James Ketrenos43f66a62005-03-25 12:31:53 -06004051 if (quality == rx_quality)
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004052 IPW_DEBUG_STATS("Quality (%d%%): Clamped to Rx quality.\n",
4053 quality);
James Ketrenos43f66a62005-03-25 12:31:53 -06004054 if (quality == signal_quality)
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004055 IPW_DEBUG_STATS("Quality (%d%%): Clamped to signal quality.\n",
4056 quality);
James Ketrenos43f66a62005-03-25 12:31:53 -06004057
4058 priv->quality = quality;
Jeff Garzikbf794512005-07-31 13:07:26 -04004059
4060 queue_delayed_work(priv->workqueue, &priv->gather_stats,
James Ketrenos43f66a62005-03-25 12:31:53 -06004061 IPW_STATS_INTERVAL);
4062}
4063
James Ketrenosc848d0a2005-08-24 21:56:24 -05004064static void ipw_bg_gather_stats(void *data)
4065{
4066 struct ipw_priv *priv = data;
4067 down(&priv->sem);
4068 ipw_gather_stats(data);
4069 up(&priv->sem);
4070}
4071
James Ketrenosea2b26e2005-08-24 21:25:16 -05004072static inline void ipw_handle_missed_beacon(struct ipw_priv *priv,
4073 int missed_count)
4074{
4075 priv->notif_missed_beacons = missed_count;
4076
James Ketrenosafbf30a2005-08-25 00:05:33 -05004077 if (missed_count > priv->disassociate_threshold &&
James Ketrenosea2b26e2005-08-24 21:25:16 -05004078 priv->status & STATUS_ASSOCIATED) {
4079 /* If associated and we've hit the missed
4080 * beacon threshold, disassociate, turn
4081 * off roaming, and abort any active scans */
4082 IPW_DEBUG(IPW_DL_INFO | IPW_DL_NOTIF |
James Ketrenosafbf30a2005-08-25 00:05:33 -05004083 IPW_DL_STATE | IPW_DL_ASSOC,
James Ketrenosea2b26e2005-08-24 21:25:16 -05004084 "Missed beacon: %d - disassociate\n", missed_count);
4085 priv->status &= ~STATUS_ROAMING;
James Ketrenosa613bff2005-08-24 21:43:11 -05004086 if (priv->status & STATUS_SCANNING) {
4087 IPW_DEBUG(IPW_DL_INFO | IPW_DL_NOTIF |
4088 IPW_DL_STATE,
4089 "Aborting scan with missed beacon.\n");
James Ketrenosea2b26e2005-08-24 21:25:16 -05004090 queue_work(priv->workqueue, &priv->abort_scan);
James Ketrenosa613bff2005-08-24 21:43:11 -05004091 }
4092
James Ketrenosea2b26e2005-08-24 21:25:16 -05004093 queue_work(priv->workqueue, &priv->disassociate);
4094 return;
4095 }
4096
4097 if (priv->status & STATUS_ROAMING) {
4098 /* If we are currently roaming, then just
4099 * print a debug statement... */
4100 IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE,
4101 "Missed beacon: %d - roam in progress\n",
4102 missed_count);
4103 return;
4104 }
4105
4106 if (missed_count > priv->roaming_threshold) {
4107 /* If we are not already roaming, set the ROAM
4108 * bit in the status and kick off a scan */
4109 IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE,
4110 "Missed beacon: %d - initiate "
4111 "roaming\n", missed_count);
4112 if (!(priv->status & STATUS_ROAMING)) {
4113 priv->status |= STATUS_ROAMING;
4114 if (!(priv->status & STATUS_SCANNING))
4115 queue_work(priv->workqueue,
4116 &priv->request_scan);
4117 }
4118 return;
4119 }
4120
4121 if (priv->status & STATUS_SCANNING) {
4122 /* Stop scan to keep fw from getting
4123 * stuck (only if we aren't roaming --
4124 * otherwise we'll never scan more than 2 or 3
4125 * channels..) */
James Ketrenosb095c382005-08-24 22:04:42 -05004126 IPW_DEBUG(IPW_DL_INFO | IPW_DL_NOTIF | IPW_DL_STATE,
4127 "Aborting scan with missed beacon.\n");
James Ketrenosea2b26e2005-08-24 21:25:16 -05004128 queue_work(priv->workqueue, &priv->abort_scan);
4129 }
4130
4131 IPW_DEBUG_NOTIF("Missed beacon: %d\n", missed_count);
4132
4133}
4134
James Ketrenos43f66a62005-03-25 12:31:53 -06004135/**
4136 * Handle host notification packet.
4137 * Called from interrupt routine
4138 */
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004139static inline void ipw_rx_notification(struct ipw_priv *priv,
James Ketrenos43f66a62005-03-25 12:31:53 -06004140 struct ipw_rx_notification *notif)
4141{
James Ketrenosa613bff2005-08-24 21:43:11 -05004142 notif->size = le16_to_cpu(notif->size);
4143
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004144 IPW_DEBUG_NOTIF("type = %i (%d bytes)\n", notif->subtype, notif->size);
Jeff Garzikbf794512005-07-31 13:07:26 -04004145
James Ketrenos43f66a62005-03-25 12:31:53 -06004146 switch (notif->subtype) {
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004147 case HOST_NOTIFICATION_STATUS_ASSOCIATED:{
4148 struct notif_association *assoc = &notif->u.assoc;
Jeff Garzikbf794512005-07-31 13:07:26 -04004149
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004150 switch (assoc->state) {
4151 case CMAS_ASSOCIATED:{
4152 IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE |
4153 IPW_DL_ASSOC,
4154 "associated: '%s' " MAC_FMT
4155 " \n",
4156 escape_essid(priv->essid,
4157 priv->essid_len),
4158 MAC_ARG(priv->bssid));
Jeff Garzikbf794512005-07-31 13:07:26 -04004159
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004160 switch (priv->ieee->iw_mode) {
4161 case IW_MODE_INFRA:
4162 memcpy(priv->ieee->bssid,
4163 priv->bssid, ETH_ALEN);
4164 break;
James Ketrenos43f66a62005-03-25 12:31:53 -06004165
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004166 case IW_MODE_ADHOC:
4167 memcpy(priv->ieee->bssid,
4168 priv->bssid, ETH_ALEN);
Jeff Garzikbf794512005-07-31 13:07:26 -04004169
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004170 /* clear out the station table */
4171 priv->num_stations = 0;
James Ketrenos43f66a62005-03-25 12:31:53 -06004172
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004173 IPW_DEBUG_ASSOC
4174 ("queueing adhoc check\n");
4175 queue_delayed_work(priv->
4176 workqueue,
4177 &priv->
4178 adhoc_check,
4179 priv->
4180 assoc_request.
4181 beacon_interval);
4182 break;
4183 }
James Ketrenos43f66a62005-03-25 12:31:53 -06004184
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004185 priv->status &= ~STATUS_ASSOCIATING;
4186 priv->status |= STATUS_ASSOCIATED;
Zhu Yid8bad6d2005-07-13 12:25:38 -05004187 queue_work(priv->workqueue,
4188 &priv->system_config);
James Ketrenos43f66a62005-03-25 12:31:53 -06004189
James Ketrenosb095c382005-08-24 22:04:42 -05004190#ifdef CONFIG_IPW_QOS
James Ketrenosafbf30a2005-08-25 00:05:33 -05004191#define IPW_GET_PACKET_STYPE(x) WLAN_FC_GET_STYPE( \
4192 le16_to_cpu(((struct ieee80211_hdr *)(x))->frame_ctl))
4193 if ((priv->status & STATUS_AUTH) &&
4194 (IPW_GET_PACKET_STYPE(&notif->u.raw)
4195 == IEEE80211_STYPE_ASSOC_RESP)) {
James Ketrenosb095c382005-08-24 22:04:42 -05004196 if ((sizeof
4197 (struct
James Ketrenos2b184d52005-08-03 20:33:14 -05004198 ieee80211_assoc_response)
James Ketrenosb095c382005-08-24 22:04:42 -05004199 <= notif->size)
4200 && (notif->size <= 2314)) {
4201 struct
4202 ieee80211_rx_stats
4203 stats = {
4204 .len =
4205 notif->
4206 size - 1,
4207 };
4208
4209 IPW_DEBUG_QOS
4210 ("QoS Associate "
4211 "size %d\n",
4212 notif->size);
4213 ieee80211_rx_mgt(priv->
4214 ieee,
4215 (struct
James Ketrenos2b184d52005-08-03 20:33:14 -05004216 ieee80211_hdr_4addr
James Ketrenosb095c382005-08-24 22:04:42 -05004217 *)
4218 &notif->u.raw, &stats);
4219 }
4220 }
4221#endif
4222
James Ketrenosa613bff2005-08-24 21:43:11 -05004223 schedule_work(&priv->link_up);
James Ketrenos43f66a62005-03-25 12:31:53 -06004224
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004225 break;
4226 }
4227
4228 case CMAS_AUTHENTICATED:{
4229 if (priv->
4230 status & (STATUS_ASSOCIATED |
4231 STATUS_AUTH)) {
4232#ifdef CONFIG_IPW_DEBUG
4233 struct notif_authenticate *auth
4234 = &notif->u.auth;
4235 IPW_DEBUG(IPW_DL_NOTIF |
4236 IPW_DL_STATE |
4237 IPW_DL_ASSOC,
4238 "deauthenticated: '%s' "
4239 MAC_FMT
4240 ": (0x%04X) - %s \n",
4241 escape_essid(priv->
4242 essid,
4243 priv->
4244 essid_len),
4245 MAC_ARG(priv->bssid),
4246 ntohs(auth->status),
4247 ipw_get_status_code
4248 (ntohs
4249 (auth->status)));
4250#endif
4251
4252 priv->status &=
4253 ~(STATUS_ASSOCIATING |
4254 STATUS_AUTH |
4255 STATUS_ASSOCIATED);
4256
James Ketrenosa613bff2005-08-24 21:43:11 -05004257 schedule_work(&priv->link_down);
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004258 break;
4259 }
4260
4261 IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE |
4262 IPW_DL_ASSOC,
4263 "authenticated: '%s' " MAC_FMT
4264 "\n",
4265 escape_essid(priv->essid,
4266 priv->essid_len),
4267 MAC_ARG(priv->bssid));
4268 break;
4269 }
4270
4271 case CMAS_INIT:{
James Ketrenosea2b26e2005-08-24 21:25:16 -05004272 if (priv->status & STATUS_AUTH) {
4273 struct
4274 ieee80211_assoc_response
4275 *resp;
4276 resp =
4277 (struct
4278 ieee80211_assoc_response
4279 *)&notif->u.raw;
4280 IPW_DEBUG(IPW_DL_NOTIF |
4281 IPW_DL_STATE |
4282 IPW_DL_ASSOC,
4283 "association failed (0x%04X): %s\n",
4284 ntohs(resp->status),
4285 ipw_get_status_code
4286 (ntohs
4287 (resp->status)));
4288 }
4289
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004290 IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE |
4291 IPW_DL_ASSOC,
4292 "disassociated: '%s' " MAC_FMT
4293 " \n",
4294 escape_essid(priv->essid,
4295 priv->essid_len),
4296 MAC_ARG(priv->bssid));
4297
4298 priv->status &=
4299 ~(STATUS_DISASSOCIATING |
4300 STATUS_ASSOCIATING |
4301 STATUS_ASSOCIATED | STATUS_AUTH);
James Ketrenosb095c382005-08-24 22:04:42 -05004302 if (priv->assoc_network
4303 && (priv->assoc_network->
4304 capability &
4305 WLAN_CAPABILITY_IBSS))
4306 ipw_remove_current_network
4307 (priv);
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004308
James Ketrenosa613bff2005-08-24 21:43:11 -05004309 schedule_work(&priv->link_down);
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004310
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004311 break;
4312 }
4313
James Ketrenosb095c382005-08-24 22:04:42 -05004314 case CMAS_RX_ASSOC_RESP:
4315 break;
4316
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004317 default:
4318 IPW_ERROR("assoc: unknown (%d)\n",
4319 assoc->state);
4320 break;
4321 }
4322
James Ketrenos43f66a62005-03-25 12:31:53 -06004323 break;
4324 }
Jeff Garzikbf794512005-07-31 13:07:26 -04004325
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004326 case HOST_NOTIFICATION_STATUS_AUTHENTICATE:{
4327 struct notif_authenticate *auth = &notif->u.auth;
4328 switch (auth->state) {
4329 case CMAS_AUTHENTICATED:
4330 IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE,
4331 "authenticated: '%s' " MAC_FMT " \n",
4332 escape_essid(priv->essid,
4333 priv->essid_len),
4334 MAC_ARG(priv->bssid));
4335 priv->status |= STATUS_AUTH;
4336 break;
4337
4338 case CMAS_INIT:
4339 if (priv->status & STATUS_AUTH) {
4340 IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE |
4341 IPW_DL_ASSOC,
4342 "authentication failed (0x%04X): %s\n",
4343 ntohs(auth->status),
4344 ipw_get_status_code(ntohs
4345 (auth->
4346 status)));
4347 }
4348 IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE |
4349 IPW_DL_ASSOC,
4350 "deauthenticated: '%s' " MAC_FMT "\n",
4351 escape_essid(priv->essid,
4352 priv->essid_len),
4353 MAC_ARG(priv->bssid));
James Ketrenos43f66a62005-03-25 12:31:53 -06004354
4355 priv->status &= ~(STATUS_ASSOCIATING |
4356 STATUS_AUTH |
4357 STATUS_ASSOCIATED);
4358
James Ketrenosa613bff2005-08-24 21:43:11 -05004359 schedule_work(&priv->link_down);
James Ketrenos43f66a62005-03-25 12:31:53 -06004360 break;
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004361
4362 case CMAS_TX_AUTH_SEQ_1:
4363 IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE |
4364 IPW_DL_ASSOC, "AUTH_SEQ_1\n");
4365 break;
4366 case CMAS_RX_AUTH_SEQ_2:
4367 IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE |
4368 IPW_DL_ASSOC, "AUTH_SEQ_2\n");
4369 break;
4370 case CMAS_AUTH_SEQ_1_PASS:
4371 IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE |
4372 IPW_DL_ASSOC, "AUTH_SEQ_1_PASS\n");
4373 break;
4374 case CMAS_AUTH_SEQ_1_FAIL:
4375 IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE |
4376 IPW_DL_ASSOC, "AUTH_SEQ_1_FAIL\n");
4377 break;
4378 case CMAS_TX_AUTH_SEQ_3:
4379 IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE |
4380 IPW_DL_ASSOC, "AUTH_SEQ_3\n");
4381 break;
4382 case CMAS_RX_AUTH_SEQ_4:
4383 IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE |
4384 IPW_DL_ASSOC, "RX_AUTH_SEQ_4\n");
4385 break;
4386 case CMAS_AUTH_SEQ_2_PASS:
4387 IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE |
4388 IPW_DL_ASSOC, "AUTH_SEQ_2_PASS\n");
4389 break;
4390 case CMAS_AUTH_SEQ_2_FAIL:
4391 IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE |
4392 IPW_DL_ASSOC, "AUT_SEQ_2_FAIL\n");
4393 break;
4394 case CMAS_TX_ASSOC:
4395 IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE |
4396 IPW_DL_ASSOC, "TX_ASSOC\n");
4397 break;
4398 case CMAS_RX_ASSOC_RESP:
4399 IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE |
4400 IPW_DL_ASSOC, "RX_ASSOC_RESP\n");
James Ketrenosb095c382005-08-24 22:04:42 -05004401
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004402 break;
4403 case CMAS_ASSOCIATED:
4404 IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE |
4405 IPW_DL_ASSOC, "ASSOCIATED\n");
4406 break;
4407 default:
4408 IPW_DEBUG_NOTIF("auth: failure - %d\n",
4409 auth->state);
4410 break;
4411 }
4412 break;
4413 }
4414
4415 case HOST_NOTIFICATION_STATUS_SCAN_CHANNEL_RESULT:{
4416 struct notif_channel_result *x =
4417 &notif->u.channel_result;
4418
4419 if (notif->size == sizeof(*x)) {
4420 IPW_DEBUG_SCAN("Scan result for channel %d\n",
4421 x->channel_num);
4422 } else {
4423 IPW_DEBUG_SCAN("Scan result of wrong size %d "
4424 "(should be %zd)\n",
4425 notif->size, sizeof(*x));
4426 }
4427 break;
4428 }
4429
4430 case HOST_NOTIFICATION_STATUS_SCAN_COMPLETED:{
4431 struct notif_scan_complete *x = &notif->u.scan_complete;
4432 if (notif->size == sizeof(*x)) {
4433 IPW_DEBUG_SCAN
4434 ("Scan completed: type %d, %d channels, "
4435 "%d status\n", x->scan_type,
4436 x->num_channels, x->status);
4437 } else {
4438 IPW_ERROR("Scan completed of wrong size %d "
4439 "(should be %zd)\n",
4440 notif->size, sizeof(*x));
4441 }
4442
4443 priv->status &=
4444 ~(STATUS_SCANNING | STATUS_SCAN_ABORTING);
4445
4446 cancel_delayed_work(&priv->scan_check);
4447
James Ketrenosb095c382005-08-24 22:04:42 -05004448 if (priv->status & STATUS_EXIT_PENDING)
4449 break;
4450
4451 priv->ieee->scans++;
4452
4453#ifdef CONFIG_IPW2200_MONITOR
4454 if (priv->ieee->iw_mode == IW_MODE_MONITOR) {
James Ketrenosafbf30a2005-08-25 00:05:33 -05004455 priv->status |= STATUS_SCAN_FORCED;
James Ketrenosb095c382005-08-24 22:04:42 -05004456 queue_work(priv->workqueue,
4457 &priv->request_scan);
4458 break;
4459 }
James Ketrenosafbf30a2005-08-25 00:05:33 -05004460 priv->status &= ~STATUS_SCAN_FORCED;
James Ketrenosb095c382005-08-24 22:04:42 -05004461#endif /* CONFIG_IPW2200_MONITOR */
4462
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004463 if (!(priv->status & (STATUS_ASSOCIATED |
4464 STATUS_ASSOCIATING |
4465 STATUS_ROAMING |
4466 STATUS_DISASSOCIATING)))
4467 queue_work(priv->workqueue, &priv->associate);
4468 else if (priv->status & STATUS_ROAMING) {
4469 /* If a scan completed and we are in roam mode, then
4470 * the scan that completed was the one requested as a
4471 * result of entering roam... so, schedule the
4472 * roam work */
4473 queue_work(priv->workqueue, &priv->roam);
4474 } else if (priv->status & STATUS_SCAN_PENDING)
4475 queue_work(priv->workqueue,
4476 &priv->request_scan);
James Ketrenosa613bff2005-08-24 21:43:11 -05004477 else if (priv->config & CFG_BACKGROUND_SCAN
4478 && priv->status & STATUS_ASSOCIATED)
4479 queue_delayed_work(priv->workqueue,
4480 &priv->request_scan, HZ);
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004481 break;
4482 }
4483
4484 case HOST_NOTIFICATION_STATUS_FRAG_LENGTH:{
4485 struct notif_frag_length *x = &notif->u.frag_len;
4486
James Ketrenosa613bff2005-08-24 21:43:11 -05004487 if (notif->size == sizeof(*x))
4488 IPW_ERROR("Frag length: %d\n",
4489 le16_to_cpu(x->frag_length));
4490 else
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004491 IPW_ERROR("Frag length of wrong size %d "
4492 "(should be %zd)\n",
4493 notif->size, sizeof(*x));
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004494 break;
4495 }
4496
4497 case HOST_NOTIFICATION_STATUS_LINK_DETERIORATION:{
4498 struct notif_link_deterioration *x =
4499 &notif->u.link_deterioration;
James Ketrenosafbf30a2005-08-25 00:05:33 -05004500
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004501 if (notif->size == sizeof(*x)) {
4502 IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE,
4503 "link deterioration: '%s' " MAC_FMT
4504 " \n", escape_essid(priv->essid,
4505 priv->essid_len),
4506 MAC_ARG(priv->bssid));
4507 memcpy(&priv->last_link_deterioration, x,
4508 sizeof(*x));
4509 } else {
4510 IPW_ERROR("Link Deterioration of wrong size %d "
4511 "(should be %zd)\n",
4512 notif->size, sizeof(*x));
4513 }
4514 break;
4515 }
4516
4517 case HOST_NOTIFICATION_DINO_CONFIG_RESPONSE:{
4518 IPW_ERROR("Dino config\n");
4519 if (priv->hcmd
James Ketrenosa613bff2005-08-24 21:43:11 -05004520 && priv->hcmd->cmd != HOST_CMD_DINO_CONFIG)
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004521 IPW_ERROR("Unexpected DINO_CONFIG_RESPONSE\n");
James Ketrenosa613bff2005-08-24 21:43:11 -05004522
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004523 break;
4524 }
4525
4526 case HOST_NOTIFICATION_STATUS_BEACON_STATE:{
4527 struct notif_beacon_state *x = &notif->u.beacon_state;
4528 if (notif->size != sizeof(*x)) {
4529 IPW_ERROR
4530 ("Beacon state of wrong size %d (should "
4531 "be %zd)\n", notif->size, sizeof(*x));
4532 break;
Jeff Garzikbf794512005-07-31 13:07:26 -04004533 }
James Ketrenos43f66a62005-03-25 12:31:53 -06004534
James Ketrenosa613bff2005-08-24 21:43:11 -05004535 if (le32_to_cpu(x->state) ==
4536 HOST_NOTIFICATION_STATUS_BEACON_MISSING)
4537 ipw_handle_missed_beacon(priv,
4538 le32_to_cpu(x->
4539 number));
Jeff Garzikbf794512005-07-31 13:07:26 -04004540
James Ketrenos43f66a62005-03-25 12:31:53 -06004541 break;
4542 }
Jeff Garzikbf794512005-07-31 13:07:26 -04004543
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004544 case HOST_NOTIFICATION_STATUS_TGI_TX_KEY:{
4545 struct notif_tgi_tx_key *x = &notif->u.tgi_tx_key;
4546 if (notif->size == sizeof(*x)) {
4547 IPW_ERROR("TGi Tx Key: state 0x%02x sec type "
4548 "0x%02x station %d\n",
4549 x->key_state, x->security_type,
4550 x->station_index);
4551 break;
James Ketrenos43f66a62005-03-25 12:31:53 -06004552 }
4553
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004554 IPW_ERROR
4555 ("TGi Tx Key of wrong size %d (should be %zd)\n",
4556 notif->size, sizeof(*x));
4557 break;
4558 }
4559
4560 case HOST_NOTIFICATION_CALIB_KEEP_RESULTS:{
4561 struct notif_calibration *x = &notif->u.calibration;
4562
4563 if (notif->size == sizeof(*x)) {
4564 memcpy(&priv->calib, x, sizeof(*x));
4565 IPW_DEBUG_INFO("TODO: Calibration\n");
4566 break;
James Ketrenos43f66a62005-03-25 12:31:53 -06004567 }
4568
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004569 IPW_ERROR
4570 ("Calibration of wrong size %d (should be %zd)\n",
4571 notif->size, sizeof(*x));
James Ketrenos43f66a62005-03-25 12:31:53 -06004572 break;
Jeff Garzikbf794512005-07-31 13:07:26 -04004573 }
James Ketrenos43f66a62005-03-25 12:31:53 -06004574
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004575 case HOST_NOTIFICATION_NOISE_STATS:{
4576 if (notif->size == sizeof(u32)) {
4577 priv->last_noise =
James Ketrenosa613bff2005-08-24 21:43:11 -05004578 (u8) (le32_to_cpu(notif->u.noise.value) &
4579 0xff);
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004580 average_add(&priv->average_noise,
4581 priv->last_noise);
4582 break;
4583 }
James Ketrenos43f66a62005-03-25 12:31:53 -06004584
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004585 IPW_ERROR
4586 ("Noise stat is wrong size %d (should be %zd)\n",
4587 notif->size, sizeof(u32));
James Ketrenos43f66a62005-03-25 12:31:53 -06004588 break;
Jeff Garzikbf794512005-07-31 13:07:26 -04004589 }
4590
James Ketrenos43f66a62005-03-25 12:31:53 -06004591 default:
4592 IPW_ERROR("Unknown notification: "
4593 "subtype=%d,flags=0x%2x,size=%d\n",
4594 notif->subtype, notif->flags, notif->size);
4595 }
4596}
4597
4598/**
4599 * Destroys all DMA structures and initialise them again
Jeff Garzikbf794512005-07-31 13:07:26 -04004600 *
James Ketrenos43f66a62005-03-25 12:31:53 -06004601 * @param priv
4602 * @return error code
4603 */
4604static int ipw_queue_reset(struct ipw_priv *priv)
4605{
4606 int rc = 0;
4607 /** @todo customize queue sizes */
4608 int nTx = 64, nTxCmd = 8;
4609 ipw_tx_queue_free(priv);
4610 /* Tx CMD queue */
4611 rc = ipw_queue_tx_init(priv, &priv->txq_cmd, nTxCmd,
James Ketrenosb095c382005-08-24 22:04:42 -05004612 IPW_TX_CMD_QUEUE_READ_INDEX,
4613 IPW_TX_CMD_QUEUE_WRITE_INDEX,
4614 IPW_TX_CMD_QUEUE_BD_BASE,
4615 IPW_TX_CMD_QUEUE_BD_SIZE);
James Ketrenos43f66a62005-03-25 12:31:53 -06004616 if (rc) {
4617 IPW_ERROR("Tx Cmd queue init failed\n");
4618 goto error;
4619 }
4620 /* Tx queue(s) */
4621 rc = ipw_queue_tx_init(priv, &priv->txq[0], nTx,
James Ketrenosb095c382005-08-24 22:04:42 -05004622 IPW_TX_QUEUE_0_READ_INDEX,
4623 IPW_TX_QUEUE_0_WRITE_INDEX,
4624 IPW_TX_QUEUE_0_BD_BASE, IPW_TX_QUEUE_0_BD_SIZE);
James Ketrenos43f66a62005-03-25 12:31:53 -06004625 if (rc) {
4626 IPW_ERROR("Tx 0 queue init failed\n");
4627 goto error;
4628 }
4629 rc = ipw_queue_tx_init(priv, &priv->txq[1], nTx,
James Ketrenosb095c382005-08-24 22:04:42 -05004630 IPW_TX_QUEUE_1_READ_INDEX,
4631 IPW_TX_QUEUE_1_WRITE_INDEX,
4632 IPW_TX_QUEUE_1_BD_BASE, IPW_TX_QUEUE_1_BD_SIZE);
James Ketrenos43f66a62005-03-25 12:31:53 -06004633 if (rc) {
4634 IPW_ERROR("Tx 1 queue init failed\n");
4635 goto error;
4636 }
4637 rc = ipw_queue_tx_init(priv, &priv->txq[2], nTx,
James Ketrenosb095c382005-08-24 22:04:42 -05004638 IPW_TX_QUEUE_2_READ_INDEX,
4639 IPW_TX_QUEUE_2_WRITE_INDEX,
4640 IPW_TX_QUEUE_2_BD_BASE, IPW_TX_QUEUE_2_BD_SIZE);
James Ketrenos43f66a62005-03-25 12:31:53 -06004641 if (rc) {
4642 IPW_ERROR("Tx 2 queue init failed\n");
4643 goto error;
4644 }
4645 rc = ipw_queue_tx_init(priv, &priv->txq[3], nTx,
James Ketrenosb095c382005-08-24 22:04:42 -05004646 IPW_TX_QUEUE_3_READ_INDEX,
4647 IPW_TX_QUEUE_3_WRITE_INDEX,
4648 IPW_TX_QUEUE_3_BD_BASE, IPW_TX_QUEUE_3_BD_SIZE);
James Ketrenos43f66a62005-03-25 12:31:53 -06004649 if (rc) {
4650 IPW_ERROR("Tx 3 queue init failed\n");
4651 goto error;
4652 }
4653 /* statistics */
4654 priv->rx_bufs_min = 0;
4655 priv->rx_pend_max = 0;
4656 return rc;
4657
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004658 error:
James Ketrenos43f66a62005-03-25 12:31:53 -06004659 ipw_tx_queue_free(priv);
4660 return rc;
4661}
4662
4663/**
4664 * Reclaim Tx queue entries no more used by NIC.
Jeff Garzikbf794512005-07-31 13:07:26 -04004665 *
James Ketrenos43f66a62005-03-25 12:31:53 -06004666 * When FW adwances 'R' index, all entries between old and
4667 * new 'R' index need to be reclaimed. As result, some free space
4668 * forms. If there is enough free space (> low mark), wake Tx queue.
Jeff Garzikbf794512005-07-31 13:07:26 -04004669 *
James Ketrenos43f66a62005-03-25 12:31:53 -06004670 * @note Need to protect against garbage in 'R' index
4671 * @param priv
4672 * @param txq
4673 * @param qindex
4674 * @return Number of used entries remains in the queue
4675 */
Jeff Garzikbf794512005-07-31 13:07:26 -04004676static int ipw_queue_tx_reclaim(struct ipw_priv *priv,
James Ketrenos43f66a62005-03-25 12:31:53 -06004677 struct clx2_tx_queue *txq, int qindex)
4678{
4679 u32 hw_tail;
4680 int used;
4681 struct clx2_queue *q = &txq->q;
4682
4683 hw_tail = ipw_read32(priv, q->reg_r);
4684 if (hw_tail >= q->n_bd) {
4685 IPW_ERROR
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004686 ("Read index for DMA queue (%d) is out of range [0-%d)\n",
4687 hw_tail, q->n_bd);
James Ketrenos43f66a62005-03-25 12:31:53 -06004688 goto done;
4689 }
4690 for (; q->last_used != hw_tail;
4691 q->last_used = ipw_queue_inc_wrap(q->last_used, q->n_bd)) {
4692 ipw_queue_tx_free_tfd(priv, txq);
4693 priv->tx_packets++;
4694 }
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004695 done:
James Ketrenos9ddf84f2005-08-16 17:07:11 -05004696 if ((ipw_queue_space(q) > q->low_mark) &&
4697 (qindex >= 0) &&
4698 (priv->status & STATUS_ASSOCIATED) && netif_running(priv->net_dev))
4699 netif_wake_queue(priv->net_dev);
James Ketrenos43f66a62005-03-25 12:31:53 -06004700 used = q->first_empty - q->last_used;
4701 if (used < 0)
4702 used += q->n_bd;
4703
4704 return used;
4705}
4706
4707static int ipw_queue_tx_hcmd(struct ipw_priv *priv, int hcmd, void *buf,
4708 int len, int sync)
4709{
4710 struct clx2_tx_queue *txq = &priv->txq_cmd;
4711 struct clx2_queue *q = &txq->q;
4712 struct tfd_frame *tfd;
4713
4714 if (ipw_queue_space(q) < (sync ? 1 : 2)) {
4715 IPW_ERROR("No space for Tx\n");
4716 return -EBUSY;
4717 }
4718
4719 tfd = &txq->bd[q->first_empty];
4720 txq->txb[q->first_empty] = NULL;
4721
4722 memset(tfd, 0, sizeof(*tfd));
4723 tfd->control_flags.message_type = TX_HOST_COMMAND_TYPE;
4724 tfd->control_flags.control_bits = TFD_NEED_IRQ_MASK;
4725 priv->hcmd_seq++;
4726 tfd->u.cmd.index = hcmd;
4727 tfd->u.cmd.length = len;
4728 memcpy(tfd->u.cmd.payload, buf, len);
4729 q->first_empty = ipw_queue_inc_wrap(q->first_empty, q->n_bd);
4730 ipw_write32(priv, q->reg_w, q->first_empty);
4731 _ipw_read32(priv, 0x90);
4732
4733 return 0;
4734}
4735
Jeff Garzikbf794512005-07-31 13:07:26 -04004736/*
James Ketrenos43f66a62005-03-25 12:31:53 -06004737 * Rx theory of operation
4738 *
4739 * The host allocates 32 DMA target addresses and passes the host address
James Ketrenosb095c382005-08-24 22:04:42 -05004740 * to the firmware at register IPW_RFDS_TABLE_LOWER + N * RFD_SIZE where N is
James Ketrenos43f66a62005-03-25 12:31:53 -06004741 * 0 to 31
4742 *
4743 * Rx Queue Indexes
4744 * The host/firmware share two index registers for managing the Rx buffers.
4745 *
Jeff Garzikbf794512005-07-31 13:07:26 -04004746 * The READ index maps to the first position that the firmware may be writing
4747 * to -- the driver can read up to (but not including) this position and get
4748 * good data.
James Ketrenos43f66a62005-03-25 12:31:53 -06004749 * The READ index is managed by the firmware once the card is enabled.
4750 *
4751 * The WRITE index maps to the last position the driver has read from -- the
4752 * position preceding WRITE is the last slot the firmware can place a packet.
4753 *
4754 * The queue is empty (no good data) if WRITE = READ - 1, and is full if
Jeff Garzikbf794512005-07-31 13:07:26 -04004755 * WRITE = READ.
James Ketrenos43f66a62005-03-25 12:31:53 -06004756 *
Jeff Garzikbf794512005-07-31 13:07:26 -04004757 * During initialization the host sets up the READ queue position to the first
James Ketrenos43f66a62005-03-25 12:31:53 -06004758 * INDEX position, and WRITE to the last (READ - 1 wrapped)
4759 *
4760 * When the firmware places a packet in a buffer it will advance the READ index
4761 * and fire the RX interrupt. The driver can then query the READ index and
4762 * process as many packets as possible, moving the WRITE index forward as it
4763 * resets the Rx queue buffers with new memory.
Jeff Garzikbf794512005-07-31 13:07:26 -04004764 *
James Ketrenos43f66a62005-03-25 12:31:53 -06004765 * The management in the driver is as follows:
Jeff Garzikbf794512005-07-31 13:07:26 -04004766 * + A list of pre-allocated SKBs is stored in ipw->rxq->rx_free. When
James Ketrenos43f66a62005-03-25 12:31:53 -06004767 * ipw->rxq->free_count drops to or below RX_LOW_WATERMARK, work is scheduled
Jeff Garzikbf794512005-07-31 13:07:26 -04004768 * to replensish the ipw->rxq->rx_free.
James Ketrenos43f66a62005-03-25 12:31:53 -06004769 * + In ipw_rx_queue_replenish (scheduled) if 'processed' != 'read' then the
4770 * ipw->rxq is replenished and the READ INDEX is updated (updating the
4771 * 'processed' and 'read' driver indexes as well)
4772 * + A received packet is processed and handed to the kernel network stack,
4773 * detached from the ipw->rxq. The driver 'processed' index is updated.
4774 * + The Host/Firmware ipw->rxq is replenished at tasklet time from the rx_free
Jeff Garzikbf794512005-07-31 13:07:26 -04004775 * list. If there are no allocated buffers in ipw->rxq->rx_free, the READ
4776 * INDEX is not incremented and ipw->status(RX_STALLED) is set. If there
James Ketrenos43f66a62005-03-25 12:31:53 -06004777 * were enough free buffers and RX_STALLED is set it is cleared.
4778 *
4779 *
4780 * Driver sequence:
4781 *
Jeff Garzikbf794512005-07-31 13:07:26 -04004782 * ipw_rx_queue_alloc() Allocates rx_free
James Ketrenos43f66a62005-03-25 12:31:53 -06004783 * ipw_rx_queue_replenish() Replenishes rx_free list from rx_used, and calls
4784 * ipw_rx_queue_restock
4785 * ipw_rx_queue_restock() Moves available buffers from rx_free into Rx
4786 * queue, updates firmware pointers, and updates
4787 * the WRITE index. If insufficient rx_free buffers
4788 * are available, schedules ipw_rx_queue_replenish
4789 *
4790 * -- enable interrupts --
4791 * ISR - ipw_rx() Detach ipw_rx_mem_buffers from pool up to the
Jeff Garzikbf794512005-07-31 13:07:26 -04004792 * READ INDEX, detaching the SKB from the pool.
James Ketrenos43f66a62005-03-25 12:31:53 -06004793 * Moves the packet buffer from queue to rx_used.
4794 * Calls ipw_rx_queue_restock to refill any empty
4795 * slots.
4796 * ...
4797 *
4798 */
4799
Jeff Garzikbf794512005-07-31 13:07:26 -04004800/*
James Ketrenos43f66a62005-03-25 12:31:53 -06004801 * If there are slots in the RX queue that need to be restocked,
4802 * and we have free pre-allocated buffers, fill the ranks as much
4803 * as we can pulling from rx_free.
4804 *
4805 * This moves the 'write' index forward to catch up with 'processed', and
4806 * also updates the memory address in the firmware to reference the new
4807 * target buffer.
4808 */
4809static void ipw_rx_queue_restock(struct ipw_priv *priv)
4810{
4811 struct ipw_rx_queue *rxq = priv->rxq;
4812 struct list_head *element;
4813 struct ipw_rx_mem_buffer *rxb;
4814 unsigned long flags;
4815 int write;
4816
4817 spin_lock_irqsave(&rxq->lock, flags);
4818 write = rxq->write;
4819 while ((rxq->write != rxq->processed) && (rxq->free_count)) {
4820 element = rxq->rx_free.next;
4821 rxb = list_entry(element, struct ipw_rx_mem_buffer, list);
4822 list_del(element);
4823
James Ketrenosb095c382005-08-24 22:04:42 -05004824 ipw_write32(priv, IPW_RFDS_TABLE_LOWER + rxq->write * RFD_SIZE,
James Ketrenos43f66a62005-03-25 12:31:53 -06004825 rxb->dma_addr);
4826 rxq->queue[rxq->write] = rxb;
4827 rxq->write = (rxq->write + 1) % RX_QUEUE_SIZE;
4828 rxq->free_count--;
4829 }
4830 spin_unlock_irqrestore(&rxq->lock, flags);
4831
Jeff Garzikbf794512005-07-31 13:07:26 -04004832 /* If the pre-allocated buffer pool is dropping low, schedule to
James Ketrenos43f66a62005-03-25 12:31:53 -06004833 * refill it */
4834 if (rxq->free_count <= RX_LOW_WATERMARK)
4835 queue_work(priv->workqueue, &priv->rx_replenish);
4836
4837 /* If we've added more space for the firmware to place data, tell it */
Jeff Garzikbf794512005-07-31 13:07:26 -04004838 if (write != rxq->write)
James Ketrenosb095c382005-08-24 22:04:42 -05004839 ipw_write32(priv, IPW_RX_WRITE_INDEX, rxq->write);
James Ketrenos43f66a62005-03-25 12:31:53 -06004840}
4841
4842/*
4843 * Move all used packet from rx_used to rx_free, allocating a new SKB for each.
Jeff Garzikbf794512005-07-31 13:07:26 -04004844 * Also restock the Rx queue via ipw_rx_queue_restock.
4845 *
James Ketrenos43f66a62005-03-25 12:31:53 -06004846 * This is called as a scheduled work item (except for during intialization)
4847 */
4848static void ipw_rx_queue_replenish(void *data)
4849{
4850 struct ipw_priv *priv = data;
4851 struct ipw_rx_queue *rxq = priv->rxq;
4852 struct list_head *element;
4853 struct ipw_rx_mem_buffer *rxb;
4854 unsigned long flags;
4855
4856 spin_lock_irqsave(&rxq->lock, flags);
4857 while (!list_empty(&rxq->rx_used)) {
4858 element = rxq->rx_used.next;
4859 rxb = list_entry(element, struct ipw_rx_mem_buffer, list);
James Ketrenosb095c382005-08-24 22:04:42 -05004860 rxb->skb = alloc_skb(IPW_RX_BUF_SIZE, GFP_ATOMIC);
James Ketrenos43f66a62005-03-25 12:31:53 -06004861 if (!rxb->skb) {
4862 printk(KERN_CRIT "%s: Can not allocate SKB buffers.\n",
4863 priv->net_dev->name);
4864 /* We don't reschedule replenish work here -- we will
4865 * call the restock method and if it still needs
4866 * more buffers it will schedule replenish */
4867 break;
4868 }
4869 list_del(element);
Jeff Garzikbf794512005-07-31 13:07:26 -04004870
James Ketrenos43f66a62005-03-25 12:31:53 -06004871 rxb->rxb = (struct ipw_rx_buffer *)rxb->skb->data;
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004872 rxb->dma_addr =
4873 pci_map_single(priv->pci_dev, rxb->skb->data,
James Ketrenosb095c382005-08-24 22:04:42 -05004874 IPW_RX_BUF_SIZE, PCI_DMA_FROMDEVICE);
Jeff Garzikbf794512005-07-31 13:07:26 -04004875
James Ketrenos43f66a62005-03-25 12:31:53 -06004876 list_add_tail(&rxb->list, &rxq->rx_free);
4877 rxq->free_count++;
4878 }
4879 spin_unlock_irqrestore(&rxq->lock, flags);
4880
4881 ipw_rx_queue_restock(priv);
4882}
4883
James Ketrenosc848d0a2005-08-24 21:56:24 -05004884static void ipw_bg_rx_queue_replenish(void *data)
4885{
4886 struct ipw_priv *priv = data;
4887 down(&priv->sem);
4888 ipw_rx_queue_replenish(data);
4889 up(&priv->sem);
4890}
4891
James Ketrenos43f66a62005-03-25 12:31:53 -06004892/* Assumes that the skb field of the buffers in 'pool' is kept accurate.
4893 * If an SKB has been detached, the POOL needs to have it's SKB set to NULL
Jeff Garzikbf794512005-07-31 13:07:26 -04004894 * This free routine walks the list of POOL entries and if SKB is set to
James Ketrenos43f66a62005-03-25 12:31:53 -06004895 * non NULL it is unmapped and freed
4896 */
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004897static void ipw_rx_queue_free(struct ipw_priv *priv, struct ipw_rx_queue *rxq)
James Ketrenos43f66a62005-03-25 12:31:53 -06004898{
4899 int i;
4900
4901 if (!rxq)
4902 return;
Jeff Garzikbf794512005-07-31 13:07:26 -04004903
James Ketrenos43f66a62005-03-25 12:31:53 -06004904 for (i = 0; i < RX_QUEUE_SIZE + RX_FREE_BUFFERS; i++) {
4905 if (rxq->pool[i].skb != NULL) {
4906 pci_unmap_single(priv->pci_dev, rxq->pool[i].dma_addr,
James Ketrenosb095c382005-08-24 22:04:42 -05004907 IPW_RX_BUF_SIZE, PCI_DMA_FROMDEVICE);
James Ketrenos43f66a62005-03-25 12:31:53 -06004908 dev_kfree_skb(rxq->pool[i].skb);
4909 }
4910 }
4911
4912 kfree(rxq);
4913}
4914
4915static struct ipw_rx_queue *ipw_rx_queue_alloc(struct ipw_priv *priv)
4916{
4917 struct ipw_rx_queue *rxq;
4918 int i;
4919
4920 rxq = (struct ipw_rx_queue *)kmalloc(sizeof(*rxq), GFP_KERNEL);
Panagiotis Issarisad18b0e2005-09-05 04:14:10 +02004921 if (unlikely(!rxq)) {
4922 IPW_ERROR("memory allocation failed\n");
4923 return NULL;
4924 }
James Ketrenos43f66a62005-03-25 12:31:53 -06004925 memset(rxq, 0, sizeof(*rxq));
4926 spin_lock_init(&rxq->lock);
4927 INIT_LIST_HEAD(&rxq->rx_free);
4928 INIT_LIST_HEAD(&rxq->rx_used);
4929
4930 /* Fill the rx_used queue with _all_ of the Rx buffers */
Jeff Garzikbf794512005-07-31 13:07:26 -04004931 for (i = 0; i < RX_FREE_BUFFERS + RX_QUEUE_SIZE; i++)
James Ketrenos43f66a62005-03-25 12:31:53 -06004932 list_add_tail(&rxq->pool[i].list, &rxq->rx_used);
4933
4934 /* Set us so that we have processed and used all buffers, but have
4935 * not restocked the Rx queue with fresh buffers */
4936 rxq->read = rxq->write = 0;
4937 rxq->processed = RX_QUEUE_SIZE - 1;
4938 rxq->free_count = 0;
4939
4940 return rxq;
4941}
4942
4943static int ipw_is_rate_in_mask(struct ipw_priv *priv, int ieee_mode, u8 rate)
4944{
4945 rate &= ~IEEE80211_BASIC_RATE_MASK;
4946 if (ieee_mode == IEEE_A) {
4947 switch (rate) {
Jeff Garzikbf794512005-07-31 13:07:26 -04004948 case IEEE80211_OFDM_RATE_6MB:
4949 return priv->rates_mask & IEEE80211_OFDM_RATE_6MB_MASK ?
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004950 1 : 0;
Jeff Garzikbf794512005-07-31 13:07:26 -04004951 case IEEE80211_OFDM_RATE_9MB:
4952 return priv->rates_mask & IEEE80211_OFDM_RATE_9MB_MASK ?
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004953 1 : 0;
Jeff Garzikbf794512005-07-31 13:07:26 -04004954 case IEEE80211_OFDM_RATE_12MB:
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004955 return priv->
4956 rates_mask & IEEE80211_OFDM_RATE_12MB_MASK ? 1 : 0;
Jeff Garzikbf794512005-07-31 13:07:26 -04004957 case IEEE80211_OFDM_RATE_18MB:
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004958 return priv->
4959 rates_mask & IEEE80211_OFDM_RATE_18MB_MASK ? 1 : 0;
Jeff Garzikbf794512005-07-31 13:07:26 -04004960 case IEEE80211_OFDM_RATE_24MB:
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004961 return priv->
4962 rates_mask & IEEE80211_OFDM_RATE_24MB_MASK ? 1 : 0;
Jeff Garzikbf794512005-07-31 13:07:26 -04004963 case IEEE80211_OFDM_RATE_36MB:
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004964 return priv->
4965 rates_mask & IEEE80211_OFDM_RATE_36MB_MASK ? 1 : 0;
Jeff Garzikbf794512005-07-31 13:07:26 -04004966 case IEEE80211_OFDM_RATE_48MB:
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004967 return priv->
4968 rates_mask & IEEE80211_OFDM_RATE_48MB_MASK ? 1 : 0;
Jeff Garzikbf794512005-07-31 13:07:26 -04004969 case IEEE80211_OFDM_RATE_54MB:
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004970 return priv->
4971 rates_mask & IEEE80211_OFDM_RATE_54MB_MASK ? 1 : 0;
James Ketrenos43f66a62005-03-25 12:31:53 -06004972 default:
4973 return 0;
4974 }
4975 }
Jeff Garzikbf794512005-07-31 13:07:26 -04004976
James Ketrenos43f66a62005-03-25 12:31:53 -06004977 /* B and G mixed */
4978 switch (rate) {
Jeff Garzikbf794512005-07-31 13:07:26 -04004979 case IEEE80211_CCK_RATE_1MB:
James Ketrenos43f66a62005-03-25 12:31:53 -06004980 return priv->rates_mask & IEEE80211_CCK_RATE_1MB_MASK ? 1 : 0;
Jeff Garzikbf794512005-07-31 13:07:26 -04004981 case IEEE80211_CCK_RATE_2MB:
James Ketrenos43f66a62005-03-25 12:31:53 -06004982 return priv->rates_mask & IEEE80211_CCK_RATE_2MB_MASK ? 1 : 0;
Jeff Garzikbf794512005-07-31 13:07:26 -04004983 case IEEE80211_CCK_RATE_5MB:
James Ketrenos43f66a62005-03-25 12:31:53 -06004984 return priv->rates_mask & IEEE80211_CCK_RATE_5MB_MASK ? 1 : 0;
Jeff Garzikbf794512005-07-31 13:07:26 -04004985 case IEEE80211_CCK_RATE_11MB:
James Ketrenos43f66a62005-03-25 12:31:53 -06004986 return priv->rates_mask & IEEE80211_CCK_RATE_11MB_MASK ? 1 : 0;
4987 }
4988
4989 /* If we are limited to B modulations, bail at this point */
4990 if (ieee_mode == IEEE_B)
4991 return 0;
4992
4993 /* G */
4994 switch (rate) {
Jeff Garzikbf794512005-07-31 13:07:26 -04004995 case IEEE80211_OFDM_RATE_6MB:
James Ketrenos43f66a62005-03-25 12:31:53 -06004996 return priv->rates_mask & IEEE80211_OFDM_RATE_6MB_MASK ? 1 : 0;
Jeff Garzikbf794512005-07-31 13:07:26 -04004997 case IEEE80211_OFDM_RATE_9MB:
James Ketrenos43f66a62005-03-25 12:31:53 -06004998 return priv->rates_mask & IEEE80211_OFDM_RATE_9MB_MASK ? 1 : 0;
Jeff Garzikbf794512005-07-31 13:07:26 -04004999 case IEEE80211_OFDM_RATE_12MB:
James Ketrenos43f66a62005-03-25 12:31:53 -06005000 return priv->rates_mask & IEEE80211_OFDM_RATE_12MB_MASK ? 1 : 0;
Jeff Garzikbf794512005-07-31 13:07:26 -04005001 case IEEE80211_OFDM_RATE_18MB:
James Ketrenos43f66a62005-03-25 12:31:53 -06005002 return priv->rates_mask & IEEE80211_OFDM_RATE_18MB_MASK ? 1 : 0;
Jeff Garzikbf794512005-07-31 13:07:26 -04005003 case IEEE80211_OFDM_RATE_24MB:
James Ketrenos43f66a62005-03-25 12:31:53 -06005004 return priv->rates_mask & IEEE80211_OFDM_RATE_24MB_MASK ? 1 : 0;
Jeff Garzikbf794512005-07-31 13:07:26 -04005005 case IEEE80211_OFDM_RATE_36MB:
James Ketrenos43f66a62005-03-25 12:31:53 -06005006 return priv->rates_mask & IEEE80211_OFDM_RATE_36MB_MASK ? 1 : 0;
Jeff Garzikbf794512005-07-31 13:07:26 -04005007 case IEEE80211_OFDM_RATE_48MB:
James Ketrenos43f66a62005-03-25 12:31:53 -06005008 return priv->rates_mask & IEEE80211_OFDM_RATE_48MB_MASK ? 1 : 0;
Jeff Garzikbf794512005-07-31 13:07:26 -04005009 case IEEE80211_OFDM_RATE_54MB:
James Ketrenos43f66a62005-03-25 12:31:53 -06005010 return priv->rates_mask & IEEE80211_OFDM_RATE_54MB_MASK ? 1 : 0;
5011 }
5012
5013 return 0;
5014}
5015
Jeff Garzikbf794512005-07-31 13:07:26 -04005016static int ipw_compatible_rates(struct ipw_priv *priv,
James Ketrenos43f66a62005-03-25 12:31:53 -06005017 const struct ieee80211_network *network,
5018 struct ipw_supported_rates *rates)
5019{
5020 int num_rates, i;
5021
5022 memset(rates, 0, sizeof(*rates));
Jeff Garzik0edd5b42005-09-07 00:48:31 -04005023 num_rates = min(network->rates_len, (u8) IPW_MAX_RATES);
James Ketrenos43f66a62005-03-25 12:31:53 -06005024 rates->num_rates = 0;
5025 for (i = 0; i < num_rates; i++) {
James Ketrenosa613bff2005-08-24 21:43:11 -05005026 if (!ipw_is_rate_in_mask(priv, network->mode,
5027 network->rates[i])) {
5028
James Ketrenosea2b26e2005-08-24 21:25:16 -05005029 if (network->rates[i] & IEEE80211_BASIC_RATE_MASK) {
James Ketrenosa613bff2005-08-24 21:43:11 -05005030 IPW_DEBUG_SCAN("Adding masked mandatory "
5031 "rate %02X\n",
5032 network->rates[i]);
5033 rates->supported_rates[rates->num_rates++] =
5034 network->rates[i];
5035 continue;
James Ketrenosea2b26e2005-08-24 21:25:16 -05005036 }
5037
James Ketrenos43f66a62005-03-25 12:31:53 -06005038 IPW_DEBUG_SCAN("Rate %02X masked : 0x%08X\n",
5039 network->rates[i], priv->rates_mask);
5040 continue;
5041 }
Jeff Garzikbf794512005-07-31 13:07:26 -04005042
James Ketrenos43f66a62005-03-25 12:31:53 -06005043 rates->supported_rates[rates->num_rates++] = network->rates[i];
5044 }
5045
James Ketrenosa613bff2005-08-24 21:43:11 -05005046 num_rates = min(network->rates_ex_len,
5047 (u8) (IPW_MAX_RATES - num_rates));
James Ketrenos43f66a62005-03-25 12:31:53 -06005048 for (i = 0; i < num_rates; i++) {
James Ketrenosa613bff2005-08-24 21:43:11 -05005049 if (!ipw_is_rate_in_mask(priv, network->mode,
5050 network->rates_ex[i])) {
James Ketrenosea2b26e2005-08-24 21:25:16 -05005051 if (network->rates_ex[i] & IEEE80211_BASIC_RATE_MASK) {
James Ketrenosa613bff2005-08-24 21:43:11 -05005052 IPW_DEBUG_SCAN("Adding masked mandatory "
5053 "rate %02X\n",
5054 network->rates_ex[i]);
5055 rates->supported_rates[rates->num_rates++] =
5056 network->rates[i];
5057 continue;
James Ketrenosea2b26e2005-08-24 21:25:16 -05005058 }
5059
James Ketrenos43f66a62005-03-25 12:31:53 -06005060 IPW_DEBUG_SCAN("Rate %02X masked : 0x%08X\n",
5061 network->rates_ex[i], priv->rates_mask);
5062 continue;
5063 }
Jeff Garzikbf794512005-07-31 13:07:26 -04005064
Jeff Garzik0edd5b42005-09-07 00:48:31 -04005065 rates->supported_rates[rates->num_rates++] =
5066 network->rates_ex[i];
James Ketrenos43f66a62005-03-25 12:31:53 -06005067 }
5068
James Ketrenosea2b26e2005-08-24 21:25:16 -05005069 return 1;
James Ketrenos43f66a62005-03-25 12:31:53 -06005070}
5071
5072static inline void ipw_copy_rates(struct ipw_supported_rates *dest,
5073 const struct ipw_supported_rates *src)
5074{
5075 u8 i;
5076 for (i = 0; i < src->num_rates; i++)
5077 dest->supported_rates[i] = src->supported_rates[i];
5078 dest->num_rates = src->num_rates;
5079}
5080
5081/* TODO: Look at sniffed packets in the air to determine if the basic rate
5082 * mask should ever be used -- right now all callers to add the scan rates are
5083 * set with the modulation = CCK, so BASIC_RATE_MASK is never set... */
5084static void ipw_add_cck_scan_rates(struct ipw_supported_rates *rates,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04005085 u8 modulation, u32 rate_mask)
James Ketrenos43f66a62005-03-25 12:31:53 -06005086{
Jeff Garzikbf794512005-07-31 13:07:26 -04005087 u8 basic_mask = (IEEE80211_OFDM_MODULATION == modulation) ?
Jeff Garzik0edd5b42005-09-07 00:48:31 -04005088 IEEE80211_BASIC_RATE_MASK : 0;
Jeff Garzikbf794512005-07-31 13:07:26 -04005089
James Ketrenos43f66a62005-03-25 12:31:53 -06005090 if (rate_mask & IEEE80211_CCK_RATE_1MB_MASK)
Jeff Garzikbf794512005-07-31 13:07:26 -04005091 rates->supported_rates[rates->num_rates++] =
Jeff Garzik0edd5b42005-09-07 00:48:31 -04005092 IEEE80211_BASIC_RATE_MASK | IEEE80211_CCK_RATE_1MB;
James Ketrenos43f66a62005-03-25 12:31:53 -06005093
5094 if (rate_mask & IEEE80211_CCK_RATE_2MB_MASK)
Jeff Garzikbf794512005-07-31 13:07:26 -04005095 rates->supported_rates[rates->num_rates++] =
Jeff Garzik0edd5b42005-09-07 00:48:31 -04005096 IEEE80211_BASIC_RATE_MASK | IEEE80211_CCK_RATE_2MB;
James Ketrenos43f66a62005-03-25 12:31:53 -06005097
5098 if (rate_mask & IEEE80211_CCK_RATE_5MB_MASK)
Jeff Garzikbf794512005-07-31 13:07:26 -04005099 rates->supported_rates[rates->num_rates++] = basic_mask |
Jeff Garzik0edd5b42005-09-07 00:48:31 -04005100 IEEE80211_CCK_RATE_5MB;
James Ketrenos43f66a62005-03-25 12:31:53 -06005101
5102 if (rate_mask & IEEE80211_CCK_RATE_11MB_MASK)
Jeff Garzikbf794512005-07-31 13:07:26 -04005103 rates->supported_rates[rates->num_rates++] = basic_mask |
Jeff Garzik0edd5b42005-09-07 00:48:31 -04005104 IEEE80211_CCK_RATE_11MB;
James Ketrenos43f66a62005-03-25 12:31:53 -06005105}
5106
5107static void ipw_add_ofdm_scan_rates(struct ipw_supported_rates *rates,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04005108 u8 modulation, u32 rate_mask)
James Ketrenos43f66a62005-03-25 12:31:53 -06005109{
Jeff Garzikbf794512005-07-31 13:07:26 -04005110 u8 basic_mask = (IEEE80211_OFDM_MODULATION == modulation) ?
Jeff Garzik0edd5b42005-09-07 00:48:31 -04005111 IEEE80211_BASIC_RATE_MASK : 0;
James Ketrenos43f66a62005-03-25 12:31:53 -06005112
5113 if (rate_mask & IEEE80211_OFDM_RATE_6MB_MASK)
Jeff Garzikbf794512005-07-31 13:07:26 -04005114 rates->supported_rates[rates->num_rates++] = basic_mask |
Jeff Garzik0edd5b42005-09-07 00:48:31 -04005115 IEEE80211_OFDM_RATE_6MB;
James Ketrenos43f66a62005-03-25 12:31:53 -06005116
5117 if (rate_mask & IEEE80211_OFDM_RATE_9MB_MASK)
Jeff Garzikbf794512005-07-31 13:07:26 -04005118 rates->supported_rates[rates->num_rates++] =
Jeff Garzik0edd5b42005-09-07 00:48:31 -04005119 IEEE80211_OFDM_RATE_9MB;
James Ketrenos43f66a62005-03-25 12:31:53 -06005120
5121 if (rate_mask & IEEE80211_OFDM_RATE_12MB_MASK)
Jeff Garzikbf794512005-07-31 13:07:26 -04005122 rates->supported_rates[rates->num_rates++] = basic_mask |
Jeff Garzik0edd5b42005-09-07 00:48:31 -04005123 IEEE80211_OFDM_RATE_12MB;
James Ketrenos43f66a62005-03-25 12:31:53 -06005124
5125 if (rate_mask & IEEE80211_OFDM_RATE_18MB_MASK)
Jeff Garzikbf794512005-07-31 13:07:26 -04005126 rates->supported_rates[rates->num_rates++] =
Jeff Garzik0edd5b42005-09-07 00:48:31 -04005127 IEEE80211_OFDM_RATE_18MB;
James Ketrenos43f66a62005-03-25 12:31:53 -06005128
5129 if (rate_mask & IEEE80211_OFDM_RATE_24MB_MASK)
Jeff Garzikbf794512005-07-31 13:07:26 -04005130 rates->supported_rates[rates->num_rates++] = basic_mask |
Jeff Garzik0edd5b42005-09-07 00:48:31 -04005131 IEEE80211_OFDM_RATE_24MB;
James Ketrenos43f66a62005-03-25 12:31:53 -06005132
5133 if (rate_mask & IEEE80211_OFDM_RATE_36MB_MASK)
Jeff Garzikbf794512005-07-31 13:07:26 -04005134 rates->supported_rates[rates->num_rates++] =
Jeff Garzik0edd5b42005-09-07 00:48:31 -04005135 IEEE80211_OFDM_RATE_36MB;
James Ketrenos43f66a62005-03-25 12:31:53 -06005136
5137 if (rate_mask & IEEE80211_OFDM_RATE_48MB_MASK)
Jeff Garzikbf794512005-07-31 13:07:26 -04005138 rates->supported_rates[rates->num_rates++] =
Jeff Garzik0edd5b42005-09-07 00:48:31 -04005139 IEEE80211_OFDM_RATE_48MB;
James Ketrenos43f66a62005-03-25 12:31:53 -06005140
5141 if (rate_mask & IEEE80211_OFDM_RATE_54MB_MASK)
Jeff Garzikbf794512005-07-31 13:07:26 -04005142 rates->supported_rates[rates->num_rates++] =
Jeff Garzik0edd5b42005-09-07 00:48:31 -04005143 IEEE80211_OFDM_RATE_54MB;
James Ketrenos43f66a62005-03-25 12:31:53 -06005144}
5145
5146struct ipw_network_match {
5147 struct ieee80211_network *network;
5148 struct ipw_supported_rates rates;
5149};
5150
James Ketrenosc848d0a2005-08-24 21:56:24 -05005151static int ipw_find_adhoc_network(struct ipw_priv *priv,
5152 struct ipw_network_match *match,
5153 struct ieee80211_network *network,
5154 int roaming)
5155{
5156 struct ipw_supported_rates rates;
5157
5158 /* Verify that this network's capability is compatible with the
5159 * current mode (AdHoc or Infrastructure) */
5160 if ((priv->ieee->iw_mode == IW_MODE_ADHOC &&
5161 !(network->capability & WLAN_CAPABILITY_IBSS))) {
5162 IPW_DEBUG_MERGE("Network '%s (" MAC_FMT ")' excluded due to "
5163 "capability mismatch.\n",
5164 escape_essid(network->ssid, network->ssid_len),
5165 MAC_ARG(network->bssid));
5166 return 0;
5167 }
5168
5169 /* If we do not have an ESSID for this AP, we can not associate with
5170 * it */
5171 if (network->flags & NETWORK_EMPTY_ESSID) {
5172 IPW_DEBUG_MERGE("Network '%s (" MAC_FMT ")' excluded "
5173 "because of hidden ESSID.\n",
5174 escape_essid(network->ssid, network->ssid_len),
5175 MAC_ARG(network->bssid));
5176 return 0;
5177 }
5178
5179 if (unlikely(roaming)) {
5180 /* If we are roaming, then ensure check if this is a valid
5181 * network to try and roam to */
5182 if ((network->ssid_len != match->network->ssid_len) ||
5183 memcmp(network->ssid, match->network->ssid,
5184 network->ssid_len)) {
5185 IPW_DEBUG_MERGE("Netowrk '%s (" MAC_FMT ")' excluded "
5186 "because of non-network ESSID.\n",
5187 escape_essid(network->ssid,
5188 network->ssid_len),
5189 MAC_ARG(network->bssid));
5190 return 0;
5191 }
5192 } else {
5193 /* If an ESSID has been configured then compare the broadcast
5194 * ESSID to ours */
5195 if ((priv->config & CFG_STATIC_ESSID) &&
5196 ((network->ssid_len != priv->essid_len) ||
5197 memcmp(network->ssid, priv->essid,
5198 min(network->ssid_len, priv->essid_len)))) {
5199 char escaped[IW_ESSID_MAX_SIZE * 2 + 1];
James Ketrenosafbf30a2005-08-25 00:05:33 -05005200
James Ketrenosc848d0a2005-08-24 21:56:24 -05005201 strncpy(escaped,
5202 escape_essid(network->ssid, network->ssid_len),
5203 sizeof(escaped));
5204 IPW_DEBUG_MERGE("Network '%s (" MAC_FMT ")' excluded "
5205 "because of ESSID mismatch: '%s'.\n",
5206 escaped, MAC_ARG(network->bssid),
5207 escape_essid(priv->essid,
5208 priv->essid_len));
5209 return 0;
5210 }
5211 }
5212
5213 /* If the old network rate is better than this one, don't bother
5214 * testing everything else. */
5215
5216 if (network->time_stamp[0] < match->network->time_stamp[0]) {
James Ketrenosafbf30a2005-08-25 00:05:33 -05005217 IPW_DEBUG_MERGE("Network '%s excluded because newer than "
5218 "current network.\n",
5219 escape_essid(match->network->ssid,
5220 match->network->ssid_len));
James Ketrenosc848d0a2005-08-24 21:56:24 -05005221 return 0;
5222 } else if (network->time_stamp[1] < match->network->time_stamp[1]) {
James Ketrenosafbf30a2005-08-25 00:05:33 -05005223 IPW_DEBUG_MERGE("Network '%s excluded because newer than "
5224 "current network.\n",
5225 escape_essid(match->network->ssid,
5226 match->network->ssid_len));
James Ketrenosc848d0a2005-08-24 21:56:24 -05005227 return 0;
5228 }
5229
5230 /* Now go through and see if the requested network is valid... */
5231 if (priv->ieee->scan_age != 0 &&
5232 time_after(jiffies, network->last_scanned + priv->ieee->scan_age)) {
5233 IPW_DEBUG_MERGE("Network '%s (" MAC_FMT ")' excluded "
5234 "because of age: %lums.\n",
5235 escape_essid(network->ssid, network->ssid_len),
5236 MAC_ARG(network->bssid),
James Ketrenosafbf30a2005-08-25 00:05:33 -05005237 1000 * (jiffies - network->last_scanned) / HZ);
James Ketrenosc848d0a2005-08-24 21:56:24 -05005238 return 0;
5239 }
5240
5241 if ((priv->config & CFG_STATIC_CHANNEL) &&
5242 (network->channel != priv->channel)) {
5243 IPW_DEBUG_MERGE("Network '%s (" MAC_FMT ")' excluded "
5244 "because of channel mismatch: %d != %d.\n",
5245 escape_essid(network->ssid, network->ssid_len),
5246 MAC_ARG(network->bssid),
5247 network->channel, priv->channel);
5248 return 0;
5249 }
5250
5251 /* Verify privacy compatability */
5252 if (((priv->capability & CAP_PRIVACY_ON) ? 1 : 0) !=
5253 ((network->capability & WLAN_CAPABILITY_PRIVACY) ? 1 : 0)) {
5254 IPW_DEBUG_MERGE("Network '%s (" MAC_FMT ")' excluded "
5255 "because of privacy mismatch: %s != %s.\n",
5256 escape_essid(network->ssid, network->ssid_len),
5257 MAC_ARG(network->bssid),
James Ketrenosafbf30a2005-08-25 00:05:33 -05005258 priv->
5259 capability & CAP_PRIVACY_ON ? "on" : "off",
5260 network->
5261 capability & WLAN_CAPABILITY_PRIVACY ? "on" :
5262 "off");
James Ketrenosc848d0a2005-08-24 21:56:24 -05005263 return 0;
5264 }
5265
5266 if (!memcmp(network->bssid, priv->bssid, ETH_ALEN)) {
5267 IPW_DEBUG_MERGE("Network '%s (" MAC_FMT ")' excluded "
5268 "because of the same BSSID match: " MAC_FMT
5269 ".\n", escape_essid(network->ssid,
5270 network->ssid_len),
5271 MAC_ARG(network->bssid), MAC_ARG(priv->bssid));
5272 return 0;
5273 }
5274
5275 /* Filter out any incompatible freq / mode combinations */
5276 if (!ieee80211_is_valid_mode(priv->ieee, network->mode)) {
5277 IPW_DEBUG_MERGE("Network '%s (" MAC_FMT ")' excluded "
5278 "because of invalid frequency/mode "
5279 "combination.\n",
5280 escape_essid(network->ssid, network->ssid_len),
5281 MAC_ARG(network->bssid));
5282 return 0;
5283 }
5284
5285 /* Ensure that the rates supported by the driver are compatible with
5286 * this AP, including verification of basic rates (mandatory) */
5287 if (!ipw_compatible_rates(priv, network, &rates)) {
5288 IPW_DEBUG_MERGE("Network '%s (" MAC_FMT ")' excluded "
5289 "because configured rate mask excludes "
5290 "AP mandatory rate.\n",
5291 escape_essid(network->ssid, network->ssid_len),
5292 MAC_ARG(network->bssid));
5293 return 0;
5294 }
5295
5296 if (rates.num_rates == 0) {
5297 IPW_DEBUG_MERGE("Network '%s (" MAC_FMT ")' excluded "
5298 "because of no compatible rates.\n",
5299 escape_essid(network->ssid, network->ssid_len),
5300 MAC_ARG(network->bssid));
5301 return 0;
5302 }
5303
5304 /* TODO: Perform any further minimal comparititive tests. We do not
5305 * want to put too much policy logic here; intelligent scan selection
5306 * should occur within a generic IEEE 802.11 user space tool. */
5307
5308 /* Set up 'new' AP to this network */
5309 ipw_copy_rates(&match->rates, &rates);
5310 match->network = network;
5311 IPW_DEBUG_MERGE("Network '%s (" MAC_FMT ")' is a viable match.\n",
5312 escape_essid(network->ssid, network->ssid_len),
5313 MAC_ARG(network->bssid));
5314
5315 return 1;
5316}
5317
5318static void ipw_merge_adhoc_network(void *data)
5319{
5320 struct ipw_priv *priv = data;
5321 struct ieee80211_network *network = NULL;
5322 struct ipw_network_match match = {
5323 .network = priv->assoc_network
5324 };
5325
James Ketrenosafbf30a2005-08-25 00:05:33 -05005326 if ((priv->status & STATUS_ASSOCIATED) &&
5327 (priv->ieee->iw_mode == IW_MODE_ADHOC)) {
James Ketrenosc848d0a2005-08-24 21:56:24 -05005328 /* First pass through ROAM process -- look for a better
5329 * network */
5330 unsigned long flags;
5331
5332 spin_lock_irqsave(&priv->ieee->lock, flags);
5333 list_for_each_entry(network, &priv->ieee->network_list, list) {
5334 if (network != priv->assoc_network)
5335 ipw_find_adhoc_network(priv, &match, network,
5336 1);
5337 }
5338 spin_unlock_irqrestore(&priv->ieee->lock, flags);
5339
5340 if (match.network == priv->assoc_network) {
5341 IPW_DEBUG_MERGE("No better ADHOC in this network to "
5342 "merge to.\n");
5343 return;
5344 }
5345
5346 down(&priv->sem);
5347 if ((priv->ieee->iw_mode == IW_MODE_ADHOC)) {
5348 IPW_DEBUG_MERGE("remove network %s\n",
5349 escape_essid(priv->essid,
5350 priv->essid_len));
5351 ipw_remove_current_network(priv);
5352 }
5353
5354 ipw_disassociate(priv);
5355 priv->assoc_network = match.network;
5356 up(&priv->sem);
5357 return;
5358 }
James Ketrenosc848d0a2005-08-24 21:56:24 -05005359}
5360
Jeff Garzik0edd5b42005-09-07 00:48:31 -04005361static int ipw_best_network(struct ipw_priv *priv,
5362 struct ipw_network_match *match,
5363 struct ieee80211_network *network, int roaming)
James Ketrenos43f66a62005-03-25 12:31:53 -06005364{
5365 struct ipw_supported_rates rates;
5366
5367 /* Verify that this network's capability is compatible with the
5368 * current mode (AdHoc or Infrastructure) */
5369 if ((priv->ieee->iw_mode == IW_MODE_INFRA &&
Jouni Malinen24743852005-08-14 20:59:59 -07005370 !(network->capability & WLAN_CAPABILITY_ESS)) ||
James Ketrenos43f66a62005-03-25 12:31:53 -06005371 (priv->ieee->iw_mode == IW_MODE_ADHOC &&
5372 !(network->capability & WLAN_CAPABILITY_IBSS))) {
5373 IPW_DEBUG_ASSOC("Network '%s (" MAC_FMT ")' excluded due to "
Jeff Garzikbf794512005-07-31 13:07:26 -04005374 "capability mismatch.\n",
James Ketrenos43f66a62005-03-25 12:31:53 -06005375 escape_essid(network->ssid, network->ssid_len),
5376 MAC_ARG(network->bssid));
5377 return 0;
5378 }
5379
5380 /* If we do not have an ESSID for this AP, we can not associate with
5381 * it */
5382 if (network->flags & NETWORK_EMPTY_ESSID) {
5383 IPW_DEBUG_ASSOC("Network '%s (" MAC_FMT ")' excluded "
5384 "because of hidden ESSID.\n",
5385 escape_essid(network->ssid, network->ssid_len),
5386 MAC_ARG(network->bssid));
5387 return 0;
5388 }
Jeff Garzikbf794512005-07-31 13:07:26 -04005389
James Ketrenos43f66a62005-03-25 12:31:53 -06005390 if (unlikely(roaming)) {
5391 /* If we are roaming, then ensure check if this is a valid
5392 * network to try and roam to */
5393 if ((network->ssid_len != match->network->ssid_len) ||
Jeff Garzikbf794512005-07-31 13:07:26 -04005394 memcmp(network->ssid, match->network->ssid,
James Ketrenos43f66a62005-03-25 12:31:53 -06005395 network->ssid_len)) {
5396 IPW_DEBUG_ASSOC("Netowrk '%s (" MAC_FMT ")' excluded "
5397 "because of non-network ESSID.\n",
Jeff Garzikbf794512005-07-31 13:07:26 -04005398 escape_essid(network->ssid,
James Ketrenos43f66a62005-03-25 12:31:53 -06005399 network->ssid_len),
5400 MAC_ARG(network->bssid));
5401 return 0;
5402 }
5403 } else {
Jeff Garzikbf794512005-07-31 13:07:26 -04005404 /* If an ESSID has been configured then compare the broadcast
5405 * ESSID to ours */
5406 if ((priv->config & CFG_STATIC_ESSID) &&
James Ketrenos43f66a62005-03-25 12:31:53 -06005407 ((network->ssid_len != priv->essid_len) ||
Jeff Garzikbf794512005-07-31 13:07:26 -04005408 memcmp(network->ssid, priv->essid,
James Ketrenos43f66a62005-03-25 12:31:53 -06005409 min(network->ssid_len, priv->essid_len)))) {
5410 char escaped[IW_ESSID_MAX_SIZE * 2 + 1];
Jeff Garzik0edd5b42005-09-07 00:48:31 -04005411 strncpy(escaped,
5412 escape_essid(network->ssid, network->ssid_len),
James Ketrenos43f66a62005-03-25 12:31:53 -06005413 sizeof(escaped));
5414 IPW_DEBUG_ASSOC("Network '%s (" MAC_FMT ")' excluded "
Jeff Garzikbf794512005-07-31 13:07:26 -04005415 "because of ESSID mismatch: '%s'.\n",
James Ketrenos43f66a62005-03-25 12:31:53 -06005416 escaped, MAC_ARG(network->bssid),
Jeff Garzik0edd5b42005-09-07 00:48:31 -04005417 escape_essid(priv->essid,
5418 priv->essid_len));
James Ketrenos43f66a62005-03-25 12:31:53 -06005419 return 0;
5420 }
5421 }
5422
5423 /* If the old network rate is better than this one, don't bother
5424 * testing everything else. */
Jeff Garzik0edd5b42005-09-07 00:48:31 -04005425 if (match->network && match->network->stats.rssi > network->stats.rssi) {
James Ketrenos43f66a62005-03-25 12:31:53 -06005426 char escaped[IW_ESSID_MAX_SIZE * 2 + 1];
Jeff Garzikbf794512005-07-31 13:07:26 -04005427 strncpy(escaped,
5428 escape_essid(network->ssid, network->ssid_len),
James Ketrenos43f66a62005-03-25 12:31:53 -06005429 sizeof(escaped));
5430 IPW_DEBUG_ASSOC("Network '%s (" MAC_FMT ")' excluded because "
5431 "'%s (" MAC_FMT ")' has a stronger signal.\n",
5432 escaped, MAC_ARG(network->bssid),
5433 escape_essid(match->network->ssid,
5434 match->network->ssid_len),
5435 MAC_ARG(match->network->bssid));
5436 return 0;
5437 }
Jeff Garzikbf794512005-07-31 13:07:26 -04005438
James Ketrenos43f66a62005-03-25 12:31:53 -06005439 /* If this network has already had an association attempt within the
5440 * last 3 seconds, do not try and associate again... */
5441 if (network->last_associate &&
James Ketrenosea2b26e2005-08-24 21:25:16 -05005442 time_after(network->last_associate + (HZ * 3UL), jiffies)) {
James Ketrenos43f66a62005-03-25 12:31:53 -06005443 IPW_DEBUG_ASSOC("Network '%s (" MAC_FMT ")' excluded "
James Ketrenosafbf30a2005-08-25 00:05:33 -05005444 "because of storming (%lus since last "
James Ketrenos43f66a62005-03-25 12:31:53 -06005445 "assoc attempt).\n",
5446 escape_essid(network->ssid, network->ssid_len),
5447 MAC_ARG(network->bssid),
5448 (jiffies - network->last_associate) / HZ);
5449 return 0;
5450 }
5451
5452 /* Now go through and see if the requested network is valid... */
Jeff Garzikbf794512005-07-31 13:07:26 -04005453 if (priv->ieee->scan_age != 0 &&
James Ketrenosea2b26e2005-08-24 21:25:16 -05005454 time_after(jiffies, network->last_scanned + priv->ieee->scan_age)) {
James Ketrenos43f66a62005-03-25 12:31:53 -06005455 IPW_DEBUG_ASSOC("Network '%s (" MAC_FMT ")' excluded "
5456 "because of age: %lums.\n",
5457 escape_essid(network->ssid, network->ssid_len),
5458 MAC_ARG(network->bssid),
James Ketrenosafbf30a2005-08-25 00:05:33 -05005459 1000 * (jiffies - network->last_scanned) / HZ);
James Ketrenos43f66a62005-03-25 12:31:53 -06005460 return 0;
Jeff Garzikbf794512005-07-31 13:07:26 -04005461 }
James Ketrenos43f66a62005-03-25 12:31:53 -06005462
Jeff Garzikbf794512005-07-31 13:07:26 -04005463 if ((priv->config & CFG_STATIC_CHANNEL) &&
James Ketrenos43f66a62005-03-25 12:31:53 -06005464 (network->channel != priv->channel)) {
5465 IPW_DEBUG_ASSOC("Network '%s (" MAC_FMT ")' excluded "
5466 "because of channel mismatch: %d != %d.\n",
5467 escape_essid(network->ssid, network->ssid_len),
5468 MAC_ARG(network->bssid),
5469 network->channel, priv->channel);
5470 return 0;
5471 }
Jeff Garzikbf794512005-07-31 13:07:26 -04005472
James Ketrenos43f66a62005-03-25 12:31:53 -06005473 /* Verify privacy compatability */
Jeff Garzikbf794512005-07-31 13:07:26 -04005474 if (((priv->capability & CAP_PRIVACY_ON) ? 1 : 0) !=
James Ketrenos43f66a62005-03-25 12:31:53 -06005475 ((network->capability & WLAN_CAPABILITY_PRIVACY) ? 1 : 0)) {
5476 IPW_DEBUG_ASSOC("Network '%s (" MAC_FMT ")' excluded "
5477 "because of privacy mismatch: %s != %s.\n",
5478 escape_essid(network->ssid, network->ssid_len),
5479 MAC_ARG(network->bssid),
Jeff Garzikbf794512005-07-31 13:07:26 -04005480 priv->capability & CAP_PRIVACY_ON ? "on" :
James Ketrenos43f66a62005-03-25 12:31:53 -06005481 "off",
Jeff Garzikbf794512005-07-31 13:07:26 -04005482 network->capability &
Jeff Garzik0edd5b42005-09-07 00:48:31 -04005483 WLAN_CAPABILITY_PRIVACY ? "on" : "off");
James Ketrenos43f66a62005-03-25 12:31:53 -06005484 return 0;
5485 }
Jeff Garzikbf794512005-07-31 13:07:26 -04005486
5487 if ((priv->config & CFG_STATIC_BSSID) &&
James Ketrenos43f66a62005-03-25 12:31:53 -06005488 memcmp(network->bssid, priv->bssid, ETH_ALEN)) {
5489 IPW_DEBUG_ASSOC("Network '%s (" MAC_FMT ")' excluded "
5490 "because of BSSID mismatch: " MAC_FMT ".\n",
5491 escape_essid(network->ssid, network->ssid_len),
Jeff Garzik0edd5b42005-09-07 00:48:31 -04005492 MAC_ARG(network->bssid), MAC_ARG(priv->bssid));
James Ketrenos43f66a62005-03-25 12:31:53 -06005493 return 0;
5494 }
Jeff Garzikbf794512005-07-31 13:07:26 -04005495
James Ketrenos43f66a62005-03-25 12:31:53 -06005496 /* Filter out any incompatible freq / mode combinations */
5497 if (!ieee80211_is_valid_mode(priv->ieee, network->mode)) {
5498 IPW_DEBUG_ASSOC("Network '%s (" MAC_FMT ")' excluded "
5499 "because of invalid frequency/mode "
5500 "combination.\n",
5501 escape_essid(network->ssid, network->ssid_len),
5502 MAC_ARG(network->bssid));
5503 return 0;
5504 }
Jeff Garzikbf794512005-07-31 13:07:26 -04005505
James Ketrenosea2b26e2005-08-24 21:25:16 -05005506 /* Ensure that the rates supported by the driver are compatible with
5507 * this AP, including verification of basic rates (mandatory) */
5508 if (!ipw_compatible_rates(priv, network, &rates)) {
5509 IPW_DEBUG_ASSOC("Network '%s (" MAC_FMT ")' excluded "
5510 "because configured rate mask excludes "
5511 "AP mandatory rate.\n",
5512 escape_essid(network->ssid, network->ssid_len),
5513 MAC_ARG(network->bssid));
5514 return 0;
5515 }
5516
James Ketrenos43f66a62005-03-25 12:31:53 -06005517 if (rates.num_rates == 0) {
5518 IPW_DEBUG_ASSOC("Network '%s (" MAC_FMT ")' excluded "
5519 "because of no compatible rates.\n",
5520 escape_essid(network->ssid, network->ssid_len),
5521 MAC_ARG(network->bssid));
5522 return 0;
5523 }
Jeff Garzikbf794512005-07-31 13:07:26 -04005524
James Ketrenos43f66a62005-03-25 12:31:53 -06005525 /* TODO: Perform any further minimal comparititive tests. We do not
5526 * want to put too much policy logic here; intelligent scan selection
5527 * should occur within a generic IEEE 802.11 user space tool. */
5528
5529 /* Set up 'new' AP to this network */
5530 ipw_copy_rates(&match->rates, &rates);
5531 match->network = network;
5532
5533 IPW_DEBUG_ASSOC("Network '%s (" MAC_FMT ")' is a viable match.\n",
5534 escape_essid(network->ssid, network->ssid_len),
5535 MAC_ARG(network->bssid));
5536
5537 return 1;
5538}
5539
Jeff Garzikbf794512005-07-31 13:07:26 -04005540static void ipw_adhoc_create(struct ipw_priv *priv,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04005541 struct ieee80211_network *network)
James Ketrenos43f66a62005-03-25 12:31:53 -06005542{
James Ketrenosafbf30a2005-08-25 00:05:33 -05005543 const struct ieee80211_geo *geo = ieee80211_get_geo(priv->ieee);
5544 int i;
5545
James Ketrenos43f66a62005-03-25 12:31:53 -06005546 /*
5547 * For the purposes of scanning, we can set our wireless mode
5548 * to trigger scans across combinations of bands, but when it
5549 * comes to creating a new ad-hoc network, we have tell the FW
5550 * exactly which band to use.
5551 *
Jeff Garzikbf794512005-07-31 13:07:26 -04005552 * We also have the possibility of an invalid channel for the
James Ketrenos43f66a62005-03-25 12:31:53 -06005553 * chossen band. Attempting to create a new ad-hoc network
5554 * with an invalid channel for wireless mode will trigger a
5555 * FW fatal error.
James Ketrenosafbf30a2005-08-25 00:05:33 -05005556 *
James Ketrenos43f66a62005-03-25 12:31:53 -06005557 */
James Ketrenosafbf30a2005-08-25 00:05:33 -05005558 switch (ieee80211_is_valid_channel(priv->ieee, priv->channel)) {
5559 case IEEE80211_52GHZ_BAND:
5560 network->mode = IEEE_A;
5561 i = ieee80211_channel_to_index(priv->ieee, priv->channel);
5562 if (i == -1)
5563 BUG();
5564 if (geo->a[i].flags & IEEE80211_CH_PASSIVE_ONLY) {
5565 IPW_WARNING("Overriding invalid channel\n");
5566 priv->channel = geo->a[0].channel;
5567 }
5568 break;
5569
5570 case IEEE80211_24GHZ_BAND:
5571 if (priv->ieee->mode & IEEE_G)
5572 network->mode = IEEE_G;
5573 else
5574 network->mode = IEEE_B;
5575 break;
5576
5577 default:
James Ketrenos43f66a62005-03-25 12:31:53 -06005578 IPW_WARNING("Overriding invalid channel\n");
5579 if (priv->ieee->mode & IEEE_A) {
5580 network->mode = IEEE_A;
James Ketrenosb095c382005-08-24 22:04:42 -05005581 priv->channel = geo->a[0].channel;
James Ketrenos43f66a62005-03-25 12:31:53 -06005582 } else if (priv->ieee->mode & IEEE_G) {
5583 network->mode = IEEE_G;
James Ketrenosb095c382005-08-24 22:04:42 -05005584 priv->channel = geo->bg[0].channel;
James Ketrenos43f66a62005-03-25 12:31:53 -06005585 } else {
5586 network->mode = IEEE_B;
James Ketrenosb095c382005-08-24 22:04:42 -05005587 priv->channel = geo->bg[0].channel;
James Ketrenos43f66a62005-03-25 12:31:53 -06005588 }
James Ketrenosafbf30a2005-08-25 00:05:33 -05005589 break;
5590 }
James Ketrenos43f66a62005-03-25 12:31:53 -06005591
5592 network->channel = priv->channel;
5593 priv->config |= CFG_ADHOC_PERSIST;
5594 ipw_create_bssid(priv, network->bssid);
5595 network->ssid_len = priv->essid_len;
5596 memcpy(network->ssid, priv->essid, priv->essid_len);
5597 memset(&network->stats, 0, sizeof(network->stats));
5598 network->capability = WLAN_CAPABILITY_IBSS;
James Ketrenosea2b26e2005-08-24 21:25:16 -05005599 if (!(priv->config & CFG_PREAMBLE_LONG))
5600 network->capability |= WLAN_CAPABILITY_SHORT_PREAMBLE;
James Ketrenos43f66a62005-03-25 12:31:53 -06005601 if (priv->capability & CAP_PRIVACY_ON)
5602 network->capability |= WLAN_CAPABILITY_PRIVACY;
5603 network->rates_len = min(priv->rates.num_rates, MAX_RATES_LENGTH);
Jeff Garzik0edd5b42005-09-07 00:48:31 -04005604 memcpy(network->rates, priv->rates.supported_rates, network->rates_len);
James Ketrenos43f66a62005-03-25 12:31:53 -06005605 network->rates_ex_len = priv->rates.num_rates - network->rates_len;
Jeff Garzikbf794512005-07-31 13:07:26 -04005606 memcpy(network->rates_ex,
James Ketrenos43f66a62005-03-25 12:31:53 -06005607 &priv->rates.supported_rates[network->rates_len],
5608 network->rates_ex_len);
5609 network->last_scanned = 0;
5610 network->flags = 0;
5611 network->last_associate = 0;
5612 network->time_stamp[0] = 0;
5613 network->time_stamp[1] = 0;
Jeff Garzik0edd5b42005-09-07 00:48:31 -04005614 network->beacon_interval = 100; /* Default */
5615 network->listen_interval = 10; /* Default */
5616 network->atim_window = 0; /* Default */
James Ketrenos43f66a62005-03-25 12:31:53 -06005617 network->wpa_ie_len = 0;
5618 network->rsn_ie_len = 0;
James Ketrenos43f66a62005-03-25 12:31:53 -06005619}
5620
James Ketrenosb095c382005-08-24 22:04:42 -05005621static void ipw_send_tgi_tx_key(struct ipw_priv *priv, int type, int index)
5622{
5623 struct ipw_tgi_tx_key *key;
5624 struct host_cmd cmd = {
5625 .cmd = IPW_CMD_TGI_TX_KEY,
5626 .len = sizeof(*key)
5627 };
5628
5629 if (!(priv->ieee->sec.flags & (1 << index)))
5630 return;
5631
5632 key = (struct ipw_tgi_tx_key *)&cmd.param;
5633 key->key_id = index;
5634 memcpy(key->key, priv->ieee->sec.keys[index], SCM_TEMPORAL_KEY_LENGTH);
5635 key->security_type = type;
5636 key->station_index = 0; /* always 0 for BSS */
5637 key->flags = 0;
5638 /* 0 for new key; previous value of counter (after fatal error) */
5639 key->tx_counter[0] = 0;
5640 key->tx_counter[1] = 0;
5641
James Ketrenos9ddf84f2005-08-16 17:07:11 -05005642 ipw_send_cmd(priv, &cmd);
James Ketrenosb095c382005-08-24 22:04:42 -05005643}
5644
5645static void ipw_send_wep_keys(struct ipw_priv *priv, int type)
James Ketrenos43f66a62005-03-25 12:31:53 -06005646{
5647 struct ipw_wep_key *key;
5648 int i;
5649 struct host_cmd cmd = {
5650 .cmd = IPW_CMD_WEP_KEY,
5651 .len = sizeof(*key)
5652 };
5653
5654 key = (struct ipw_wep_key *)&cmd.param;
5655 key->cmd_id = DINO_CMD_WEP_KEY;
5656 key->seq_num = 0;
5657
James Ketrenosb095c382005-08-24 22:04:42 -05005658 /* Note: AES keys cannot be set for multiple times.
5659 * Only set it at the first time. */
Jeff Garzikbf794512005-07-31 13:07:26 -04005660 for (i = 0; i < 4; i++) {
James Ketrenosb095c382005-08-24 22:04:42 -05005661 key->key_index = i | type;
5662 if (!(priv->ieee->sec.flags & (1 << i))) {
James Ketrenos43f66a62005-03-25 12:31:53 -06005663 key->key_size = 0;
James Ketrenosb095c382005-08-24 22:04:42 -05005664 continue;
James Ketrenos43f66a62005-03-25 12:31:53 -06005665 }
5666
James Ketrenosb095c382005-08-24 22:04:42 -05005667 key->key_size = priv->ieee->sec.key_sizes[i];
5668 memcpy(key->key, priv->ieee->sec.keys[i], key->key_size);
5669
James Ketrenos9ddf84f2005-08-16 17:07:11 -05005670 ipw_send_cmd(priv, &cmd);
Jeff Garzikbf794512005-07-31 13:07:26 -04005671 }
James Ketrenos43f66a62005-03-25 12:31:53 -06005672}
5673
Zhu Yi1fbfea52005-08-05 17:22:56 +08005674static void ipw_set_hw_decrypt_unicast(struct ipw_priv *priv, int level)
5675{
5676 if (priv->ieee->host_encrypt)
5677 return;
5678
5679 switch (level) {
5680 case SEC_LEVEL_3:
5681 priv->sys_config.disable_unicast_decryption = 0;
5682 priv->ieee->host_decrypt = 0;
5683 break;
5684 case SEC_LEVEL_2:
5685 priv->sys_config.disable_unicast_decryption = 1;
5686 priv->ieee->host_decrypt = 1;
5687 break;
5688 case SEC_LEVEL_1:
5689 priv->sys_config.disable_unicast_decryption = 0;
5690 priv->ieee->host_decrypt = 0;
5691 break;
5692 case SEC_LEVEL_0:
5693 priv->sys_config.disable_unicast_decryption = 1;
5694 break;
5695 default:
5696 break;
5697 }
5698}
5699
5700static void ipw_set_hw_decrypt_multicast(struct ipw_priv *priv, int level)
5701{
5702 if (priv->ieee->host_encrypt)
5703 return;
5704
5705 switch (level) {
5706 case SEC_LEVEL_3:
5707 priv->sys_config.disable_multicast_decryption = 0;
5708 break;
5709 case SEC_LEVEL_2:
5710 priv->sys_config.disable_multicast_decryption = 1;
5711 break;
5712 case SEC_LEVEL_1:
5713 priv->sys_config.disable_multicast_decryption = 0;
5714 break;
5715 case SEC_LEVEL_0:
5716 priv->sys_config.disable_multicast_decryption = 1;
5717 break;
5718 default:
5719 break;
5720 }
5721}
5722
James Ketrenosb095c382005-08-24 22:04:42 -05005723static void ipw_set_hwcrypto_keys(struct ipw_priv *priv)
5724{
5725 switch (priv->ieee->sec.level) {
5726 case SEC_LEVEL_3:
Zhu Yid8bad6d2005-07-13 12:25:38 -05005727 if (priv->ieee->sec.flags & SEC_ACTIVE_KEY)
5728 ipw_send_tgi_tx_key(priv,
5729 DCT_FLAG_EXT_SECURITY_CCM,
5730 priv->ieee->sec.active_key);
James Ketrenosafbf30a2005-08-25 00:05:33 -05005731
James Ketrenosb095c382005-08-24 22:04:42 -05005732 ipw_send_wep_keys(priv, DCW_WEP_KEY_SEC_TYPE_CCM);
James Ketrenosb095c382005-08-24 22:04:42 -05005733 break;
5734 case SEC_LEVEL_2:
Zhu Yid8bad6d2005-07-13 12:25:38 -05005735 if (priv->ieee->sec.flags & SEC_ACTIVE_KEY)
5736 ipw_send_tgi_tx_key(priv,
5737 DCT_FLAG_EXT_SECURITY_TKIP,
5738 priv->ieee->sec.active_key);
James Ketrenosb095c382005-08-24 22:04:42 -05005739 break;
5740 case SEC_LEVEL_1:
5741 ipw_send_wep_keys(priv, DCW_WEP_KEY_SEC_TYPE_WEP);
James Ketrenosb095c382005-08-24 22:04:42 -05005742 break;
5743 case SEC_LEVEL_0:
5744 default:
5745 break;
5746 }
Zhu Yi1fbfea52005-08-05 17:22:56 +08005747
5748 ipw_set_hw_decrypt_unicast(priv, priv->ieee->sec.level);
5749 ipw_set_hw_decrypt_multicast(priv, priv->ieee->sec.level);
James Ketrenosb095c382005-08-24 22:04:42 -05005750}
5751
James Ketrenos43f66a62005-03-25 12:31:53 -06005752static void ipw_adhoc_check(void *data)
5753{
5754 struct ipw_priv *priv = data;
Jeff Garzikbf794512005-07-31 13:07:26 -04005755
James Ketrenosafbf30a2005-08-25 00:05:33 -05005756 if (priv->missed_adhoc_beacons++ > priv->disassociate_threshold &&
James Ketrenos43f66a62005-03-25 12:31:53 -06005757 !(priv->config & CFG_ADHOC_PERSIST)) {
James Ketrenosafbf30a2005-08-25 00:05:33 -05005758 IPW_DEBUG(IPW_DL_INFO | IPW_DL_NOTIF |
5759 IPW_DL_STATE | IPW_DL_ASSOC,
5760 "Missed beacon: %d - disassociate\n",
5761 priv->missed_adhoc_beacons);
James Ketrenos43f66a62005-03-25 12:31:53 -06005762 ipw_remove_current_network(priv);
5763 ipw_disassociate(priv);
5764 return;
5765 }
5766
Jeff Garzikbf794512005-07-31 13:07:26 -04005767 queue_delayed_work(priv->workqueue, &priv->adhoc_check,
James Ketrenos43f66a62005-03-25 12:31:53 -06005768 priv->assoc_request.beacon_interval);
5769}
5770
James Ketrenosc848d0a2005-08-24 21:56:24 -05005771static void ipw_bg_adhoc_check(void *data)
5772{
5773 struct ipw_priv *priv = data;
5774 down(&priv->sem);
5775 ipw_adhoc_check(data);
5776 up(&priv->sem);
5777}
5778
James Ketrenos43f66a62005-03-25 12:31:53 -06005779#ifdef CONFIG_IPW_DEBUG
5780static void ipw_debug_config(struct ipw_priv *priv)
5781{
5782 IPW_DEBUG_INFO("Scan completed, no valid APs matched "
5783 "[CFG 0x%08X]\n", priv->config);
5784 if (priv->config & CFG_STATIC_CHANNEL)
Jeff Garzik0edd5b42005-09-07 00:48:31 -04005785 IPW_DEBUG_INFO("Channel locked to %d\n", priv->channel);
James Ketrenos43f66a62005-03-25 12:31:53 -06005786 else
5787 IPW_DEBUG_INFO("Channel unlocked.\n");
5788 if (priv->config & CFG_STATIC_ESSID)
Jeff Garzikbf794512005-07-31 13:07:26 -04005789 IPW_DEBUG_INFO("ESSID locked to '%s'\n",
Jeff Garzik0edd5b42005-09-07 00:48:31 -04005790 escape_essid(priv->essid, priv->essid_len));
James Ketrenos43f66a62005-03-25 12:31:53 -06005791 else
5792 IPW_DEBUG_INFO("ESSID unlocked.\n");
5793 if (priv->config & CFG_STATIC_BSSID)
James Ketrenosea2b26e2005-08-24 21:25:16 -05005794 IPW_DEBUG_INFO("BSSID locked to " MAC_FMT "\n",
5795 MAC_ARG(priv->bssid));
James Ketrenos43f66a62005-03-25 12:31:53 -06005796 else
5797 IPW_DEBUG_INFO("BSSID unlocked.\n");
5798 if (priv->capability & CAP_PRIVACY_ON)
5799 IPW_DEBUG_INFO("PRIVACY on\n");
5800 else
5801 IPW_DEBUG_INFO("PRIVACY off\n");
5802 IPW_DEBUG_INFO("RATE MASK: 0x%08X\n", priv->rates_mask);
5803}
5804#else
Jiri Benc8d45ff72005-08-25 20:09:39 -04005805#define ipw_debug_config(x) do {} while (0)
James Ketrenos43f66a62005-03-25 12:31:53 -06005806#endif
5807
James Ketrenosb095c382005-08-24 22:04:42 -05005808static inline void ipw_set_fixed_rate(struct ipw_priv *priv, int mode)
James Ketrenos43f66a62005-03-25 12:31:53 -06005809{
5810 /* TODO: Verify that this works... */
5811 struct ipw_fixed_rate fr = {
5812 .tx_rates = priv->rates_mask
5813 };
5814 u32 reg;
5815 u16 mask = 0;
5816
Jeff Garzikbf794512005-07-31 13:07:26 -04005817 /* Identify 'current FW band' and match it with the fixed
James Ketrenos43f66a62005-03-25 12:31:53 -06005818 * Tx rates */
Jeff Garzikbf794512005-07-31 13:07:26 -04005819
James Ketrenos43f66a62005-03-25 12:31:53 -06005820 switch (priv->ieee->freq_band) {
Jeff Garzik0edd5b42005-09-07 00:48:31 -04005821 case IEEE80211_52GHZ_BAND: /* A only */
James Ketrenos43f66a62005-03-25 12:31:53 -06005822 /* IEEE_A */
5823 if (priv->rates_mask & ~IEEE80211_OFDM_RATES_MASK) {
5824 /* Invalid fixed rate mask */
James Ketrenosea2b26e2005-08-24 21:25:16 -05005825 IPW_DEBUG_WX
5826 ("invalid fixed rate mask in ipw_set_fixed_rate\n");
James Ketrenos43f66a62005-03-25 12:31:53 -06005827 fr.tx_rates = 0;
5828 break;
5829 }
Jeff Garzikbf794512005-07-31 13:07:26 -04005830
James Ketrenos43f66a62005-03-25 12:31:53 -06005831 fr.tx_rates >>= IEEE80211_OFDM_SHIFT_MASK_A;
5832 break;
5833
Jeff Garzik0edd5b42005-09-07 00:48:31 -04005834 default: /* 2.4Ghz or Mixed */
James Ketrenos43f66a62005-03-25 12:31:53 -06005835 /* IEEE_B */
James Ketrenosb095c382005-08-24 22:04:42 -05005836 if (mode == IEEE_B) {
James Ketrenos43f66a62005-03-25 12:31:53 -06005837 if (fr.tx_rates & ~IEEE80211_CCK_RATES_MASK) {
5838 /* Invalid fixed rate mask */
James Ketrenosea2b26e2005-08-24 21:25:16 -05005839 IPW_DEBUG_WX
5840 ("invalid fixed rate mask in ipw_set_fixed_rate\n");
James Ketrenos43f66a62005-03-25 12:31:53 -06005841 fr.tx_rates = 0;
5842 }
5843 break;
Jeff Garzikbf794512005-07-31 13:07:26 -04005844 }
James Ketrenos43f66a62005-03-25 12:31:53 -06005845
5846 /* IEEE_G */
5847 if (fr.tx_rates & ~(IEEE80211_CCK_RATES_MASK |
5848 IEEE80211_OFDM_RATES_MASK)) {
5849 /* Invalid fixed rate mask */
James Ketrenosea2b26e2005-08-24 21:25:16 -05005850 IPW_DEBUG_WX
5851 ("invalid fixed rate mask in ipw_set_fixed_rate\n");
James Ketrenos43f66a62005-03-25 12:31:53 -06005852 fr.tx_rates = 0;
5853 break;
5854 }
5855
5856 if (IEEE80211_OFDM_RATE_6MB_MASK & fr.tx_rates) {
5857 mask |= (IEEE80211_OFDM_RATE_6MB_MASK >> 1);
5858 fr.tx_rates &= ~IEEE80211_OFDM_RATE_6MB_MASK;
5859 }
Jeff Garzikbf794512005-07-31 13:07:26 -04005860
James Ketrenos43f66a62005-03-25 12:31:53 -06005861 if (IEEE80211_OFDM_RATE_9MB_MASK & fr.tx_rates) {
5862 mask |= (IEEE80211_OFDM_RATE_9MB_MASK >> 1);
5863 fr.tx_rates &= ~IEEE80211_OFDM_RATE_9MB_MASK;
5864 }
Jeff Garzikbf794512005-07-31 13:07:26 -04005865
James Ketrenos43f66a62005-03-25 12:31:53 -06005866 if (IEEE80211_OFDM_RATE_12MB_MASK & fr.tx_rates) {
5867 mask |= (IEEE80211_OFDM_RATE_12MB_MASK >> 1);
5868 fr.tx_rates &= ~IEEE80211_OFDM_RATE_12MB_MASK;
5869 }
Jeff Garzikbf794512005-07-31 13:07:26 -04005870
James Ketrenos43f66a62005-03-25 12:31:53 -06005871 fr.tx_rates |= mask;
5872 break;
5873 }
5874
5875 reg = ipw_read32(priv, IPW_MEM_FIXED_OVERRIDE);
Jeff Garzik0edd5b42005-09-07 00:48:31 -04005876 ipw_write_reg32(priv, reg, *(u32 *) & fr);
James Ketrenos43f66a62005-03-25 12:31:53 -06005877}
5878
James Ketrenosea2b26e2005-08-24 21:25:16 -05005879static void ipw_abort_scan(struct ipw_priv *priv)
5880{
5881 int err;
5882
5883 if (priv->status & STATUS_SCAN_ABORTING) {
5884 IPW_DEBUG_HC("Ignoring concurrent scan abort request.\n");
5885 return;
5886 }
5887 priv->status |= STATUS_SCAN_ABORTING;
5888
5889 err = ipw_send_scan_abort(priv);
5890 if (err)
5891 IPW_DEBUG_HC("Request to abort scan failed.\n");
5892}
5893
James Ketrenosafbf30a2005-08-25 00:05:33 -05005894static void ipw_add_scan_channels(struct ipw_priv *priv,
5895 struct ipw_scan_request_ext *scan,
5896 int scan_type)
5897{
5898 int channel_index = 0;
5899 const struct ieee80211_geo *geo;
5900 int i;
5901
5902 geo = ieee80211_get_geo(priv->ieee);
5903
5904 if (priv->ieee->freq_band & IEEE80211_52GHZ_BAND) {
5905 int start = channel_index;
5906 for (i = 0; i < geo->a_channels; i++) {
5907 if ((priv->status & STATUS_ASSOCIATED) &&
5908 geo->a[i].channel == priv->channel)
5909 continue;
5910 channel_index++;
5911 scan->channels_list[channel_index] = geo->a[i].channel;
5912 ipw_set_scan_type(scan, channel_index, scan_type);
5913 }
5914
5915 if (start != channel_index) {
5916 scan->channels_list[start] = (u8) (IPW_A_MODE << 6) |
5917 (channel_index - start);
5918 channel_index++;
5919 }
5920 }
5921
5922 if (priv->ieee->freq_band & IEEE80211_24GHZ_BAND) {
5923 int start = channel_index;
5924 if (priv->config & CFG_SPEED_SCAN) {
5925 u8 channels[IEEE80211_24GHZ_CHANNELS] = {
5926 /* nop out the list */
5927 [0] = 0
5928 };
5929
5930 u8 channel;
5931 while (channel_index < IPW_SCAN_CHANNELS) {
5932 channel =
5933 priv->speed_scan[priv->speed_scan_pos];
5934 if (channel == 0) {
5935 priv->speed_scan_pos = 0;
5936 channel = priv->speed_scan[0];
5937 }
5938 if ((priv->status & STATUS_ASSOCIATED) &&
5939 channel == priv->channel) {
5940 priv->speed_scan_pos++;
5941 continue;
5942 }
5943
5944 /* If this channel has already been
5945 * added in scan, break from loop
5946 * and this will be the first channel
5947 * in the next scan.
5948 */
5949 if (channels[channel - 1] != 0)
5950 break;
5951
5952 channels[channel - 1] = 1;
5953 priv->speed_scan_pos++;
5954 channel_index++;
5955 scan->channels_list[channel_index] = channel;
5956 ipw_set_scan_type(scan, channel_index,
5957 scan_type);
5958 }
5959 } else {
5960 for (i = 0; i < geo->bg_channels; i++) {
5961 if ((priv->status & STATUS_ASSOCIATED) &&
5962 geo->bg[i].channel == priv->channel)
5963 continue;
5964 channel_index++;
5965 scan->channels_list[channel_index] =
5966 geo->bg[i].channel;
5967 ipw_set_scan_type(scan, channel_index,
5968 scan_type);
5969 }
5970 }
5971
5972 if (start != channel_index) {
5973 scan->channels_list[start] = (u8) (IPW_B_MODE << 6) |
5974 (channel_index - start);
5975 }
5976 }
5977}
5978
James Ketrenosea2b26e2005-08-24 21:25:16 -05005979static int ipw_request_scan(struct ipw_priv *priv)
5980{
5981 struct ipw_scan_request_ext scan;
James Ketrenosafbf30a2005-08-25 00:05:33 -05005982 int err = 0, scan_type;
5983
5984 if (!(priv->status & STATUS_INIT) ||
5985 (priv->status & STATUS_EXIT_PENDING))
5986 return 0;
James Ketrenosea2b26e2005-08-24 21:25:16 -05005987
James Ketrenosb095c382005-08-24 22:04:42 -05005988 down(&priv->sem);
5989
James Ketrenosea2b26e2005-08-24 21:25:16 -05005990 if (priv->status & STATUS_SCANNING) {
James Ketrenosa613bff2005-08-24 21:43:11 -05005991 IPW_DEBUG_HC("Concurrent scan requested. Ignoring.\n");
James Ketrenosea2b26e2005-08-24 21:25:16 -05005992 priv->status |= STATUS_SCAN_PENDING;
James Ketrenosb095c382005-08-24 22:04:42 -05005993 goto done;
James Ketrenosea2b26e2005-08-24 21:25:16 -05005994 }
5995
James Ketrenosafbf30a2005-08-25 00:05:33 -05005996 if (!(priv->status & STATUS_SCAN_FORCED) &&
5997 priv->status & STATUS_SCAN_ABORTING) {
James Ketrenosea2b26e2005-08-24 21:25:16 -05005998 IPW_DEBUG_HC("Scan request while abort pending. Queuing.\n");
5999 priv->status |= STATUS_SCAN_PENDING;
James Ketrenosb095c382005-08-24 22:04:42 -05006000 goto done;
James Ketrenosea2b26e2005-08-24 21:25:16 -05006001 }
6002
6003 if (priv->status & STATUS_RF_KILL_MASK) {
6004 IPW_DEBUG_HC("Aborting scan due to RF Kill activation\n");
6005 priv->status |= STATUS_SCAN_PENDING;
James Ketrenosb095c382005-08-24 22:04:42 -05006006 goto done;
James Ketrenosea2b26e2005-08-24 21:25:16 -05006007 }
6008
6009 memset(&scan, 0, sizeof(scan));
6010
James Ketrenosb095c382005-08-24 22:04:42 -05006011 if (priv->config & CFG_SPEED_SCAN)
6012 scan.dwell_time[IPW_SCAN_ACTIVE_BROADCAST_SCAN] =
6013 cpu_to_le16(30);
6014 else
6015 scan.dwell_time[IPW_SCAN_ACTIVE_BROADCAST_SCAN] =
6016 cpu_to_le16(20);
6017
James Ketrenosa613bff2005-08-24 21:43:11 -05006018 scan.dwell_time[IPW_SCAN_ACTIVE_BROADCAST_AND_DIRECT_SCAN] =
6019 cpu_to_le16(20);
6020 scan.dwell_time[IPW_SCAN_PASSIVE_FULL_DWELL_SCAN] = cpu_to_le16(20);
James Ketrenosea2b26e2005-08-24 21:25:16 -05006021
James Ketrenosa613bff2005-08-24 21:43:11 -05006022 scan.full_scan_index = cpu_to_le32(ieee80211_get_scans(priv->ieee));
James Ketrenosea2b26e2005-08-24 21:25:16 -05006023
James Ketrenosb095c382005-08-24 22:04:42 -05006024#ifdef CONFIG_IPW2200_MONITOR
James Ketrenosea2b26e2005-08-24 21:25:16 -05006025 if (priv->ieee->iw_mode == IW_MODE_MONITOR) {
James Ketrenosafbf30a2005-08-25 00:05:33 -05006026 u8 channel;
James Ketrenosb095c382005-08-24 22:04:42 -05006027 u8 band = 0;
James Ketrenosea2b26e2005-08-24 21:25:16 -05006028
James Ketrenosb095c382005-08-24 22:04:42 -05006029 switch (ieee80211_is_valid_channel(priv->ieee, priv->channel)) {
6030 case IEEE80211_52GHZ_BAND:
James Ketrenosea2b26e2005-08-24 21:25:16 -05006031 band = (u8) (IPW_A_MODE << 6) | 1;
James Ketrenosb095c382005-08-24 22:04:42 -05006032 channel = priv->channel;
6033 break;
James Ketrenosea2b26e2005-08-24 21:25:16 -05006034
James Ketrenosb095c382005-08-24 22:04:42 -05006035 case IEEE80211_24GHZ_BAND:
James Ketrenosea2b26e2005-08-24 21:25:16 -05006036 band = (u8) (IPW_B_MODE << 6) | 1;
James Ketrenosb095c382005-08-24 22:04:42 -05006037 channel = priv->channel;
6038 break;
James Ketrenosea2b26e2005-08-24 21:25:16 -05006039
James Ketrenosb095c382005-08-24 22:04:42 -05006040 default:
James Ketrenosea2b26e2005-08-24 21:25:16 -05006041 band = (u8) (IPW_B_MODE << 6) | 1;
6042 channel = 9;
James Ketrenosb095c382005-08-24 22:04:42 -05006043 break;
James Ketrenosea2b26e2005-08-24 21:25:16 -05006044 }
6045
James Ketrenosb095c382005-08-24 22:04:42 -05006046 scan.channels_list[0] = band;
6047 scan.channels_list[1] = channel;
6048 ipw_set_scan_type(&scan, 1, IPW_SCAN_PASSIVE_FULL_DWELL_SCAN);
James Ketrenosea2b26e2005-08-24 21:25:16 -05006049
James Ketrenosb095c382005-08-24 22:04:42 -05006050 /* NOTE: The card will sit on this channel for this time
6051 * period. Scan aborts are timing sensitive and frequently
6052 * result in firmware restarts. As such, it is best to
6053 * set a small dwell_time here and just keep re-issuing
6054 * scans. Otherwise fast channel hopping will not actually
6055 * hop channels.
6056 *
6057 * TODO: Move SPEED SCAN support to all modes and bands */
James Ketrenosa613bff2005-08-24 21:43:11 -05006058 scan.dwell_time[IPW_SCAN_PASSIVE_FULL_DWELL_SCAN] =
6059 cpu_to_le16(2000);
James Ketrenosea2b26e2005-08-24 21:25:16 -05006060 } else {
James Ketrenosb095c382005-08-24 22:04:42 -05006061#endif /* CONFIG_IPW2200_MONITOR */
6062 /* If we are roaming, then make this a directed scan for the
6063 * current network. Otherwise, ensure that every other scan
6064 * is a fast channel hop scan */
6065 if ((priv->status & STATUS_ROAMING)
6066 || (!(priv->status & STATUS_ASSOCIATED)
6067 && (priv->config & CFG_STATIC_ESSID)
6068 && (le32_to_cpu(scan.full_scan_index) % 2))) {
James Ketrenosea2b26e2005-08-24 21:25:16 -05006069 err = ipw_send_ssid(priv, priv->essid, priv->essid_len);
6070 if (err) {
James Ketrenosb095c382005-08-24 22:04:42 -05006071 IPW_DEBUG_HC("Attempt to send SSID command "
6072 "failed.\n");
6073 goto done;
James Ketrenosea2b26e2005-08-24 21:25:16 -05006074 }
6075
6076 scan_type = IPW_SCAN_ACTIVE_BROADCAST_AND_DIRECT_SCAN;
James Ketrenosafbf30a2005-08-25 00:05:33 -05006077 } else
James Ketrenosea2b26e2005-08-24 21:25:16 -05006078 scan_type = IPW_SCAN_ACTIVE_BROADCAST_SCAN;
James Ketrenosea2b26e2005-08-24 21:25:16 -05006079
James Ketrenosafbf30a2005-08-25 00:05:33 -05006080 ipw_add_scan_channels(priv, &scan, scan_type);
James Ketrenosb095c382005-08-24 22:04:42 -05006081#ifdef CONFIG_IPW2200_MONITOR
James Ketrenosea2b26e2005-08-24 21:25:16 -05006082 }
6083#endif
6084
6085 err = ipw_send_scan_request_ext(priv, &scan);
6086 if (err) {
6087 IPW_DEBUG_HC("Sending scan command failed: %08X\n", err);
James Ketrenosb095c382005-08-24 22:04:42 -05006088 goto done;
James Ketrenosea2b26e2005-08-24 21:25:16 -05006089 }
6090
6091 priv->status |= STATUS_SCANNING;
6092 priv->status &= ~STATUS_SCAN_PENDING;
James Ketrenosafbf30a2005-08-25 00:05:33 -05006093 queue_delayed_work(priv->workqueue, &priv->scan_check,
6094 IPW_SCAN_CHECK_WATCHDOG);
James Ketrenosb095c382005-08-24 22:04:42 -05006095 done:
James Ketrenosc848d0a2005-08-24 21:56:24 -05006096 up(&priv->sem);
James Ketrenosb095c382005-08-24 22:04:42 -05006097 return err;
James Ketrenosc848d0a2005-08-24 21:56:24 -05006098}
6099
6100static void ipw_bg_abort_scan(void *data)
6101{
6102 struct ipw_priv *priv = data;
6103 down(&priv->sem);
6104 ipw_abort_scan(data);
6105 up(&priv->sem);
6106}
6107
James Ketrenosafbf30a2005-08-25 00:05:33 -05006108#if WIRELESS_EXT < 18
6109/* Support for wpa_supplicant before WE-18, deprecated. */
James Ketrenosea2b26e2005-08-24 21:25:16 -05006110
6111/* following definitions must match definitions in driver_ipw.c */
6112
6113#define IPW_IOCTL_WPA_SUPPLICANT SIOCIWFIRSTPRIV+30
6114
6115#define IPW_CMD_SET_WPA_PARAM 1
6116#define IPW_CMD_SET_WPA_IE 2
6117#define IPW_CMD_SET_ENCRYPTION 3
6118#define IPW_CMD_MLME 4
6119
6120#define IPW_PARAM_WPA_ENABLED 1
6121#define IPW_PARAM_TKIP_COUNTERMEASURES 2
6122#define IPW_PARAM_DROP_UNENCRYPTED 3
6123#define IPW_PARAM_PRIVACY_INVOKED 4
6124#define IPW_PARAM_AUTH_ALGS 5
6125#define IPW_PARAM_IEEE_802_1X 6
6126
6127#define IPW_MLME_STA_DEAUTH 1
6128#define IPW_MLME_STA_DISASSOC 2
6129
6130#define IPW_CRYPT_ERR_UNKNOWN_ALG 2
6131#define IPW_CRYPT_ERR_UNKNOWN_ADDR 3
6132#define IPW_CRYPT_ERR_CRYPT_INIT_FAILED 4
6133#define IPW_CRYPT_ERR_KEY_SET_FAILED 5
6134#define IPW_CRYPT_ERR_TX_KEY_SET_FAILED 6
6135#define IPW_CRYPT_ERR_CARD_CONF_FAILED 7
6136
6137#define IPW_CRYPT_ALG_NAME_LEN 16
6138
6139struct ipw_param {
6140 u32 cmd;
6141 u8 sta_addr[ETH_ALEN];
6142 union {
6143 struct {
6144 u8 name;
6145 u32 value;
6146 } wpa_param;
6147 struct {
6148 u32 len;
James Ketrenosb095c382005-08-24 22:04:42 -05006149 u8 reserved[32];
6150 u8 data[0];
James Ketrenosea2b26e2005-08-24 21:25:16 -05006151 } wpa_ie;
6152 struct {
James Ketrenosafbf30a2005-08-25 00:05:33 -05006153 u32 command;
6154 u32 reason_code;
James Ketrenosea2b26e2005-08-24 21:25:16 -05006155 } mlme;
6156 struct {
6157 u8 alg[IPW_CRYPT_ALG_NAME_LEN];
6158 u8 set_tx;
6159 u32 err;
6160 u8 idx;
6161 u8 seq[8]; /* sequence counter (set: RX, get: TX) */
6162 u16 key_len;
6163 u8 key[0];
6164 } crypt;
6165
6166 } u;
6167};
6168
6169/* end of driver_ipw.c code */
James Ketrenosafbf30a2005-08-25 00:05:33 -05006170#endif
James Ketrenosea2b26e2005-08-24 21:25:16 -05006171
6172static int ipw_wpa_enable(struct ipw_priv *priv, int value)
6173{
James Ketrenosb095c382005-08-24 22:04:42 -05006174 /* This is called when wpa_supplicant loads and closes the driver
6175 * interface. */
6176 return 0;
James Ketrenosea2b26e2005-08-24 21:25:16 -05006177}
6178
James Ketrenosafbf30a2005-08-25 00:05:33 -05006179#if WIRELESS_EXT < 18
6180#define IW_AUTH_ALG_OPEN_SYSTEM 0x1
6181#define IW_AUTH_ALG_SHARED_KEY 0x2
6182#endif
James Ketrenosea2b26e2005-08-24 21:25:16 -05006183
6184static int ipw_wpa_set_auth_algs(struct ipw_priv *priv, int value)
6185{
6186 struct ieee80211_device *ieee = priv->ieee;
6187 struct ieee80211_security sec = {
6188 .flags = SEC_AUTH_MODE,
6189 };
6190 int ret = 0;
6191
James Ketrenosafbf30a2005-08-25 00:05:33 -05006192 if (value & IW_AUTH_ALG_SHARED_KEY) {
James Ketrenosea2b26e2005-08-24 21:25:16 -05006193 sec.auth_mode = WLAN_AUTH_SHARED_KEY;
6194 ieee->open_wep = 0;
James Ketrenosafbf30a2005-08-25 00:05:33 -05006195 } else if (value & IW_AUTH_ALG_OPEN_SYSTEM) {
James Ketrenosea2b26e2005-08-24 21:25:16 -05006196 sec.auth_mode = WLAN_AUTH_OPEN;
6197 ieee->open_wep = 1;
James Ketrenosafbf30a2005-08-25 00:05:33 -05006198 } else
6199 return -EINVAL;
James Ketrenosea2b26e2005-08-24 21:25:16 -05006200
6201 if (ieee->set_security)
6202 ieee->set_security(ieee->dev, &sec);
6203 else
6204 ret = -EOPNOTSUPP;
6205
6206 return ret;
6207}
6208
James Ketrenosafbf30a2005-08-25 00:05:33 -05006209void ipw_wpa_assoc_frame(struct ipw_priv *priv, char *wpa_ie, int wpa_ie_len)
6210{
6211 /* make sure WPA is enabled */
6212 ipw_wpa_enable(priv, 1);
6213
6214 ipw_disassociate(priv);
6215}
6216
6217static int ipw_set_rsn_capa(struct ipw_priv *priv,
6218 char *capabilities, int length)
6219{
6220 struct host_cmd cmd = {
6221 .cmd = IPW_CMD_RSN_CAPABILITIES,
6222 .len = length,
6223 };
6224
6225 IPW_DEBUG_HC("HOST_CMD_RSN_CAPABILITIES\n");
6226
6227 memcpy(cmd.param, capabilities, length);
James Ketrenos9ddf84f2005-08-16 17:07:11 -05006228 return ipw_send_cmd(priv, &cmd);
James Ketrenosafbf30a2005-08-25 00:05:33 -05006229}
6230
6231#if WIRELESS_EXT < 18
James Ketrenosea2b26e2005-08-24 21:25:16 -05006232static int ipw_wpa_set_param(struct net_device *dev, u8 name, u32 value)
6233{
6234 struct ipw_priv *priv = ieee80211_priv(dev);
James Ketrenosa613bff2005-08-24 21:43:11 -05006235 struct ieee80211_crypt_data *crypt;
6236 unsigned long flags;
James Ketrenosea2b26e2005-08-24 21:25:16 -05006237 int ret = 0;
6238
6239 switch (name) {
6240 case IPW_PARAM_WPA_ENABLED:
6241 ret = ipw_wpa_enable(priv, value);
6242 break;
6243
6244 case IPW_PARAM_TKIP_COUNTERMEASURES:
James Ketrenosa613bff2005-08-24 21:43:11 -05006245 crypt = priv->ieee->crypt[priv->ieee->tx_keyidx];
6246 if (!crypt || !crypt->ops->set_flags || !crypt->ops->get_flags) {
6247 IPW_WARNING("Can't set TKIP countermeasures: "
6248 "crypt not set!\n");
6249 break;
6250 }
6251
6252 flags = crypt->ops->get_flags(crypt->priv);
6253
6254 if (value)
6255 flags |= IEEE80211_CRYPTO_TKIP_COUNTERMEASURES;
6256 else
6257 flags &= ~IEEE80211_CRYPTO_TKIP_COUNTERMEASURES;
6258
6259 crypt->ops->set_flags(flags, crypt->priv);
6260
James Ketrenosea2b26e2005-08-24 21:25:16 -05006261 break;
6262
James Ketrenosb095c382005-08-24 22:04:42 -05006263 case IPW_PARAM_DROP_UNENCRYPTED:{
6264 /* HACK:
6265 *
6266 * wpa_supplicant calls set_wpa_enabled when the driver
6267 * is loaded and unloaded, regardless of if WPA is being
6268 * used. No other calls are made which can be used to
6269 * determine if encryption will be used or not prior to
6270 * association being expected. If encryption is not being
6271 * used, drop_unencrypted is set to false, else true -- we
6272 * can use this to determine if the CAP_PRIVACY_ON bit should
6273 * be set.
6274 */
6275 struct ieee80211_security sec = {
6276 .flags = SEC_ENABLED,
6277 .enabled = value,
6278 };
6279 priv->ieee->drop_unencrypted = value;
6280 /* We only change SEC_LEVEL for open mode. Others
6281 * are set by ipw_wpa_set_encryption.
6282 */
6283 if (!value) {
6284 sec.flags |= SEC_LEVEL;
6285 sec.level = SEC_LEVEL_0;
6286 } else {
6287 sec.flags |= SEC_LEVEL;
6288 sec.level = SEC_LEVEL_1;
6289 }
6290 if (priv->ieee->set_security)
6291 priv->ieee->set_security(priv->ieee->dev, &sec);
6292 break;
6293 }
James Ketrenosea2b26e2005-08-24 21:25:16 -05006294
6295 case IPW_PARAM_PRIVACY_INVOKED:
6296 priv->ieee->privacy_invoked = value;
6297 break;
6298
6299 case IPW_PARAM_AUTH_ALGS:
6300 ret = ipw_wpa_set_auth_algs(priv, value);
6301 break;
6302
6303 case IPW_PARAM_IEEE_802_1X:
6304 priv->ieee->ieee802_1x = value;
6305 break;
6306
6307 default:
6308 IPW_ERROR("%s: Unknown WPA param: %d\n", dev->name, name);
6309 ret = -EOPNOTSUPP;
6310 }
6311
6312 return ret;
6313}
6314
6315static int ipw_wpa_mlme(struct net_device *dev, int command, int reason)
6316{
6317 struct ipw_priv *priv = ieee80211_priv(dev);
6318 int ret = 0;
6319
6320 switch (command) {
6321 case IPW_MLME_STA_DEAUTH:
6322 // silently ignore
6323 break;
6324
6325 case IPW_MLME_STA_DISASSOC:
6326 ipw_disassociate(priv);
6327 break;
6328
6329 default:
6330 IPW_ERROR("%s: Unknown MLME request: %d\n", dev->name, command);
6331 ret = -EOPNOTSUPP;
6332 }
6333
6334 return ret;
6335}
6336
Zhu Yi1fbfea52005-08-05 17:22:56 +08006337static int ipw_wpa_ie_cipher2level(u8 cipher)
6338{
6339 switch (cipher) {
6340 case 4: /* CCMP */
6341 return SEC_LEVEL_3;
6342 case 2: /* TKIP */
6343 return SEC_LEVEL_2;
6344 case 5: /* WEP104 */
6345 case 1: /* WEP40 */
6346 return SEC_LEVEL_1;
6347 case 0: /* NONE */
6348 return SEC_LEVEL_0;
6349 default:
6350 return -1;
6351 }
6352}
6353
James Ketrenosea2b26e2005-08-24 21:25:16 -05006354static int ipw_wpa_set_wpa_ie(struct net_device *dev,
6355 struct ipw_param *param, int plen)
6356{
6357 struct ipw_priv *priv = ieee80211_priv(dev);
6358 struct ieee80211_device *ieee = priv->ieee;
6359 u8 *buf;
Zhu Yi1fbfea52005-08-05 17:22:56 +08006360 u8 *ptk, *gtk;
6361 int level;
James Ketrenosea2b26e2005-08-24 21:25:16 -05006362
James Ketrenosea2b26e2005-08-24 21:25:16 -05006363 if (param->u.wpa_ie.len > MAX_WPA_IE_LEN ||
6364 (param->u.wpa_ie.len && param->u.wpa_ie.data == NULL))
6365 return -EINVAL;
6366
6367 if (param->u.wpa_ie.len) {
6368 buf = kmalloc(param->u.wpa_ie.len, GFP_KERNEL);
6369 if (buf == NULL)
6370 return -ENOMEM;
6371
6372 memcpy(buf, param->u.wpa_ie.data, param->u.wpa_ie.len);
6373 kfree(ieee->wpa_ie);
6374 ieee->wpa_ie = buf;
6375 ieee->wpa_ie_len = param->u.wpa_ie.len;
6376 } else {
6377 kfree(ieee->wpa_ie);
6378 ieee->wpa_ie = NULL;
6379 ieee->wpa_ie_len = 0;
Zhu Yi1fbfea52005-08-05 17:22:56 +08006380 goto done;
James Ketrenosea2b26e2005-08-24 21:25:16 -05006381 }
6382
Zhu Yi1fbfea52005-08-05 17:22:56 +08006383 if (priv->ieee->host_encrypt)
6384 goto done;
6385
6386 /* HACK: Parse wpa_ie here to get pairwise suite, otherwise
6387 * we need to change driver_ipw.c from wpa_supplicant. This
6388 * is OK since -Dipw is deprecated. The -Dwext driver has a
6389 * clean way to handle this. */
6390 gtk = ptk = (u8 *) ieee->wpa_ie;
6391 if (ieee->wpa_ie[0] == 0x30) { /* RSN IE */
6392 gtk += 4 + 3;
6393 ptk += 4 + 4 + 2 + 3;
6394 } else { /* WPA IE */
6395 gtk += 8 + 3;
6396 ptk += 8 + 4 + 2 + 3;
6397 }
6398
6399 if (ptk - (u8 *) ieee->wpa_ie > ieee->wpa_ie_len)
6400 return -EINVAL;
6401
6402 level = ipw_wpa_ie_cipher2level(*gtk);
6403 ipw_set_hw_decrypt_multicast(priv, level);
6404
6405 level = ipw_wpa_ie_cipher2level(*ptk);
6406 ipw_set_hw_decrypt_unicast(priv, level);
6407
6408 done:
James Ketrenosea2b26e2005-08-24 21:25:16 -05006409 ipw_wpa_assoc_frame(priv, ieee->wpa_ie, ieee->wpa_ie_len);
6410 return 0;
6411}
6412
6413/* implementation borrowed from hostap driver */
6414
6415static int ipw_wpa_set_encryption(struct net_device *dev,
6416 struct ipw_param *param, int param_len)
6417{
6418 int ret = 0;
6419 struct ipw_priv *priv = ieee80211_priv(dev);
6420 struct ieee80211_device *ieee = priv->ieee;
6421 struct ieee80211_crypto_ops *ops;
6422 struct ieee80211_crypt_data **crypt;
6423
6424 struct ieee80211_security sec = {
6425 .flags = 0,
6426 };
6427
6428 param->u.crypt.err = 0;
6429 param->u.crypt.alg[IPW_CRYPT_ALG_NAME_LEN - 1] = '\0';
6430
6431 if (param_len !=
6432 (int)((char *)param->u.crypt.key - (char *)param) +
6433 param->u.crypt.key_len) {
6434 IPW_DEBUG_INFO("Len mismatch %d, %d\n", param_len,
6435 param->u.crypt.key_len);
6436 return -EINVAL;
6437 }
6438 if (param->sta_addr[0] == 0xff && param->sta_addr[1] == 0xff &&
6439 param->sta_addr[2] == 0xff && param->sta_addr[3] == 0xff &&
6440 param->sta_addr[4] == 0xff && param->sta_addr[5] == 0xff) {
6441 if (param->u.crypt.idx >= WEP_KEYS)
6442 return -EINVAL;
6443 crypt = &ieee->crypt[param->u.crypt.idx];
6444 } else {
6445 return -EINVAL;
6446 }
6447
James Ketrenosafbf30a2005-08-25 00:05:33 -05006448 sec.flags |= SEC_ENABLED | SEC_ENCRYPT;
James Ketrenosea2b26e2005-08-24 21:25:16 -05006449 if (strcmp(param->u.crypt.alg, "none") == 0) {
6450 if (crypt) {
6451 sec.enabled = 0;
James Ketrenosb095c382005-08-24 22:04:42 -05006452 sec.encrypt = 0;
James Ketrenosea2b26e2005-08-24 21:25:16 -05006453 sec.level = SEC_LEVEL_0;
James Ketrenosafbf30a2005-08-25 00:05:33 -05006454 sec.flags |= SEC_LEVEL;
James Ketrenosea2b26e2005-08-24 21:25:16 -05006455 ieee80211_crypt_delayed_deinit(ieee, crypt);
6456 }
6457 goto done;
6458 }
6459 sec.enabled = 1;
James Ketrenosb095c382005-08-24 22:04:42 -05006460 sec.encrypt = 1;
James Ketrenosea2b26e2005-08-24 21:25:16 -05006461
James Ketrenosb095c382005-08-24 22:04:42 -05006462 /* IPW HW cannot build TKIP MIC, host decryption still needed. */
James Ketrenosafbf30a2005-08-25 00:05:33 -05006463 if (strcmp(param->u.crypt.alg, "TKIP") == 0)
6464 ieee->host_encrypt_msdu = 1;
6465
6466 if (!(ieee->host_encrypt || ieee->host_encrypt_msdu ||
6467 ieee->host_decrypt))
James Ketrenosb095c382005-08-24 22:04:42 -05006468 goto skip_host_crypt;
6469
James Ketrenosea2b26e2005-08-24 21:25:16 -05006470 ops = ieee80211_get_crypto_ops(param->u.crypt.alg);
6471 if (ops == NULL && strcmp(param->u.crypt.alg, "WEP") == 0) {
6472 request_module("ieee80211_crypt_wep");
6473 ops = ieee80211_get_crypto_ops(param->u.crypt.alg);
6474 } else if (ops == NULL && strcmp(param->u.crypt.alg, "TKIP") == 0) {
6475 request_module("ieee80211_crypt_tkip");
6476 ops = ieee80211_get_crypto_ops(param->u.crypt.alg);
6477 } else if (ops == NULL && strcmp(param->u.crypt.alg, "CCMP") == 0) {
6478 request_module("ieee80211_crypt_ccmp");
6479 ops = ieee80211_get_crypto_ops(param->u.crypt.alg);
6480 }
6481 if (ops == NULL) {
6482 IPW_DEBUG_INFO("%s: unknown crypto alg '%s'\n",
6483 dev->name, param->u.crypt.alg);
6484 param->u.crypt.err = IPW_CRYPT_ERR_UNKNOWN_ALG;
6485 ret = -EINVAL;
6486 goto done;
6487 }
6488
6489 if (*crypt == NULL || (*crypt)->ops != ops) {
6490 struct ieee80211_crypt_data *new_crypt;
6491
6492 ieee80211_crypt_delayed_deinit(ieee, crypt);
6493
6494 new_crypt = (struct ieee80211_crypt_data *)
6495 kmalloc(sizeof(*new_crypt), GFP_KERNEL);
6496 if (new_crypt == NULL) {
6497 ret = -ENOMEM;
6498 goto done;
6499 }
6500 memset(new_crypt, 0, sizeof(struct ieee80211_crypt_data));
6501 new_crypt->ops = ops;
6502 if (new_crypt->ops && try_module_get(new_crypt->ops->owner))
6503 new_crypt->priv =
6504 new_crypt->ops->init(param->u.crypt.idx);
6505
6506 if (new_crypt->priv == NULL) {
6507 kfree(new_crypt);
6508 param->u.crypt.err = IPW_CRYPT_ERR_CRYPT_INIT_FAILED;
6509 ret = -EINVAL;
6510 goto done;
6511 }
6512
6513 *crypt = new_crypt;
6514 }
6515
6516 if (param->u.crypt.key_len > 0 && (*crypt)->ops->set_key &&
6517 (*crypt)->ops->set_key(param->u.crypt.key,
6518 param->u.crypt.key_len, param->u.crypt.seq,
6519 (*crypt)->priv) < 0) {
6520 IPW_DEBUG_INFO("%s: key setting failed\n", dev->name);
6521 param->u.crypt.err = IPW_CRYPT_ERR_KEY_SET_FAILED;
6522 ret = -EINVAL;
6523 goto done;
6524 }
6525
James Ketrenosb095c382005-08-24 22:04:42 -05006526 skip_host_crypt:
James Ketrenosea2b26e2005-08-24 21:25:16 -05006527 if (param->u.crypt.set_tx) {
6528 ieee->tx_keyidx = param->u.crypt.idx;
6529 sec.active_key = param->u.crypt.idx;
6530 sec.flags |= SEC_ACTIVE_KEY;
James Ketrenosb095c382005-08-24 22:04:42 -05006531 } else
6532 sec.flags &= ~SEC_ACTIVE_KEY;
James Ketrenosea2b26e2005-08-24 21:25:16 -05006533
James Ketrenosb095c382005-08-24 22:04:42 -05006534 if (param->u.crypt.alg != NULL) {
6535 memcpy(sec.keys[param->u.crypt.idx],
6536 param->u.crypt.key, param->u.crypt.key_len);
6537 sec.key_sizes[param->u.crypt.idx] = param->u.crypt.key_len;
6538 sec.flags |= (1 << param->u.crypt.idx);
6539
6540 if (strcmp(param->u.crypt.alg, "WEP") == 0) {
James Ketrenosea2b26e2005-08-24 21:25:16 -05006541 sec.flags |= SEC_LEVEL;
6542 sec.level = SEC_LEVEL_1;
James Ketrenosb095c382005-08-24 22:04:42 -05006543 } else if (strcmp(param->u.crypt.alg, "TKIP") == 0) {
James Ketrenosea2b26e2005-08-24 21:25:16 -05006544 sec.flags |= SEC_LEVEL;
6545 sec.level = SEC_LEVEL_2;
James Ketrenosb095c382005-08-24 22:04:42 -05006546 } else if (strcmp(param->u.crypt.alg, "CCMP") == 0) {
James Ketrenosea2b26e2005-08-24 21:25:16 -05006547 sec.flags |= SEC_LEVEL;
6548 sec.level = SEC_LEVEL_3;
6549 }
6550 }
6551 done:
6552 if (ieee->set_security)
6553 ieee->set_security(ieee->dev, &sec);
6554
6555 /* Do not reset port if card is in Managed mode since resetting will
6556 * generate new IEEE 802.11 authentication which may end up in looping
6557 * with IEEE 802.1X. If your hardware requires a reset after WEP
6558 * configuration (for example... Prism2), implement the reset_port in
6559 * the callbacks structures used to initialize the 802.11 stack. */
6560 if (ieee->reset_on_keychange &&
6561 ieee->iw_mode != IW_MODE_INFRA &&
6562 ieee->reset_port && ieee->reset_port(dev)) {
6563 IPW_DEBUG_INFO("%s: reset_port failed\n", dev->name);
6564 param->u.crypt.err = IPW_CRYPT_ERR_CARD_CONF_FAILED;
6565 return -EINVAL;
6566 }
6567
6568 return ret;
6569}
6570
6571static int ipw_wpa_supplicant(struct net_device *dev, struct iw_point *p)
6572{
6573 struct ipw_param *param;
James Ketrenosafbf30a2005-08-25 00:05:33 -05006574 struct ipw_priv *priv = ieee80211_priv(dev);
James Ketrenosea2b26e2005-08-24 21:25:16 -05006575 int ret = 0;
6576
6577 IPW_DEBUG_INFO("wpa_supplicant: len=%d\n", p->length);
6578
6579 if (p->length < sizeof(struct ipw_param) || !p->pointer)
6580 return -EINVAL;
6581
6582 param = (struct ipw_param *)kmalloc(p->length, GFP_KERNEL);
6583 if (param == NULL)
6584 return -ENOMEM;
6585
6586 if (copy_from_user(param, p->pointer, p->length)) {
6587 kfree(param);
6588 return -EFAULT;
6589 }
6590
James Ketrenosafbf30a2005-08-25 00:05:33 -05006591 down(&priv->sem);
James Ketrenosea2b26e2005-08-24 21:25:16 -05006592 switch (param->cmd) {
6593
6594 case IPW_CMD_SET_WPA_PARAM:
6595 ret = ipw_wpa_set_param(dev, param->u.wpa_param.name,
6596 param->u.wpa_param.value);
6597 break;
6598
6599 case IPW_CMD_SET_WPA_IE:
6600 ret = ipw_wpa_set_wpa_ie(dev, param, p->length);
6601 break;
6602
6603 case IPW_CMD_SET_ENCRYPTION:
6604 ret = ipw_wpa_set_encryption(dev, param, p->length);
6605 break;
6606
6607 case IPW_CMD_MLME:
6608 ret = ipw_wpa_mlme(dev, param->u.mlme.command,
6609 param->u.mlme.reason_code);
6610 break;
6611
6612 default:
6613 IPW_ERROR("%s: Unknown WPA supplicant request: %d\n",
6614 dev->name, param->cmd);
6615 ret = -EOPNOTSUPP;
6616 }
6617
James Ketrenosafbf30a2005-08-25 00:05:33 -05006618 up(&priv->sem);
James Ketrenosea2b26e2005-08-24 21:25:16 -05006619 if (ret == 0 && copy_to_user(p->pointer, param, p->length))
6620 ret = -EFAULT;
6621
6622 kfree(param);
6623 return ret;
6624}
James Ketrenosafbf30a2005-08-25 00:05:33 -05006625#else
6626/*
6627 * WE-18 support
6628 */
6629
6630/* SIOCSIWGENIE */
6631static int ipw_wx_set_genie(struct net_device *dev,
6632 struct iw_request_info *info,
6633 union iwreq_data *wrqu, char *extra)
6634{
6635 struct ipw_priv *priv = ieee80211_priv(dev);
6636 struct ieee80211_device *ieee = priv->ieee;
6637 u8 *buf;
6638 int err = 0;
6639
6640 if (wrqu->data.length > MAX_WPA_IE_LEN ||
6641 (wrqu->data.length && extra == NULL))
6642 return -EINVAL;
6643
6644 //down(&priv->sem);
6645
6646 //if (!ieee->wpa_enabled) {
6647 // err = -EOPNOTSUPP;
6648 // goto out;
6649 //}
6650
6651 if (wrqu->data.length) {
6652 buf = kmalloc(wrqu->data.length, GFP_KERNEL);
6653 if (buf == NULL) {
6654 err = -ENOMEM;
6655 goto out;
6656 }
6657
6658 memcpy(buf, extra, wrqu->data.length);
6659 kfree(ieee->wpa_ie);
6660 ieee->wpa_ie = buf;
6661 ieee->wpa_ie_len = wrqu->data.length;
6662 } else {
6663 kfree(ieee->wpa_ie);
6664 ieee->wpa_ie = NULL;
6665 ieee->wpa_ie_len = 0;
6666 }
6667
6668 ipw_wpa_assoc_frame(priv, ieee->wpa_ie, ieee->wpa_ie_len);
6669 out:
6670 //up(&priv->sem);
6671 return err;
6672}
6673
6674/* SIOCGIWGENIE */
6675static int ipw_wx_get_genie(struct net_device *dev,
6676 struct iw_request_info *info,
6677 union iwreq_data *wrqu, char *extra)
6678{
6679 struct ipw_priv *priv = ieee80211_priv(dev);
6680 struct ieee80211_device *ieee = priv->ieee;
6681 int err = 0;
6682
6683 //down(&priv->sem);
6684
6685 //if (!ieee->wpa_enabled) {
6686 // err = -EOPNOTSUPP;
6687 // goto out;
6688 //}
6689
6690 if (ieee->wpa_ie_len == 0 || ieee->wpa_ie == NULL) {
6691 wrqu->data.length = 0;
6692 goto out;
6693 }
6694
6695 if (wrqu->data.length < ieee->wpa_ie_len) {
6696 err = -E2BIG;
6697 goto out;
6698 }
6699
6700 wrqu->data.length = ieee->wpa_ie_len;
6701 memcpy(extra, ieee->wpa_ie, ieee->wpa_ie_len);
6702
6703 out:
6704 //up(&priv->sem);
6705 return err;
6706}
6707
Zhu Yi1fbfea52005-08-05 17:22:56 +08006708static int wext_cipher2level(int cipher)
6709{
6710 switch (cipher) {
6711 case IW_AUTH_CIPHER_NONE:
6712 return SEC_LEVEL_0;
6713 case IW_AUTH_CIPHER_WEP40:
6714 case IW_AUTH_CIPHER_WEP104:
6715 return SEC_LEVEL_1;
6716 case IW_AUTH_CIPHER_TKIP:
6717 return SEC_LEVEL_2;
6718 case IW_AUTH_CIPHER_CCMP:
6719 return SEC_LEVEL_3;
6720 default:
6721 return -1;
6722 }
6723}
6724
James Ketrenosafbf30a2005-08-25 00:05:33 -05006725/* SIOCSIWAUTH */
6726static int ipw_wx_set_auth(struct net_device *dev,
6727 struct iw_request_info *info,
6728 union iwreq_data *wrqu, char *extra)
6729{
6730 struct ipw_priv *priv = ieee80211_priv(dev);
6731 struct ieee80211_device *ieee = priv->ieee;
6732 struct iw_param *param = &wrqu->param;
6733 struct ieee80211_crypt_data *crypt;
6734 unsigned long flags;
6735 int ret = 0;
6736
6737 switch (param->flags & IW_AUTH_INDEX) {
6738 case IW_AUTH_WPA_VERSION:
Zhu Yi1fbfea52005-08-05 17:22:56 +08006739 break;
James Ketrenosafbf30a2005-08-25 00:05:33 -05006740 case IW_AUTH_CIPHER_PAIRWISE:
Zhu Yi1fbfea52005-08-05 17:22:56 +08006741 ipw_set_hw_decrypt_unicast(priv,
6742 wext_cipher2level(param->value));
6743 break;
James Ketrenosafbf30a2005-08-25 00:05:33 -05006744 case IW_AUTH_CIPHER_GROUP:
Zhu Yi1fbfea52005-08-05 17:22:56 +08006745 ipw_set_hw_decrypt_multicast(priv,
6746 wext_cipher2level(param->value));
6747 break;
James Ketrenosafbf30a2005-08-25 00:05:33 -05006748 case IW_AUTH_KEY_MGMT:
6749 /*
6750 * ipw2200 does not use these parameters
6751 */
6752 break;
6753
6754 case IW_AUTH_TKIP_COUNTERMEASURES:
6755 crypt = priv->ieee->crypt[priv->ieee->tx_keyidx];
6756 if (!crypt || !crypt->ops->set_flags || !crypt->ops->get_flags) {
6757 IPW_WARNING("Can't set TKIP countermeasures: "
6758 "crypt not set!\n");
6759 break;
6760 }
6761
6762 flags = crypt->ops->get_flags(crypt->priv);
6763
6764 if (param->value)
6765 flags |= IEEE80211_CRYPTO_TKIP_COUNTERMEASURES;
6766 else
6767 flags &= ~IEEE80211_CRYPTO_TKIP_COUNTERMEASURES;
6768
6769 crypt->ops->set_flags(flags, crypt->priv);
6770
6771 break;
6772
6773 case IW_AUTH_DROP_UNENCRYPTED:{
6774 /* HACK:
6775 *
6776 * wpa_supplicant calls set_wpa_enabled when the driver
6777 * is loaded and unloaded, regardless of if WPA is being
6778 * used. No other calls are made which can be used to
6779 * determine if encryption will be used or not prior to
6780 * association being expected. If encryption is not being
6781 * used, drop_unencrypted is set to false, else true -- we
6782 * can use this to determine if the CAP_PRIVACY_ON bit should
6783 * be set.
6784 */
6785 struct ieee80211_security sec = {
6786 .flags = SEC_ENABLED,
6787 .enabled = param->value,
6788 };
6789 priv->ieee->drop_unencrypted = param->value;
6790 /* We only change SEC_LEVEL for open mode. Others
6791 * are set by ipw_wpa_set_encryption.
6792 */
6793 if (!param->value) {
6794 sec.flags |= SEC_LEVEL;
6795 sec.level = SEC_LEVEL_0;
6796 } else {
6797 sec.flags |= SEC_LEVEL;
6798 sec.level = SEC_LEVEL_1;
6799 }
6800 if (priv->ieee->set_security)
6801 priv->ieee->set_security(priv->ieee->dev, &sec);
6802 break;
6803 }
6804
6805 case IW_AUTH_80211_AUTH_ALG:
6806 ret = ipw_wpa_set_auth_algs(priv, param->value);
6807 break;
6808
6809 case IW_AUTH_WPA_ENABLED:
6810 ret = ipw_wpa_enable(priv, param->value);
6811 break;
6812
6813 case IW_AUTH_RX_UNENCRYPTED_EAPOL:
6814 ieee->ieee802_1x = param->value;
6815 break;
6816
6817 //case IW_AUTH_ROAMING_CONTROL:
6818 case IW_AUTH_PRIVACY_INVOKED:
6819 ieee->privacy_invoked = param->value;
6820 break;
6821
6822 default:
6823 return -EOPNOTSUPP;
6824 }
6825 return ret;
6826}
6827
6828/* SIOCGIWAUTH */
6829static int ipw_wx_get_auth(struct net_device *dev,
6830 struct iw_request_info *info,
6831 union iwreq_data *wrqu, char *extra)
6832{
6833 struct ipw_priv *priv = ieee80211_priv(dev);
6834 struct ieee80211_device *ieee = priv->ieee;
6835 struct ieee80211_crypt_data *crypt;
6836 struct iw_param *param = &wrqu->param;
6837 int ret = 0;
6838
6839 switch (param->flags & IW_AUTH_INDEX) {
6840 case IW_AUTH_WPA_VERSION:
6841 case IW_AUTH_CIPHER_PAIRWISE:
6842 case IW_AUTH_CIPHER_GROUP:
6843 case IW_AUTH_KEY_MGMT:
6844 /*
6845 * wpa_supplicant will control these internally
6846 */
6847 ret = -EOPNOTSUPP;
6848 break;
6849
6850 case IW_AUTH_TKIP_COUNTERMEASURES:
6851 crypt = priv->ieee->crypt[priv->ieee->tx_keyidx];
6852 if (!crypt || !crypt->ops->get_flags) {
6853 IPW_WARNING("Can't get TKIP countermeasures: "
6854 "crypt not set!\n");
6855 break;
6856 }
6857
6858 param->value = (crypt->ops->get_flags(crypt->priv) &
6859 IEEE80211_CRYPTO_TKIP_COUNTERMEASURES) ? 1 : 0;
6860
6861 break;
6862
6863 case IW_AUTH_DROP_UNENCRYPTED:
6864 param->value = ieee->drop_unencrypted;
6865 break;
6866
6867 case IW_AUTH_80211_AUTH_ALG:
6868 param->value = ieee->sec.auth_mode;
6869 break;
6870
6871 case IW_AUTH_WPA_ENABLED:
6872 param->value = ieee->wpa_enabled;
6873 break;
6874
6875 case IW_AUTH_RX_UNENCRYPTED_EAPOL:
6876 param->value = ieee->ieee802_1x;
6877 break;
6878
6879 case IW_AUTH_ROAMING_CONTROL:
6880 case IW_AUTH_PRIVACY_INVOKED:
6881 param->value = ieee->privacy_invoked;
6882 break;
6883
6884 default:
6885 return -EOPNOTSUPP;
6886 }
6887 return 0;
6888}
6889
6890/* SIOCSIWENCODEEXT */
6891static int ipw_wx_set_encodeext(struct net_device *dev,
6892 struct iw_request_info *info,
6893 union iwreq_data *wrqu, char *extra)
6894{
6895 struct ipw_priv *priv = ieee80211_priv(dev);
6896 struct iw_encode_ext *ext = (struct iw_encode_ext *)extra;
6897
6898 if (hwcrypto) {
6899 /* IPW HW can't build TKIP MIC, host decryption still needed */
6900 if (ext->alg == IW_ENCODE_ALG_TKIP) {
6901 priv->ieee->host_encrypt = 0;
6902 priv->ieee->host_encrypt_msdu = 1;
6903 priv->ieee->host_decrypt = 1;
6904 } else {
6905 priv->ieee->host_encrypt = 0;
6906 priv->ieee->host_encrypt_msdu = 0;
6907 priv->ieee->host_decrypt = 0;
6908 }
6909 }
6910
6911 return ieee80211_wx_set_encodeext(priv->ieee, info, wrqu, extra);
6912}
6913
6914/* SIOCGIWENCODEEXT */
6915static int ipw_wx_get_encodeext(struct net_device *dev,
6916 struct iw_request_info *info,
6917 union iwreq_data *wrqu, char *extra)
6918{
6919 struct ipw_priv *priv = ieee80211_priv(dev);
6920 return ieee80211_wx_get_encodeext(priv->ieee, info, wrqu, extra);
6921}
6922
6923/* SIOCSIWMLME */
6924static int ipw_wx_set_mlme(struct net_device *dev,
6925 struct iw_request_info *info,
6926 union iwreq_data *wrqu, char *extra)
6927{
6928 struct ipw_priv *priv = ieee80211_priv(dev);
6929 struct iw_mlme *mlme = (struct iw_mlme *)extra;
6930 u16 reason;
6931
6932 reason = cpu_to_le16(mlme->reason_code);
6933
6934 switch (mlme->cmd) {
6935 case IW_MLME_DEAUTH:
6936 // silently ignore
6937 break;
6938
6939 case IW_MLME_DISASSOC:
6940 ipw_disassociate(priv);
6941 break;
6942
6943 default:
6944 return -EOPNOTSUPP;
6945 }
6946 return 0;
6947}
6948#endif
James Ketrenosea2b26e2005-08-24 21:25:16 -05006949
James Ketrenosb095c382005-08-24 22:04:42 -05006950#ifdef CONFIG_IPW_QOS
6951
6952/* QoS */
6953/*
6954* get the modulation type of the current network or
6955* the card current mode
6956*/
6957u8 ipw_qos_current_mode(struct ipw_priv * priv)
6958{
6959 u8 mode = 0;
6960
6961 if (priv->status & STATUS_ASSOCIATED) {
6962 unsigned long flags;
6963
6964 spin_lock_irqsave(&priv->ieee->lock, flags);
6965 mode = priv->assoc_network->mode;
6966 spin_unlock_irqrestore(&priv->ieee->lock, flags);
6967 } else {
6968 mode = priv->ieee->mode;
6969 }
6970 IPW_DEBUG_QOS("QoS network/card mode %d \n", mode);
6971 return mode;
6972}
6973
6974/*
6975* Handle management frame beacon and probe response
6976*/
6977static int ipw_qos_handle_probe_reponse(struct ipw_priv *priv,
6978 int active_network,
6979 struct ieee80211_network *network)
6980{
6981 u32 size = sizeof(struct ieee80211_qos_parameters);
6982
James Ketrenosafbf30a2005-08-25 00:05:33 -05006983 if (network->capability & WLAN_CAPABILITY_IBSS)
James Ketrenosb095c382005-08-24 22:04:42 -05006984 network->qos_data.active = network->qos_data.supported;
6985
6986 if (network->flags & NETWORK_HAS_QOS_MASK) {
James Ketrenosafbf30a2005-08-25 00:05:33 -05006987 if (active_network &&
6988 (network->flags & NETWORK_HAS_QOS_PARAMETERS))
James Ketrenosb095c382005-08-24 22:04:42 -05006989 network->qos_data.active = network->qos_data.supported;
6990
6991 if ((network->qos_data.active == 1) && (active_network == 1) &&
6992 (network->flags & NETWORK_HAS_QOS_PARAMETERS) &&
6993 (network->qos_data.old_param_count !=
6994 network->qos_data.param_count)) {
6995 network->qos_data.old_param_count =
6996 network->qos_data.param_count;
6997 schedule_work(&priv->qos_activate);
James Ketrenosafbf30a2005-08-25 00:05:33 -05006998 IPW_DEBUG_QOS("QoS parameters change call "
6999 "qos_activate\n");
James Ketrenosb095c382005-08-24 22:04:42 -05007000 }
7001 } else {
James Ketrenosafbf30a2005-08-25 00:05:33 -05007002 if ((priv->ieee->mode == IEEE_B) || (network->mode == IEEE_B))
7003 memcpy(&network->qos_data.parameters,
James Ketrenosb095c382005-08-24 22:04:42 -05007004 &def_parameters_CCK, size);
James Ketrenosafbf30a2005-08-25 00:05:33 -05007005 else
7006 memcpy(&network->qos_data.parameters,
James Ketrenosb095c382005-08-24 22:04:42 -05007007 &def_parameters_OFDM, size);
James Ketrenosafbf30a2005-08-25 00:05:33 -05007008
James Ketrenosb095c382005-08-24 22:04:42 -05007009 if ((network->qos_data.active == 1) && (active_network == 1)) {
7010 IPW_DEBUG_QOS("QoS was disabled call qos_activate \n");
7011 schedule_work(&priv->qos_activate);
7012 }
7013
7014 network->qos_data.active = 0;
7015 network->qos_data.supported = 0;
7016 }
James Ketrenosafbf30a2005-08-25 00:05:33 -05007017 if ((priv->status & STATUS_ASSOCIATED) &&
7018 (priv->ieee->iw_mode == IW_MODE_ADHOC) && (active_network == 0)) {
7019 if (memcmp(network->bssid, priv->bssid, ETH_ALEN))
7020 if ((network->capability & WLAN_CAPABILITY_IBSS) &&
7021 !(network->flags & NETWORK_EMPTY_ESSID))
James Ketrenosb095c382005-08-24 22:04:42 -05007022 if ((network->ssid_len ==
James Ketrenosafbf30a2005-08-25 00:05:33 -05007023 priv->assoc_network->ssid_len) &&
7024 !memcmp(network->ssid,
7025 priv->assoc_network->ssid,
7026 network->ssid_len)) {
James Ketrenosb095c382005-08-24 22:04:42 -05007027 queue_work(priv->workqueue,
7028 &priv->merge_networks);
7029 }
James Ketrenosb095c382005-08-24 22:04:42 -05007030 }
7031
7032 return 0;
7033}
7034
7035/*
7036* This function set up the firmware to support QoS. It sends
7037* IPW_CMD_QOS_PARAMETERS and IPW_CMD_WME_INFO
7038*/
7039static int ipw_qos_activate(struct ipw_priv *priv,
7040 struct ieee80211_qos_data *qos_network_data)
7041{
7042 int err;
7043 struct ieee80211_qos_parameters qos_parameters[QOS_QOS_SETS];
7044 struct ieee80211_qos_parameters *active_one = NULL;
7045 u32 size = sizeof(struct ieee80211_qos_parameters);
7046 u32 burst_duration;
7047 int i;
7048 u8 type;
7049
7050 type = ipw_qos_current_mode(priv);
7051
7052 active_one = &(qos_parameters[QOS_PARAM_SET_DEF_CCK]);
7053 memcpy(active_one, priv->qos_data.def_qos_parm_CCK, size);
7054 active_one = &(qos_parameters[QOS_PARAM_SET_DEF_OFDM]);
7055 memcpy(active_one, priv->qos_data.def_qos_parm_OFDM, size);
7056
7057 if (qos_network_data == NULL) {
7058 if (type == IEEE_B) {
7059 IPW_DEBUG_QOS("QoS activate network mode %d\n", type);
7060 active_one = &def_parameters_CCK;
7061 } else
7062 active_one = &def_parameters_OFDM;
7063
James Ketrenosafbf30a2005-08-25 00:05:33 -05007064 memcpy(&qos_parameters[QOS_PARAM_SET_ACTIVE], active_one, size);
James Ketrenosb095c382005-08-24 22:04:42 -05007065 burst_duration = ipw_qos_get_burst_duration(priv);
7066 for (i = 0; i < QOS_QUEUE_NUM; i++)
James Ketrenosafbf30a2005-08-25 00:05:33 -05007067 qos_parameters[QOS_PARAM_SET_ACTIVE].tx_op_limit[i] =
7068 (u16) burst_duration;
7069 } else if (priv->ieee->iw_mode == IW_MODE_ADHOC) {
James Ketrenosb095c382005-08-24 22:04:42 -05007070 if (type == IEEE_B) {
7071 IPW_DEBUG_QOS("QoS activate IBSS nework mode %d\n",
7072 type);
7073 if (priv->qos_data.qos_enable == 0)
7074 active_one = &def_parameters_CCK;
7075 else
7076 active_one = priv->qos_data.def_qos_parm_CCK;
7077 } else {
7078 if (priv->qos_data.qos_enable == 0)
7079 active_one = &def_parameters_OFDM;
7080 else
7081 active_one = priv->qos_data.def_qos_parm_OFDM;
7082 }
James Ketrenosafbf30a2005-08-25 00:05:33 -05007083 memcpy(&qos_parameters[QOS_PARAM_SET_ACTIVE], active_one, size);
James Ketrenosb095c382005-08-24 22:04:42 -05007084 } else {
7085 unsigned long flags;
7086 int active;
7087
7088 spin_lock_irqsave(&priv->ieee->lock, flags);
7089 active_one = &(qos_network_data->parameters);
7090 qos_network_data->old_param_count =
7091 qos_network_data->param_count;
James Ketrenosafbf30a2005-08-25 00:05:33 -05007092 memcpy(&qos_parameters[QOS_PARAM_SET_ACTIVE], active_one, size);
James Ketrenosb095c382005-08-24 22:04:42 -05007093 active = qos_network_data->supported;
7094 spin_unlock_irqrestore(&priv->ieee->lock, flags);
7095
7096 if (active == 0) {
7097 burst_duration = ipw_qos_get_burst_duration(priv);
7098 for (i = 0; i < QOS_QUEUE_NUM; i++)
7099 qos_parameters[QOS_PARAM_SET_ACTIVE].
7100 tx_op_limit[i] = (u16) burst_duration;
7101 }
7102 }
7103
7104 IPW_DEBUG_QOS("QoS sending IPW_CMD_QOS_PARAMETERS\n");
James Ketrenosafbf30a2005-08-25 00:05:33 -05007105 err = ipw_send_qos_params_command(priv,
7106 (struct ieee80211_qos_parameters *)
7107 &(qos_parameters[0]));
James Ketrenosb095c382005-08-24 22:04:42 -05007108 if (err)
7109 IPW_DEBUG_QOS("QoS IPW_CMD_QOS_PARAMETERS failed\n");
7110
7111 return err;
7112}
7113
7114/*
7115* send IPW_CMD_WME_INFO to the firmware
7116*/
7117static int ipw_qos_set_info_element(struct ipw_priv *priv)
7118{
7119 int ret = 0;
7120 struct ieee80211_qos_information_element qos_info;
7121
7122 if (priv == NULL)
7123 return -1;
7124
7125 qos_info.elementID = QOS_ELEMENT_ID;
7126 qos_info.length = sizeof(struct ieee80211_qos_information_element) - 2;
7127
7128 qos_info.version = QOS_VERSION_1;
7129 qos_info.ac_info = 0;
7130
7131 memcpy(qos_info.qui, qos_oui, QOS_OUI_LEN);
7132 qos_info.qui_type = QOS_OUI_TYPE;
7133 qos_info.qui_subtype = QOS_OUI_INFO_SUB_TYPE;
7134
7135 ret = ipw_send_qos_info_command(priv, &qos_info);
7136 if (ret != 0) {
7137 IPW_DEBUG_QOS("QoS error calling ipw_send_qos_info_command\n");
7138 }
7139 return ret;
7140}
7141
7142/*
7143* Set the QoS parameter with the association request structure
7144*/
7145static int ipw_qos_association(struct ipw_priv *priv,
7146 struct ieee80211_network *network)
7147{
7148 int err = 0;
7149 struct ieee80211_qos_data *qos_data = NULL;
7150 struct ieee80211_qos_data ibss_data = {
7151 .supported = 1,
7152 .active = 1,
7153 };
7154
7155 switch (priv->ieee->iw_mode) {
7156 case IW_MODE_ADHOC:
7157 if (!(network->capability & WLAN_CAPABILITY_IBSS))
7158 BUG();
7159
7160 qos_data = &ibss_data;
7161 break;
7162
7163 case IW_MODE_INFRA:
7164 qos_data = &network->qos_data;
7165 break;
7166
7167 default:
7168 BUG();
7169 break;
7170 }
7171
7172 err = ipw_qos_activate(priv, qos_data);
7173 if (err) {
7174 priv->assoc_request.policy_support &= ~HC_QOS_SUPPORT_ASSOC;
7175 return err;
7176 }
7177
7178 if (priv->qos_data.qos_enable && qos_data->supported) {
7179 IPW_DEBUG_QOS("QoS will be enabled for this association\n");
7180 priv->assoc_request.policy_support |= HC_QOS_SUPPORT_ASSOC;
7181 return ipw_qos_set_info_element(priv);
7182 }
7183
7184 return 0;
7185}
7186
7187/*
7188* handling the beaconing responces. if we get different QoS setting
7189* of the network from the the associated setting adjust the QoS
7190* setting
7191*/
7192static int ipw_qos_association_resp(struct ipw_priv *priv,
7193 struct ieee80211_network *network)
7194{
7195 int ret = 0;
7196 unsigned long flags;
7197 u32 size = sizeof(struct ieee80211_qos_parameters);
7198 int set_qos_param = 0;
7199
James Ketrenosafbf30a2005-08-25 00:05:33 -05007200 if ((priv == NULL) || (network == NULL) ||
7201 (priv->assoc_network == NULL))
James Ketrenosb095c382005-08-24 22:04:42 -05007202 return ret;
7203
7204 if (!(priv->status & STATUS_ASSOCIATED))
7205 return ret;
7206
James Ketrenosafbf30a2005-08-25 00:05:33 -05007207 if ((priv->ieee->iw_mode != IW_MODE_INFRA))
James Ketrenosb095c382005-08-24 22:04:42 -05007208 return ret;
James Ketrenosb095c382005-08-24 22:04:42 -05007209
7210 spin_lock_irqsave(&priv->ieee->lock, flags);
7211 if (network->flags & NETWORK_HAS_QOS_PARAMETERS) {
James Ketrenosafbf30a2005-08-25 00:05:33 -05007212 memcpy(&priv->assoc_network->qos_data, &network->qos_data,
James Ketrenosb095c382005-08-24 22:04:42 -05007213 sizeof(struct ieee80211_qos_data));
7214 priv->assoc_network->qos_data.active = 1;
7215 if ((network->qos_data.old_param_count !=
7216 network->qos_data.param_count)) {
7217 set_qos_param = 1;
7218 network->qos_data.old_param_count =
7219 network->qos_data.param_count;
7220 }
7221
7222 } else {
James Ketrenosafbf30a2005-08-25 00:05:33 -05007223 if ((network->mode == IEEE_B) || (priv->ieee->mode == IEEE_B))
7224 memcpy(&priv->assoc_network->qos_data.parameters,
James Ketrenosb095c382005-08-24 22:04:42 -05007225 &def_parameters_CCK, size);
James Ketrenosafbf30a2005-08-25 00:05:33 -05007226 else
7227 memcpy(&priv->assoc_network->qos_data.parameters,
James Ketrenosb095c382005-08-24 22:04:42 -05007228 &def_parameters_OFDM, size);
James Ketrenosb095c382005-08-24 22:04:42 -05007229 priv->assoc_network->qos_data.active = 0;
7230 priv->assoc_network->qos_data.supported = 0;
7231 set_qos_param = 1;
7232 }
7233
7234 spin_unlock_irqrestore(&priv->ieee->lock, flags);
7235
7236 if (set_qos_param == 1)
7237 schedule_work(&priv->qos_activate);
7238
7239 return ret;
7240}
7241
7242static u32 ipw_qos_get_burst_duration(struct ipw_priv *priv)
7243{
7244 u32 ret = 0;
7245
7246 if ((priv == NULL))
7247 return 0;
7248
James Ketrenosafbf30a2005-08-25 00:05:33 -05007249 if (!(priv->ieee->modulation & IEEE80211_OFDM_MODULATION))
James Ketrenosb095c382005-08-24 22:04:42 -05007250 ret = priv->qos_data.burst_duration_CCK;
James Ketrenosafbf30a2005-08-25 00:05:33 -05007251 else
James Ketrenosb095c382005-08-24 22:04:42 -05007252 ret = priv->qos_data.burst_duration_OFDM;
James Ketrenosafbf30a2005-08-25 00:05:33 -05007253
James Ketrenosb095c382005-08-24 22:04:42 -05007254 return ret;
7255}
7256
7257/*
7258* Initialize the setting of QoS global
7259*/
7260static void ipw_qos_init(struct ipw_priv *priv, int enable,
7261 int burst_enable, u32 burst_duration_CCK,
7262 u32 burst_duration_OFDM)
7263{
7264 priv->qos_data.qos_enable = enable;
7265
7266 if (priv->qos_data.qos_enable) {
7267 priv->qos_data.def_qos_parm_CCK = &def_qos_parameters_CCK;
7268 priv->qos_data.def_qos_parm_OFDM = &def_qos_parameters_OFDM;
7269 IPW_DEBUG_QOS("QoS is enabled\n");
7270 } else {
7271 priv->qos_data.def_qos_parm_CCK = &def_parameters_CCK;
7272 priv->qos_data.def_qos_parm_OFDM = &def_parameters_OFDM;
7273 IPW_DEBUG_QOS("QoS is not enabled\n");
7274 }
7275
7276 priv->qos_data.burst_enable = burst_enable;
7277
7278 if (burst_enable) {
7279 priv->qos_data.burst_duration_CCK = burst_duration_CCK;
7280 priv->qos_data.burst_duration_OFDM = burst_duration_OFDM;
7281 } else {
7282 priv->qos_data.burst_duration_CCK = 0;
7283 priv->qos_data.burst_duration_OFDM = 0;
7284 }
7285}
7286
7287/*
7288* map the packet priority to the right TX Queue
7289*/
7290static int ipw_get_tx_queue_number(struct ipw_priv *priv, u16 priority)
7291{
7292 if (priority > 7 || !priv->qos_data.qos_enable)
7293 priority = 0;
7294
7295 return from_priority_to_tx_queue[priority] - 1;
7296}
7297
7298/*
7299* add QoS parameter to the TX command
7300*/
7301static int ipw_qos_set_tx_queue_command(struct ipw_priv *priv,
7302 u16 priority,
7303 struct tfd_data *tfd, u8 unicast)
7304{
7305 int ret = 0;
7306 int tx_queue_id = 0;
7307 struct ieee80211_qos_data *qos_data = NULL;
7308 int active, supported;
7309 unsigned long flags;
7310
7311 if (!(priv->status & STATUS_ASSOCIATED))
7312 return 0;
7313
7314 qos_data = &priv->assoc_network->qos_data;
7315
7316 spin_lock_irqsave(&priv->ieee->lock, flags);
7317
7318 if (priv->ieee->iw_mode == IW_MODE_ADHOC) {
7319 if (unicast == 0)
7320 qos_data->active = 0;
7321 else
7322 qos_data->active = qos_data->supported;
7323 }
7324
7325 active = qos_data->active;
7326 supported = qos_data->supported;
7327
7328 spin_unlock_irqrestore(&priv->ieee->lock, flags);
7329
James Ketrenosafbf30a2005-08-25 00:05:33 -05007330 IPW_DEBUG_QOS("QoS %d network is QoS active %d supported %d "
7331 "unicast %d\n",
7332 priv->qos_data.qos_enable, active, supported, unicast);
James Ketrenosb095c382005-08-24 22:04:42 -05007333 if (active && priv->qos_data.qos_enable) {
7334 ret = from_priority_to_tx_queue[priority];
7335 tx_queue_id = ret - 1;
7336 IPW_DEBUG_QOS("QoS packet priority is %d \n", priority);
7337 if (priority <= 7) {
7338 tfd->tx_flags_ext |= DCT_FLAG_EXT_QOS_ENABLED;
7339 tfd->tfd.tfd_26.mchdr.qos_ctrl = priority;
7340 tfd->tfd.tfd_26.mchdr.frame_ctl |=
7341 IEEE80211_STYPE_QOS_DATA;
7342
7343 if (priv->qos_data.qos_no_ack_mask &
7344 (1UL << tx_queue_id)) {
7345 tfd->tx_flags &= ~DCT_FLAG_ACK_REQD;
7346 tfd->tfd.tfd_26.mchdr.qos_ctrl |=
7347 CTRL_QOS_NO_ACK;
7348 }
7349 }
7350 }
7351
7352 return ret;
7353}
7354
7355/*
7356* background support to run QoS activate functionality
7357*/
7358static void ipw_bg_qos_activate(void *data)
7359{
7360 struct ipw_priv *priv = data;
7361
7362 if (priv == NULL)
7363 return;
7364
7365 down(&priv->sem);
7366
7367 if (priv->status & STATUS_ASSOCIATED)
7368 ipw_qos_activate(priv, &(priv->assoc_network->qos_data));
7369
7370 up(&priv->sem);
7371}
7372
7373/*
7374* Handler for probe responce and beacon frame
7375*/
James Ketrenos2b184d52005-08-03 20:33:14 -05007376static int ipw_handle_management(struct net_device *dev,
7377 struct ieee80211_network *network, u16 type)
James Ketrenosb095c382005-08-24 22:04:42 -05007378{
7379 struct ipw_priv *priv = ieee80211_priv(dev);
7380 int active_network;
7381
7382 if (priv->status & STATUS_ASSOCIATED && network == priv->assoc_network)
7383 active_network = 1;
7384 else
7385 active_network = 0;
7386
7387 switch (type) {
7388 case IEEE80211_STYPE_PROBE_RESP:
7389 case IEEE80211_STYPE_BEACON:
7390 ipw_qos_handle_probe_reponse(priv, active_network, network);
7391 break;
7392 case IEEE80211_STYPE_ASSOC_RESP:
7393 ipw_qos_association_resp(priv, network);
7394 break;
7395 default:
7396 break;
7397 }
7398
7399 return 0;
7400}
7401
7402static int ipw_send_qos_params_command(struct ipw_priv *priv, struct ieee80211_qos_parameters
7403 *qos_param)
7404{
7405 struct host_cmd cmd = {
7406 .cmd = IPW_CMD_QOS_PARAMETERS,
7407 .len = (sizeof(struct ieee80211_qos_parameters) * 3)
7408 };
7409
James Ketrenosafbf30a2005-08-25 00:05:33 -05007410 memcpy(cmd.param, qos_param, sizeof(*qos_param) * 3);
James Ketrenos9ddf84f2005-08-16 17:07:11 -05007411 return ipw_send_cmd(priv, &cmd);
James Ketrenosb095c382005-08-24 22:04:42 -05007412}
7413
7414static int ipw_send_qos_info_command(struct ipw_priv *priv, struct ieee80211_qos_information_element
7415 *qos_param)
7416{
7417 struct host_cmd cmd = {
7418 .cmd = IPW_CMD_WME_INFO,
7419 .len = sizeof(*qos_param)
7420 };
7421
James Ketrenosafbf30a2005-08-25 00:05:33 -05007422 memcpy(cmd.param, qos_param, sizeof(*qos_param));
James Ketrenos9ddf84f2005-08-16 17:07:11 -05007423 return ipw_send_cmd(priv, &cmd);
James Ketrenosb095c382005-08-24 22:04:42 -05007424}
7425
7426#endif /* CONFIG_IPW_QOS */
7427
James Ketrenos43f66a62005-03-25 12:31:53 -06007428static int ipw_associate_network(struct ipw_priv *priv,
7429 struct ieee80211_network *network,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04007430 struct ipw_supported_rates *rates, int roaming)
James Ketrenos43f66a62005-03-25 12:31:53 -06007431{
7432 int err;
7433
7434 if (priv->config & CFG_FIXED_RATE)
James Ketrenosb095c382005-08-24 22:04:42 -05007435 ipw_set_fixed_rate(priv, network->mode);
James Ketrenos43f66a62005-03-25 12:31:53 -06007436
7437 if (!(priv->config & CFG_STATIC_ESSID)) {
Jeff Garzikbf794512005-07-31 13:07:26 -04007438 priv->essid_len = min(network->ssid_len,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04007439 (u8) IW_ESSID_MAX_SIZE);
James Ketrenos43f66a62005-03-25 12:31:53 -06007440 memcpy(priv->essid, network->ssid, priv->essid_len);
7441 }
7442
7443 network->last_associate = jiffies;
7444
7445 memset(&priv->assoc_request, 0, sizeof(priv->assoc_request));
7446 priv->assoc_request.channel = network->channel;
7447 if ((priv->capability & CAP_PRIVACY_ON) &&
7448 (priv->capability & CAP_SHARED_KEY)) {
7449 priv->assoc_request.auth_type = AUTH_SHARED_KEY;
James Ketrenosb095c382005-08-24 22:04:42 -05007450 priv->assoc_request.auth_key = priv->ieee->sec.active_key;
7451
7452 if ((priv->capability & CAP_PRIVACY_ON) &&
7453 (priv->ieee->sec.level == SEC_LEVEL_1) &&
7454 !(priv->ieee->host_encrypt || priv->ieee->host_decrypt))
7455 ipw_send_wep_keys(priv, DCW_WEP_KEY_SEC_TYPE_WEP);
James Ketrenos43f66a62005-03-25 12:31:53 -06007456 } else {
7457 priv->assoc_request.auth_type = AUTH_OPEN;
7458 priv->assoc_request.auth_key = 0;
7459 }
7460
James Ketrenosa613bff2005-08-24 21:43:11 -05007461 if (priv->ieee->wpa_ie_len) {
James Ketrenosea2b26e2005-08-24 21:25:16 -05007462 priv->assoc_request.policy_support = 0x02; /* RSN active */
7463 ipw_set_rsn_capa(priv, priv->ieee->wpa_ie,
7464 priv->ieee->wpa_ie_len);
7465 }
James Ketrenosea2b26e2005-08-24 21:25:16 -05007466
Jeff Garzikbf794512005-07-31 13:07:26 -04007467 /*
7468 * It is valid for our ieee device to support multiple modes, but
7469 * when it comes to associating to a given network we have to choose
James Ketrenos43f66a62005-03-25 12:31:53 -06007470 * just one mode.
7471 */
7472 if (network->mode & priv->ieee->mode & IEEE_A)
7473 priv->assoc_request.ieee_mode = IPW_A_MODE;
7474 else if (network->mode & priv->ieee->mode & IEEE_G)
7475 priv->assoc_request.ieee_mode = IPW_G_MODE;
7476 else if (network->mode & priv->ieee->mode & IEEE_B)
7477 priv->assoc_request.ieee_mode = IPW_B_MODE;
7478
James Ketrenosea2b26e2005-08-24 21:25:16 -05007479 priv->assoc_request.capability = network->capability;
7480 if ((network->capability & WLAN_CAPABILITY_SHORT_PREAMBLE)
7481 && !(priv->config & CFG_PREAMBLE_LONG)) {
7482 priv->assoc_request.preamble_length = DCT_FLAG_SHORT_PREAMBLE;
7483 } else {
7484 priv->assoc_request.preamble_length = DCT_FLAG_LONG_PREAMBLE;
7485
7486 /* Clear the short preamble if we won't be supporting it */
7487 priv->assoc_request.capability &=
7488 ~WLAN_CAPABILITY_SHORT_PREAMBLE;
7489 }
7490
James Ketrenosafbf30a2005-08-25 00:05:33 -05007491 /* Clear capability bits that aren't used in Ad Hoc */
7492 if (priv->ieee->iw_mode == IW_MODE_ADHOC)
7493 priv->assoc_request.capability &=
7494 ~WLAN_CAPABILITY_SHORT_SLOT_TIME;
7495
James Ketrenos43f66a62005-03-25 12:31:53 -06007496 IPW_DEBUG_ASSOC("%sssocation attempt: '%s', channel %d, "
James Ketrenosea2b26e2005-08-24 21:25:16 -05007497 "802.11%c [%d], %s[:%s], enc=%s%s%s%c%c\n",
James Ketrenos43f66a62005-03-25 12:31:53 -06007498 roaming ? "Rea" : "A",
Jeff Garzikbf794512005-07-31 13:07:26 -04007499 escape_essid(priv->essid, priv->essid_len),
7500 network->channel,
7501 ipw_modes[priv->assoc_request.ieee_mode],
7502 rates->num_rates,
James Ketrenosea2b26e2005-08-24 21:25:16 -05007503 (priv->assoc_request.preamble_length ==
7504 DCT_FLAG_LONG_PREAMBLE) ? "long" : "short",
7505 network->capability &
7506 WLAN_CAPABILITY_SHORT_PREAMBLE ? "short" : "long",
James Ketrenos43f66a62005-03-25 12:31:53 -06007507 priv->capability & CAP_PRIVACY_ON ? "on " : "off",
Jeff Garzikbf794512005-07-31 13:07:26 -04007508 priv->capability & CAP_PRIVACY_ON ?
7509 (priv->capability & CAP_SHARED_KEY ? "(shared)" :
James Ketrenos43f66a62005-03-25 12:31:53 -06007510 "(open)") : "",
7511 priv->capability & CAP_PRIVACY_ON ? " key=" : "",
Jeff Garzikbf794512005-07-31 13:07:26 -04007512 priv->capability & CAP_PRIVACY_ON ?
James Ketrenosb095c382005-08-24 22:04:42 -05007513 '1' + priv->ieee->sec.active_key : '.',
Jeff Garzik0edd5b42005-09-07 00:48:31 -04007514 priv->capability & CAP_PRIVACY_ON ? '.' : ' ');
James Ketrenos43f66a62005-03-25 12:31:53 -06007515
7516 priv->assoc_request.beacon_interval = network->beacon_interval;
7517 if ((priv->ieee->iw_mode == IW_MODE_ADHOC) &&
Jeff Garzik0edd5b42005-09-07 00:48:31 -04007518 (network->time_stamp[0] == 0) && (network->time_stamp[1] == 0)) {
James Ketrenos43f66a62005-03-25 12:31:53 -06007519 priv->assoc_request.assoc_type = HC_IBSS_START;
7520 priv->assoc_request.assoc_tsf_msw = 0;
7521 priv->assoc_request.assoc_tsf_lsw = 0;
7522 } else {
7523 if (unlikely(roaming))
7524 priv->assoc_request.assoc_type = HC_REASSOCIATE;
7525 else
7526 priv->assoc_request.assoc_type = HC_ASSOCIATE;
7527 priv->assoc_request.assoc_tsf_msw = network->time_stamp[1];
7528 priv->assoc_request.assoc_tsf_lsw = network->time_stamp[0];
7529 }
7530
James Ketrenosafbf30a2005-08-25 00:05:33 -05007531 memcpy(priv->assoc_request.bssid, network->bssid, ETH_ALEN);
James Ketrenos43f66a62005-03-25 12:31:53 -06007532
7533 if (priv->ieee->iw_mode == IW_MODE_ADHOC) {
7534 memset(&priv->assoc_request.dest, 0xFF, ETH_ALEN);
7535 priv->assoc_request.atim_window = network->atim_window;
7536 } else {
James Ketrenosafbf30a2005-08-25 00:05:33 -05007537 memcpy(priv->assoc_request.dest, network->bssid, ETH_ALEN);
James Ketrenos43f66a62005-03-25 12:31:53 -06007538 priv->assoc_request.atim_window = 0;
7539 }
7540
James Ketrenos43f66a62005-03-25 12:31:53 -06007541 priv->assoc_request.listen_interval = network->listen_interval;
Jeff Garzikbf794512005-07-31 13:07:26 -04007542
James Ketrenos43f66a62005-03-25 12:31:53 -06007543 err = ipw_send_ssid(priv, priv->essid, priv->essid_len);
7544 if (err) {
7545 IPW_DEBUG_HC("Attempt to send SSID command failed.\n");
7546 return err;
7547 }
7548
7549 rates->ieee_mode = priv->assoc_request.ieee_mode;
7550 rates->purpose = IPW_RATE_CONNECT;
7551 ipw_send_supported_rates(priv, rates);
Jeff Garzikbf794512005-07-31 13:07:26 -04007552
James Ketrenos43f66a62005-03-25 12:31:53 -06007553 if (priv->assoc_request.ieee_mode == IPW_G_MODE)
7554 priv->sys_config.dot11g_auto_detection = 1;
7555 else
7556 priv->sys_config.dot11g_auto_detection = 0;
James Ketrenosc848d0a2005-08-24 21:56:24 -05007557
7558 if (priv->ieee->iw_mode == IW_MODE_ADHOC)
7559 priv->sys_config.answer_broadcast_ssid_probe = 1;
7560 else
7561 priv->sys_config.answer_broadcast_ssid_probe = 0;
7562
James Ketrenos43f66a62005-03-25 12:31:53 -06007563 err = ipw_send_system_config(priv, &priv->sys_config);
7564 if (err) {
7565 IPW_DEBUG_HC("Attempt to send sys config command failed.\n");
7566 return err;
7567 }
Jeff Garzikbf794512005-07-31 13:07:26 -04007568
James Ketrenos43f66a62005-03-25 12:31:53 -06007569 IPW_DEBUG_ASSOC("Association sensitivity: %d\n", network->stats.rssi);
James Ketrenosea2b26e2005-08-24 21:25:16 -05007570 err = ipw_set_sensitivity(priv, network->stats.rssi + IPW_RSSI_TO_DBM);
James Ketrenos43f66a62005-03-25 12:31:53 -06007571 if (err) {
7572 IPW_DEBUG_HC("Attempt to send associate command failed.\n");
7573 return err;
7574 }
7575
7576 /*
7577 * If preemption is enabled, it is possible for the association
7578 * to complete before we return from ipw_send_associate. Therefore
7579 * we have to be sure and update our priviate data first.
7580 */
7581 priv->channel = network->channel;
7582 memcpy(priv->bssid, network->bssid, ETH_ALEN);
Jeff Garzikbf794512005-07-31 13:07:26 -04007583 priv->status |= STATUS_ASSOCIATING;
James Ketrenos43f66a62005-03-25 12:31:53 -06007584 priv->status &= ~STATUS_SECURITY_UPDATED;
7585
7586 priv->assoc_network = network;
7587
James Ketrenosb095c382005-08-24 22:04:42 -05007588#ifdef CONFIG_IPW_QOS
7589 ipw_qos_association(priv, network);
7590#endif
7591
James Ketrenos43f66a62005-03-25 12:31:53 -06007592 err = ipw_send_associate(priv, &priv->assoc_request);
7593 if (err) {
7594 IPW_DEBUG_HC("Attempt to send associate command failed.\n");
7595 return err;
7596 }
Jeff Garzikbf794512005-07-31 13:07:26 -04007597
7598 IPW_DEBUG(IPW_DL_STATE, "associating: '%s' " MAC_FMT " \n",
James Ketrenos43f66a62005-03-25 12:31:53 -06007599 escape_essid(priv->essid, priv->essid_len),
7600 MAC_ARG(priv->bssid));
7601
7602 return 0;
7603}
7604
7605static void ipw_roam(void *data)
7606{
7607 struct ipw_priv *priv = data;
7608 struct ieee80211_network *network = NULL;
7609 struct ipw_network_match match = {
7610 .network = priv->assoc_network
7611 };
7612
7613 /* The roaming process is as follows:
Jeff Garzikbf794512005-07-31 13:07:26 -04007614 *
7615 * 1. Missed beacon threshold triggers the roaming process by
James Ketrenos43f66a62005-03-25 12:31:53 -06007616 * setting the status ROAM bit and requesting a scan.
7617 * 2. When the scan completes, it schedules the ROAM work
7618 * 3. The ROAM work looks at all of the known networks for one that
7619 * is a better network than the currently associated. If none
7620 * found, the ROAM process is over (ROAM bit cleared)
7621 * 4. If a better network is found, a disassociation request is
7622 * sent.
7623 * 5. When the disassociation completes, the roam work is again
7624 * scheduled. The second time through, the driver is no longer
7625 * associated, and the newly selected network is sent an
Jeff Garzikbf794512005-07-31 13:07:26 -04007626 * association request.
James Ketrenos43f66a62005-03-25 12:31:53 -06007627 * 6. At this point ,the roaming process is complete and the ROAM
7628 * status bit is cleared.
7629 */
7630
7631 /* If we are no longer associated, and the roaming bit is no longer
7632 * set, then we are not actively roaming, so just return */
7633 if (!(priv->status & (STATUS_ASSOCIATED | STATUS_ROAMING)))
7634 return;
Jeff Garzikbf794512005-07-31 13:07:26 -04007635
James Ketrenos43f66a62005-03-25 12:31:53 -06007636 if (priv->status & STATUS_ASSOCIATED) {
Jeff Garzikbf794512005-07-31 13:07:26 -04007637 /* First pass through ROAM process -- look for a better
James Ketrenos43f66a62005-03-25 12:31:53 -06007638 * network */
James Ketrenosa613bff2005-08-24 21:43:11 -05007639 unsigned long flags;
James Ketrenos43f66a62005-03-25 12:31:53 -06007640 u8 rssi = priv->assoc_network->stats.rssi;
7641 priv->assoc_network->stats.rssi = -128;
James Ketrenosa613bff2005-08-24 21:43:11 -05007642 spin_lock_irqsave(&priv->ieee->lock, flags);
James Ketrenos43f66a62005-03-25 12:31:53 -06007643 list_for_each_entry(network, &priv->ieee->network_list, list) {
7644 if (network != priv->assoc_network)
7645 ipw_best_network(priv, &match, network, 1);
7646 }
James Ketrenosa613bff2005-08-24 21:43:11 -05007647 spin_unlock_irqrestore(&priv->ieee->lock, flags);
James Ketrenos43f66a62005-03-25 12:31:53 -06007648 priv->assoc_network->stats.rssi = rssi;
Jeff Garzikbf794512005-07-31 13:07:26 -04007649
James Ketrenos43f66a62005-03-25 12:31:53 -06007650 if (match.network == priv->assoc_network) {
7651 IPW_DEBUG_ASSOC("No better APs in this network to "
7652 "roam to.\n");
7653 priv->status &= ~STATUS_ROAMING;
7654 ipw_debug_config(priv);
7655 return;
7656 }
Jeff Garzikbf794512005-07-31 13:07:26 -04007657
James Ketrenos43f66a62005-03-25 12:31:53 -06007658 ipw_send_disassociate(priv, 1);
7659 priv->assoc_network = match.network;
7660
7661 return;
Jeff Garzikbf794512005-07-31 13:07:26 -04007662 }
James Ketrenos43f66a62005-03-25 12:31:53 -06007663
7664 /* Second pass through ROAM process -- request association */
7665 ipw_compatible_rates(priv, priv->assoc_network, &match.rates);
7666 ipw_associate_network(priv, priv->assoc_network, &match.rates, 1);
7667 priv->status &= ~STATUS_ROAMING;
7668}
7669
James Ketrenosc848d0a2005-08-24 21:56:24 -05007670static void ipw_bg_roam(void *data)
7671{
7672 struct ipw_priv *priv = data;
7673 down(&priv->sem);
7674 ipw_roam(data);
7675 up(&priv->sem);
7676}
7677
7678static int ipw_associate(void *data)
James Ketrenos43f66a62005-03-25 12:31:53 -06007679{
7680 struct ipw_priv *priv = data;
7681
7682 struct ieee80211_network *network = NULL;
7683 struct ipw_network_match match = {
7684 .network = NULL
7685 };
7686 struct ipw_supported_rates *rates;
7687 struct list_head *element;
James Ketrenosa613bff2005-08-24 21:43:11 -05007688 unsigned long flags;
James Ketrenos43f66a62005-03-25 12:31:53 -06007689
James Ketrenosb095c382005-08-24 22:04:42 -05007690 if (priv->ieee->iw_mode == IW_MODE_MONITOR) {
7691 IPW_DEBUG_ASSOC("Not attempting association (monitor mode)\n");
7692 return 0;
7693 }
7694
James Ketrenosc848d0a2005-08-24 21:56:24 -05007695 if (priv->status & (STATUS_ASSOCIATED | STATUS_ASSOCIATING)) {
James Ketrenosafbf30a2005-08-25 00:05:33 -05007696 IPW_DEBUG_ASSOC("Not attempting association (already in "
7697 "progress)\n");
James Ketrenosc848d0a2005-08-24 21:56:24 -05007698 return 0;
7699 }
7700
7701 if (!ipw_is_init(priv) || (priv->status & STATUS_SCANNING)) {
James Ketrenosafbf30a2005-08-25 00:05:33 -05007702 IPW_DEBUG_ASSOC("Not attempting association (scanning or not "
7703 "initialized)\n");
James Ketrenosc848d0a2005-08-24 21:56:24 -05007704 return 0;
7705 }
7706
James Ketrenos43f66a62005-03-25 12:31:53 -06007707 if (!(priv->config & CFG_ASSOCIATE) &&
7708 !(priv->config & (CFG_STATIC_ESSID |
Jeff Garzik0edd5b42005-09-07 00:48:31 -04007709 CFG_STATIC_CHANNEL | CFG_STATIC_BSSID))) {
James Ketrenos43f66a62005-03-25 12:31:53 -06007710 IPW_DEBUG_ASSOC("Not attempting association (associate=0)\n");
James Ketrenosc848d0a2005-08-24 21:56:24 -05007711 return 0;
James Ketrenos43f66a62005-03-25 12:31:53 -06007712 }
7713
James Ketrenosa613bff2005-08-24 21:43:11 -05007714 /* Protect our use of the network_list */
7715 spin_lock_irqsave(&priv->ieee->lock, flags);
Jeff Garzikbf794512005-07-31 13:07:26 -04007716 list_for_each_entry(network, &priv->ieee->network_list, list)
Jeff Garzik0edd5b42005-09-07 00:48:31 -04007717 ipw_best_network(priv, &match, network, 0);
James Ketrenos43f66a62005-03-25 12:31:53 -06007718
7719 network = match.network;
7720 rates = &match.rates;
7721
7722 if (network == NULL &&
7723 priv->ieee->iw_mode == IW_MODE_ADHOC &&
7724 priv->config & CFG_ADHOC_CREATE &&
7725 priv->config & CFG_STATIC_ESSID &&
James Ketrenosa613bff2005-08-24 21:43:11 -05007726 priv->config & CFG_STATIC_CHANNEL &&
James Ketrenos43f66a62005-03-25 12:31:53 -06007727 !list_empty(&priv->ieee->network_free_list)) {
7728 element = priv->ieee->network_free_list.next;
Jeff Garzik0edd5b42005-09-07 00:48:31 -04007729 network = list_entry(element, struct ieee80211_network, list);
James Ketrenos43f66a62005-03-25 12:31:53 -06007730 ipw_adhoc_create(priv, network);
7731 rates = &priv->rates;
7732 list_del(element);
7733 list_add_tail(&network->list, &priv->ieee->network_list);
7734 }
James Ketrenosa613bff2005-08-24 21:43:11 -05007735 spin_unlock_irqrestore(&priv->ieee->lock, flags);
Jeff Garzikbf794512005-07-31 13:07:26 -04007736
James Ketrenos43f66a62005-03-25 12:31:53 -06007737 /* If we reached the end of the list, then we don't have any valid
7738 * matching APs */
7739 if (!network) {
7740 ipw_debug_config(priv);
7741
James Ketrenosb095c382005-08-24 22:04:42 -05007742 if (!(priv->status & STATUS_SCANNING)) {
7743 if (!(priv->config & CFG_SPEED_SCAN))
7744 queue_delayed_work(priv->workqueue,
7745 &priv->request_scan,
7746 SCAN_INTERVAL);
7747 else
7748 queue_work(priv->workqueue,
7749 &priv->request_scan);
7750 }
Jeff Garzikbf794512005-07-31 13:07:26 -04007751
James Ketrenosc848d0a2005-08-24 21:56:24 -05007752 return 0;
James Ketrenos43f66a62005-03-25 12:31:53 -06007753 }
7754
7755 ipw_associate_network(priv, network, rates, 0);
James Ketrenosc848d0a2005-08-24 21:56:24 -05007756
7757 return 1;
7758}
7759
7760static void ipw_bg_associate(void *data)
7761{
7762 struct ipw_priv *priv = data;
7763 down(&priv->sem);
7764 ipw_associate(data);
7765 up(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06007766}
Jeff Garzikbf794512005-07-31 13:07:26 -04007767
James Ketrenosb095c382005-08-24 22:04:42 -05007768static void ipw_rebuild_decrypted_skb(struct ipw_priv *priv,
7769 struct sk_buff *skb)
7770{
7771 struct ieee80211_hdr *hdr;
7772 u16 fc;
7773
7774 hdr = (struct ieee80211_hdr *)skb->data;
7775 fc = le16_to_cpu(hdr->frame_ctl);
7776 if (!(fc & IEEE80211_FCTL_PROTECTED))
7777 return;
7778
7779 fc &= ~IEEE80211_FCTL_PROTECTED;
7780 hdr->frame_ctl = cpu_to_le16(fc);
7781 switch (priv->ieee->sec.level) {
7782 case SEC_LEVEL_3:
7783 /* Remove CCMP HDR */
7784 memmove(skb->data + IEEE80211_3ADDR_LEN,
7785 skb->data + IEEE80211_3ADDR_LEN + 8,
7786 skb->len - IEEE80211_3ADDR_LEN - 8);
Zhu Yia2d73e62005-07-13 12:24:51 -05007787 if (fc & IEEE80211_FCTL_MOREFRAGS)
7788 skb_trim(skb, skb->len - 16); /* 2*MIC */
7789 else
7790 skb_trim(skb, skb->len - 8); /* MIC */
James Ketrenosb095c382005-08-24 22:04:42 -05007791 break;
7792 case SEC_LEVEL_2:
7793 break;
7794 case SEC_LEVEL_1:
7795 /* Remove IV */
7796 memmove(skb->data + IEEE80211_3ADDR_LEN,
7797 skb->data + IEEE80211_3ADDR_LEN + 4,
7798 skb->len - IEEE80211_3ADDR_LEN - 4);
Zhu Yia2d73e62005-07-13 12:24:51 -05007799 if (fc & IEEE80211_FCTL_MOREFRAGS)
7800 skb_trim(skb, skb->len - 8); /* 2*ICV */
7801 else
7802 skb_trim(skb, skb->len - 4); /* ICV */
James Ketrenosb095c382005-08-24 22:04:42 -05007803 break;
7804 case SEC_LEVEL_0:
7805 break;
7806 default:
7807 printk(KERN_ERR "Unknow security level %d\n",
7808 priv->ieee->sec.level);
7809 break;
7810 }
7811}
7812
7813static void ipw_handle_data_packet(struct ipw_priv *priv,
7814 struct ipw_rx_mem_buffer *rxb,
7815 struct ieee80211_rx_stats *stats)
James Ketrenos43f66a62005-03-25 12:31:53 -06007816{
7817 struct ipw_rx_packet *pkt = (struct ipw_rx_packet *)rxb->skb->data;
7818
7819 /* We received data from the HW, so stop the watchdog */
7820 priv->net_dev->trans_start = jiffies;
7821
Jeff Garzikbf794512005-07-31 13:07:26 -04007822 /* We only process data packets if the
James Ketrenos43f66a62005-03-25 12:31:53 -06007823 * interface is open */
James Ketrenosa613bff2005-08-24 21:43:11 -05007824 if (unlikely((le16_to_cpu(pkt->u.frame.length) + IPW_RX_FRAME_SIZE) >
James Ketrenos43f66a62005-03-25 12:31:53 -06007825 skb_tailroom(rxb->skb))) {
7826 priv->ieee->stats.rx_errors++;
7827 priv->wstats.discard.misc++;
7828 IPW_DEBUG_DROP("Corruption detected! Oh no!\n");
7829 return;
7830 } else if (unlikely(!netif_running(priv->net_dev))) {
7831 priv->ieee->stats.rx_dropped++;
7832 priv->wstats.discard.misc++;
7833 IPW_DEBUG_DROP("Dropping packet while interface is not up.\n");
7834 return;
7835 }
7836
7837 /* Advance skb->data to the start of the actual payload */
Jiri Bencaaa4d302005-06-07 14:58:41 +02007838 skb_reserve(rxb->skb, offsetof(struct ipw_rx_packet, u.frame.data));
James Ketrenos43f66a62005-03-25 12:31:53 -06007839
7840 /* Set the size of the skb to the size of the frame */
James Ketrenosa613bff2005-08-24 21:43:11 -05007841 skb_put(rxb->skb, le16_to_cpu(pkt->u.frame.length));
James Ketrenos43f66a62005-03-25 12:31:53 -06007842
7843 IPW_DEBUG_RX("Rx packet of %d bytes.\n", rxb->skb->len);
7844
James Ketrenosb095c382005-08-24 22:04:42 -05007845 /* HW decrypt will not clear the WEP bit, MIC, PN, etc. */
7846 if (!priv->ieee->host_decrypt)
7847 ipw_rebuild_decrypted_skb(priv, rxb->skb);
7848
Jeff Garzikbf794512005-07-31 13:07:26 -04007849 if (!ieee80211_rx(priv->ieee, rxb->skb, stats))
James Ketrenos43f66a62005-03-25 12:31:53 -06007850 priv->ieee->stats.rx_errors++;
James Ketrenosa613bff2005-08-24 21:43:11 -05007851 else { /* ieee80211_rx succeeded, so it now owns the SKB */
James Ketrenos43f66a62005-03-25 12:31:53 -06007852 rxb->skb = NULL;
James Ketrenosb095c382005-08-24 22:04:42 -05007853 __ipw_led_activity_on(priv);
James Ketrenosa613bff2005-08-24 21:43:11 -05007854 }
James Ketrenos43f66a62005-03-25 12:31:53 -06007855}
7856
James Ketrenosea2b26e2005-08-24 21:25:16 -05007857static inline int is_network_packet(struct ipw_priv *priv,
7858 struct ieee80211_hdr_4addr *header)
7859{
7860 /* Filter incoming packets to determine if they are targetted toward
7861 * this network, discarding packets coming from ourselves */
7862 switch (priv->ieee->iw_mode) {
James Ketrenosa613bff2005-08-24 21:43:11 -05007863 case IW_MODE_ADHOC: /* Header: Dest. | Source | BSSID */
James Ketrenosc848d0a2005-08-24 21:56:24 -05007864 /* packets from our adapter are dropped (echo) */
7865 if (!memcmp(header->addr2, priv->net_dev->dev_addr, ETH_ALEN))
7866 return 0;
7867
James Ketrenosafbf30a2005-08-25 00:05:33 -05007868 /* multicast packets to our IBSS go through */
7869 if (is_multicast_ether_addr(header->addr1))
James Ketrenosea2b26e2005-08-24 21:25:16 -05007870 return !memcmp(header->addr3, priv->bssid, ETH_ALEN);
James Ketrenosa613bff2005-08-24 21:43:11 -05007871
7872 /* packets to our adapter go through */
7873 return !memcmp(header->addr1, priv->net_dev->dev_addr,
7874 ETH_ALEN);
James Ketrenosa613bff2005-08-24 21:43:11 -05007875
7876 case IW_MODE_INFRA: /* Header: Dest. | AP{BSSID} | Source */
James Ketrenosc848d0a2005-08-24 21:56:24 -05007877 /* packets from our adapter are dropped (echo) */
7878 if (!memcmp(header->addr3, priv->net_dev->dev_addr, ETH_ALEN))
7879 return 0;
7880
James Ketrenosa613bff2005-08-24 21:43:11 -05007881 /* {broad,multi}cast packets to our IBSS go through */
James Ketrenosafbf30a2005-08-25 00:05:33 -05007882 if (is_multicast_ether_addr(header->addr1))
James Ketrenosa613bff2005-08-24 21:43:11 -05007883 return !memcmp(header->addr2, priv->bssid, ETH_ALEN);
7884
7885 /* packets to our adapter go through */
7886 return !memcmp(header->addr1, priv->net_dev->dev_addr,
7887 ETH_ALEN);
James Ketrenosea2b26e2005-08-24 21:25:16 -05007888 }
James Ketrenosa613bff2005-08-24 21:43:11 -05007889
James Ketrenosea2b26e2005-08-24 21:25:16 -05007890 return 1;
7891}
7892
James Ketrenosafbf30a2005-08-25 00:05:33 -05007893#define IPW_PACKET_RETRY_TIME HZ
7894
7895static inline int is_duplicate_packet(struct ipw_priv *priv,
7896 struct ieee80211_hdr_4addr *header)
7897{
James Ketrenosafbf30a2005-08-25 00:05:33 -05007898 u16 sc = le16_to_cpu(header->seq_ctl);
7899 u16 seq = WLAN_GET_SEQ_SEQ(sc);
7900 u16 frag = WLAN_GET_SEQ_FRAG(sc);
7901 u16 *last_seq, *last_frag;
7902 unsigned long *last_time;
7903
7904 switch (priv->ieee->iw_mode) {
7905 case IW_MODE_ADHOC:
7906 {
7907 struct list_head *p;
7908 struct ipw_ibss_seq *entry = NULL;
7909 u8 *mac = header->addr2;
7910 int index = mac[5] % IPW_IBSS_MAC_HASH_SIZE;
7911
7912 __list_for_each(p, &priv->ibss_mac_hash[index]) {
7913 entry =
7914 list_entry(p, struct ipw_ibss_seq, list);
7915 if (!memcmp(entry->mac, mac, ETH_ALEN))
7916 break;
7917 }
7918 if (p == &priv->ibss_mac_hash[index]) {
7919 entry = kmalloc(sizeof(*entry), GFP_ATOMIC);
7920 if (!entry) {
7921 IPW_ERROR
7922 ("Cannot malloc new mac entry\n");
7923 return 0;
7924 }
7925 memcpy(entry->mac, mac, ETH_ALEN);
7926 entry->seq_num = seq;
7927 entry->frag_num = frag;
7928 entry->packet_time = jiffies;
7929 list_add(&entry->list,
7930 &priv->ibss_mac_hash[index]);
7931 return 0;
7932 }
7933 last_seq = &entry->seq_num;
7934 last_frag = &entry->frag_num;
7935 last_time = &entry->packet_time;
7936 break;
7937 }
7938 case IW_MODE_INFRA:
7939 last_seq = &priv->last_seq_num;
7940 last_frag = &priv->last_frag_num;
7941 last_time = &priv->last_packet_time;
7942 break;
7943 default:
7944 return 0;
7945 }
7946 if ((*last_seq == seq) &&
7947 time_after(*last_time + IPW_PACKET_RETRY_TIME, jiffies)) {
7948 if (*last_frag == frag)
7949 goto drop;
7950 if (*last_frag + 1 != frag)
7951 /* out-of-order fragment */
7952 goto drop;
James Ketrenosafbf30a2005-08-25 00:05:33 -05007953 } else
7954 *last_seq = seq;
7955
Zhu Yif57ce7c2005-07-13 12:22:15 -05007956 *last_frag = frag;
James Ketrenosafbf30a2005-08-25 00:05:33 -05007957 *last_time = jiffies;
7958 return 0;
7959
7960 drop:
Zhu Yi87b016c2005-08-05 17:17:35 +08007961 /* Comment this line now since we observed the card receives
7962 * duplicate packets but the FCTL_RETRY bit is not set in the
7963 * IBSS mode with fragmentation enabled.
7964 BUG_ON(!(le16_to_cpu(header->frame_ctl) & IEEE80211_FCTL_RETRY)); */
James Ketrenosafbf30a2005-08-25 00:05:33 -05007965 return 1;
7966}
7967
James Ketrenosb095c382005-08-24 22:04:42 -05007968static void ipw_handle_mgmt_packet(struct ipw_priv *priv,
7969 struct ipw_rx_mem_buffer *rxb,
7970 struct ieee80211_rx_stats *stats)
7971{
7972 struct sk_buff *skb = rxb->skb;
7973 struct ipw_rx_packet *pkt = (struct ipw_rx_packet *)skb->data;
7974 struct ieee80211_hdr_4addr *header = (struct ieee80211_hdr_4addr *)
7975 (skb->data + IPW_RX_FRAME_SIZE);
7976
7977 ieee80211_rx_mgt(priv->ieee, header, stats);
7978
7979 if (priv->ieee->iw_mode == IW_MODE_ADHOC &&
7980 ((WLAN_FC_GET_STYPE(le16_to_cpu(header->frame_ctl)) ==
7981 IEEE80211_STYPE_PROBE_RESP) ||
7982 (WLAN_FC_GET_STYPE(le16_to_cpu(header->frame_ctl)) ==
7983 IEEE80211_STYPE_BEACON))) {
7984 if (!memcmp(header->addr3, priv->bssid, ETH_ALEN))
7985 ipw_add_station(priv, header->addr2);
7986 }
7987
7988 if (priv->config & CFG_NET_STATS) {
7989 IPW_DEBUG_HC("sending stat packet\n");
7990
7991 /* Set the size of the skb to the size of the full
7992 * ipw header and 802.11 frame */
7993 skb_put(skb, le16_to_cpu(pkt->u.frame.length) +
7994 IPW_RX_FRAME_SIZE);
7995
7996 /* Advance past the ipw packet header to the 802.11 frame */
7997 skb_pull(skb, IPW_RX_FRAME_SIZE);
7998
7999 /* Push the ieee80211_rx_stats before the 802.11 frame */
8000 memcpy(skb_push(skb, sizeof(*stats)), stats, sizeof(*stats));
8001
8002 skb->dev = priv->ieee->dev;
8003
8004 /* Point raw at the ieee80211_stats */
8005 skb->mac.raw = skb->data;
8006
8007 skb->pkt_type = PACKET_OTHERHOST;
8008 skb->protocol = __constant_htons(ETH_P_80211_STATS);
8009 memset(skb->cb, 0, sizeof(rxb->skb->cb));
8010 netif_rx(skb);
8011 rxb->skb = NULL;
8012 }
8013}
8014
James Ketrenos43f66a62005-03-25 12:31:53 -06008015/*
8016 * Main entry function for recieving a packet with 80211 headers. This
8017 * should be called when ever the FW has notified us that there is a new
8018 * skb in the recieve queue.
8019 */
8020static void ipw_rx(struct ipw_priv *priv)
8021{
8022 struct ipw_rx_mem_buffer *rxb;
8023 struct ipw_rx_packet *pkt;
James Ketrenos0dacca12005-09-21 12:23:41 -05008024 struct ieee80211_hdr_4addr *header;
James Ketrenos43f66a62005-03-25 12:31:53 -06008025 u32 r, w, i;
8026 u8 network_packet;
8027
James Ketrenosb095c382005-08-24 22:04:42 -05008028 r = ipw_read32(priv, IPW_RX_READ_INDEX);
8029 w = ipw_read32(priv, IPW_RX_WRITE_INDEX);
James Ketrenos43f66a62005-03-25 12:31:53 -06008030 i = (priv->rxq->processed + 1) % RX_QUEUE_SIZE;
8031
8032 while (i != r) {
8033 rxb = priv->rxq->queue[i];
8034#ifdef CONFIG_IPW_DEBUG
8035 if (unlikely(rxb == NULL)) {
8036 printk(KERN_CRIT "Queue not allocated!\n");
8037 break;
8038 }
8039#endif
8040 priv->rxq->queue[i] = NULL;
8041
8042 pci_dma_sync_single_for_cpu(priv->pci_dev, rxb->dma_addr,
James Ketrenosb095c382005-08-24 22:04:42 -05008043 IPW_RX_BUF_SIZE,
James Ketrenos43f66a62005-03-25 12:31:53 -06008044 PCI_DMA_FROMDEVICE);
8045
8046 pkt = (struct ipw_rx_packet *)rxb->skb->data;
8047 IPW_DEBUG_RX("Packet: type=%02X seq=%02X bits=%02X\n",
8048 pkt->header.message_type,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04008049 pkt->header.rx_seq_num, pkt->header.control_bits);
James Ketrenos43f66a62005-03-25 12:31:53 -06008050
8051 switch (pkt->header.message_type) {
Jeff Garzik0edd5b42005-09-07 00:48:31 -04008052 case RX_FRAME_TYPE: /* 802.11 frame */ {
8053 struct ieee80211_rx_stats stats = {
James Ketrenosc848d0a2005-08-24 21:56:24 -05008054 .rssi =
8055 le16_to_cpu(pkt->u.frame.rssi_dbm) -
Jeff Garzik0edd5b42005-09-07 00:48:31 -04008056 IPW_RSSI_TO_DBM,
James Ketrenosc848d0a2005-08-24 21:56:24 -05008057 .signal =
8058 le16_to_cpu(pkt->u.frame.signal),
8059 .noise =
8060 le16_to_cpu(pkt->u.frame.noise),
Jeff Garzik0edd5b42005-09-07 00:48:31 -04008061 .rate = pkt->u.frame.rate,
8062 .mac_time = jiffies,
8063 .received_channel =
8064 pkt->u.frame.received_channel,
8065 .freq =
8066 (pkt->u.frame.
8067 control & (1 << 0)) ?
8068 IEEE80211_24GHZ_BAND :
8069 IEEE80211_52GHZ_BAND,
James Ketrenosa613bff2005-08-24 21:43:11 -05008070 .len = le16_to_cpu(pkt->u.frame.length),
Jeff Garzik0edd5b42005-09-07 00:48:31 -04008071 };
James Ketrenos43f66a62005-03-25 12:31:53 -06008072
Jeff Garzik0edd5b42005-09-07 00:48:31 -04008073 if (stats.rssi != 0)
8074 stats.mask |= IEEE80211_STATMASK_RSSI;
8075 if (stats.signal != 0)
8076 stats.mask |= IEEE80211_STATMASK_SIGNAL;
James Ketrenosc848d0a2005-08-24 21:56:24 -05008077 if (stats.noise != 0)
8078 stats.mask |= IEEE80211_STATMASK_NOISE;
Jeff Garzik0edd5b42005-09-07 00:48:31 -04008079 if (stats.rate != 0)
8080 stats.mask |= IEEE80211_STATMASK_RATE;
James Ketrenos43f66a62005-03-25 12:31:53 -06008081
Jeff Garzik0edd5b42005-09-07 00:48:31 -04008082 priv->rx_packets++;
James Ketrenos43f66a62005-03-25 12:31:53 -06008083
James Ketrenosb095c382005-08-24 22:04:42 -05008084#ifdef CONFIG_IPW2200_MONITOR
Jeff Garzik0edd5b42005-09-07 00:48:31 -04008085 if (priv->ieee->iw_mode == IW_MODE_MONITOR) {
8086 ipw_handle_data_packet(priv, rxb,
8087 &stats);
8088 break;
8089 }
James Ketrenos43f66a62005-03-25 12:31:53 -06008090#endif
Jeff Garzikbf794512005-07-31 13:07:26 -04008091
Jeff Garzik0edd5b42005-09-07 00:48:31 -04008092 header =
James Ketrenos0dacca12005-09-21 12:23:41 -05008093 (struct ieee80211_hdr_4addr *)(rxb->skb->
8094 data +
8095 IPW_RX_FRAME_SIZE);
James Ketrenos43f66a62005-03-25 12:31:53 -06008096 /* TODO: Check Ad-Hoc dest/source and make sure
8097 * that we are actually parsing these packets
Jeff Garzikbf794512005-07-31 13:07:26 -04008098 * correctly -- we should probably use the
James Ketrenos43f66a62005-03-25 12:31:53 -06008099 * frame control of the packet and disregard
8100 * the current iw_mode */
James Ketrenos43f66a62005-03-25 12:31:53 -06008101
James Ketrenosea2b26e2005-08-24 21:25:16 -05008102 network_packet =
8103 is_network_packet(priv, header);
Jeff Garzik0edd5b42005-09-07 00:48:31 -04008104 if (network_packet && priv->assoc_network) {
8105 priv->assoc_network->stats.rssi =
8106 stats.rssi;
8107 average_add(&priv->average_rssi,
8108 stats.rssi);
8109 priv->last_rx_rssi = stats.rssi;
8110 }
8111
8112 IPW_DEBUG_RX("Frame: len=%u\n",
James Ketrenosa613bff2005-08-24 21:43:11 -05008113 le16_to_cpu(pkt->u.frame.length));
Jeff Garzik0edd5b42005-09-07 00:48:31 -04008114
James Ketrenosa613bff2005-08-24 21:43:11 -05008115 if (le16_to_cpu(pkt->u.frame.length) <
8116 frame_hdr_len(header)) {
Jeff Garzik0edd5b42005-09-07 00:48:31 -04008117 IPW_DEBUG_DROP
8118 ("Received packet is too small. "
8119 "Dropping.\n");
8120 priv->ieee->stats.rx_errors++;
8121 priv->wstats.discard.misc++;
8122 break;
8123 }
8124
James Ketrenosa613bff2005-08-24 21:43:11 -05008125 switch (WLAN_FC_GET_TYPE
8126 (le16_to_cpu(header->frame_ctl))) {
James Ketrenosb095c382005-08-24 22:04:42 -05008127
Jeff Garzik0edd5b42005-09-07 00:48:31 -04008128 case IEEE80211_FTYPE_MGMT:
James Ketrenosb095c382005-08-24 22:04:42 -05008129 ipw_handle_mgmt_packet(priv, rxb,
8130 &stats);
Jeff Garzik0edd5b42005-09-07 00:48:31 -04008131 break;
8132
8133 case IEEE80211_FTYPE_CTL:
8134 break;
8135
8136 case IEEE80211_FTYPE_DATA:
James Ketrenosafbf30a2005-08-25 00:05:33 -05008137 if (unlikely(!network_packet ||
8138 is_duplicate_packet(priv,
8139 header)))
8140 {
Jeff Garzik0edd5b42005-09-07 00:48:31 -04008141 IPW_DEBUG_DROP("Dropping: "
8142 MAC_FMT ", "
8143 MAC_FMT ", "
8144 MAC_FMT "\n",
8145 MAC_ARG(header->
8146 addr1),
8147 MAC_ARG(header->
8148 addr2),
8149 MAC_ARG(header->
8150 addr3));
James Ketrenosb095c382005-08-24 22:04:42 -05008151 break;
8152 }
8153
8154 ipw_handle_data_packet(priv, rxb,
8155 &stats);
8156
Jeff Garzik0edd5b42005-09-07 00:48:31 -04008157 break;
8158 }
James Ketrenos43f66a62005-03-25 12:31:53 -06008159 break;
8160 }
Jeff Garzikbf794512005-07-31 13:07:26 -04008161
Jeff Garzik0edd5b42005-09-07 00:48:31 -04008162 case RX_HOST_NOTIFICATION_TYPE:{
8163 IPW_DEBUG_RX
8164 ("Notification: subtype=%02X flags=%02X size=%d\n",
James Ketrenos43f66a62005-03-25 12:31:53 -06008165 pkt->u.notification.subtype,
8166 pkt->u.notification.flags,
8167 pkt->u.notification.size);
Jeff Garzik0edd5b42005-09-07 00:48:31 -04008168 ipw_rx_notification(priv, &pkt->u.notification);
8169 break;
8170 }
James Ketrenos43f66a62005-03-25 12:31:53 -06008171
8172 default:
8173 IPW_DEBUG_RX("Bad Rx packet of type %d\n",
8174 pkt->header.message_type);
8175 break;
8176 }
Jeff Garzikbf794512005-07-31 13:07:26 -04008177
8178 /* For now we just don't re-use anything. We can tweak this
8179 * later to try and re-use notification packets and SKBs that
James Ketrenos43f66a62005-03-25 12:31:53 -06008180 * fail to Rx correctly */
8181 if (rxb->skb != NULL) {
8182 dev_kfree_skb_any(rxb->skb);
8183 rxb->skb = NULL;
8184 }
Jeff Garzikbf794512005-07-31 13:07:26 -04008185
James Ketrenos43f66a62005-03-25 12:31:53 -06008186 pci_unmap_single(priv->pci_dev, rxb->dma_addr,
James Ketrenosb095c382005-08-24 22:04:42 -05008187 IPW_RX_BUF_SIZE, PCI_DMA_FROMDEVICE);
James Ketrenos43f66a62005-03-25 12:31:53 -06008188 list_add_tail(&rxb->list, &priv->rxq->rx_used);
Jeff Garzikbf794512005-07-31 13:07:26 -04008189
James Ketrenos43f66a62005-03-25 12:31:53 -06008190 i = (i + 1) % RX_QUEUE_SIZE;
8191 }
8192
8193 /* Backtrack one entry */
8194 priv->rxq->processed = (i ? i : RX_QUEUE_SIZE) - 1;
8195
8196 ipw_rx_queue_restock(priv);
8197}
8198
James Ketrenosafbf30a2005-08-25 00:05:33 -05008199#define DEFAULT_RTS_THRESHOLD 2304U
8200#define MIN_RTS_THRESHOLD 1U
8201#define MAX_RTS_THRESHOLD 2304U
8202#define DEFAULT_BEACON_INTERVAL 100U
8203#define DEFAULT_SHORT_RETRY_LIMIT 7U
8204#define DEFAULT_LONG_RETRY_LIMIT 4U
8205
8206static int ipw_sw_reset(struct ipw_priv *priv, int init)
8207{
8208 int band, modulation;
8209 int old_mode = priv->ieee->iw_mode;
8210
8211 /* Initialize module parameter values here */
8212 priv->config = 0;
8213
8214 /* We default to disabling the LED code as right now it causes
8215 * too many systems to lock up... */
8216 if (!led)
8217 priv->config |= CFG_NO_LED;
8218
8219 if (associate)
8220 priv->config |= CFG_ASSOCIATE;
8221 else
8222 IPW_DEBUG_INFO("Auto associate disabled.\n");
8223
8224 if (auto_create)
8225 priv->config |= CFG_ADHOC_CREATE;
8226 else
8227 IPW_DEBUG_INFO("Auto adhoc creation disabled.\n");
8228
8229 if (disable) {
8230 priv->status |= STATUS_RF_KILL_SW;
8231 IPW_DEBUG_INFO("Radio disabled.\n");
8232 }
8233
8234 if (channel != 0) {
8235 priv->config |= CFG_STATIC_CHANNEL;
8236 priv->channel = channel;
8237 IPW_DEBUG_INFO("Bind to static channel %d\n", channel);
8238 /* TODO: Validate that provided channel is in range */
8239 }
8240#ifdef CONFIG_IPW_QOS
8241 ipw_qos_init(priv, qos_enable, qos_burst_enable,
8242 burst_duration_CCK, burst_duration_OFDM);
8243#endif /* CONFIG_IPW_QOS */
8244
8245 switch (mode) {
8246 case 1:
8247 priv->ieee->iw_mode = IW_MODE_ADHOC;
8248 priv->net_dev->type = ARPHRD_ETHER;
8249
8250 break;
8251#ifdef CONFIG_IPW2200_MONITOR
8252 case 2:
8253 priv->ieee->iw_mode = IW_MODE_MONITOR;
8254 priv->net_dev->type = ARPHRD_IEEE80211;
8255 break;
8256#endif
8257 default:
8258 case 0:
8259 priv->net_dev->type = ARPHRD_ETHER;
8260 priv->ieee->iw_mode = IW_MODE_INFRA;
8261 break;
8262 }
8263
8264 if (hwcrypto) {
8265 priv->ieee->host_encrypt = 0;
8266 priv->ieee->host_encrypt_msdu = 0;
8267 priv->ieee->host_decrypt = 0;
8268 }
8269 IPW_DEBUG_INFO("Hardware crypto [%s]\n", hwcrypto ? "on" : "off");
8270
Zhu Yie402c932005-08-05 17:20:40 +08008271 /* IPW2200/2915 is abled to do hardware fragmentation. */
8272 priv->ieee->host_open_frag = 0;
8273
James Ketrenosafbf30a2005-08-25 00:05:33 -05008274 if ((priv->pci_dev->device == 0x4223) ||
8275 (priv->pci_dev->device == 0x4224)) {
8276 if (init)
8277 printk(KERN_INFO DRV_NAME
8278 ": Detected Intel PRO/Wireless 2915ABG Network "
8279 "Connection\n");
8280 priv->ieee->abg_true = 1;
8281 band = IEEE80211_52GHZ_BAND | IEEE80211_24GHZ_BAND;
8282 modulation = IEEE80211_OFDM_MODULATION |
8283 IEEE80211_CCK_MODULATION;
8284 priv->adapter = IPW_2915ABG;
8285 priv->ieee->mode = IEEE_A | IEEE_G | IEEE_B;
8286 } else {
8287 if (init)
8288 printk(KERN_INFO DRV_NAME
8289 ": Detected Intel PRO/Wireless 2200BG Network "
8290 "Connection\n");
8291
8292 priv->ieee->abg_true = 0;
8293 band = IEEE80211_24GHZ_BAND;
8294 modulation = IEEE80211_OFDM_MODULATION |
8295 IEEE80211_CCK_MODULATION;
8296 priv->adapter = IPW_2200BG;
8297 priv->ieee->mode = IEEE_G | IEEE_B;
8298 }
8299
8300 priv->ieee->freq_band = band;
8301 priv->ieee->modulation = modulation;
8302
8303 priv->rates_mask = IEEE80211_DEFAULT_RATES_MASK;
8304
8305 priv->disassociate_threshold = IPW_MB_DISASSOCIATE_THRESHOLD_DEFAULT;
8306 priv->roaming_threshold = IPW_MB_ROAMING_THRESHOLD_DEFAULT;
8307
8308 priv->rts_threshold = DEFAULT_RTS_THRESHOLD;
8309 priv->short_retry_limit = DEFAULT_SHORT_RETRY_LIMIT;
8310 priv->long_retry_limit = DEFAULT_LONG_RETRY_LIMIT;
8311
8312 /* If power management is turned on, default to AC mode */
8313 priv->power_mode = IPW_POWER_AC;
8314 priv->tx_power = IPW_TX_POWER_DEFAULT;
8315
Zhu Yi0ece35b2005-08-05 17:26:51 +08008316 return old_mode == priv->ieee->iw_mode;
James Ketrenosafbf30a2005-08-25 00:05:33 -05008317}
8318
James Ketrenos43f66a62005-03-25 12:31:53 -06008319/*
8320 * This file defines the Wireless Extension handlers. It does not
8321 * define any methods of hardware manipulation and relies on the
8322 * functions defined in ipw_main to provide the HW interaction.
Jeff Garzikbf794512005-07-31 13:07:26 -04008323 *
8324 * The exception to this is the use of the ipw_get_ordinal()
James Ketrenos43f66a62005-03-25 12:31:53 -06008325 * function used to poll the hardware vs. making unecessary calls.
8326 *
8327 */
8328
Jeff Garzikbf794512005-07-31 13:07:26 -04008329static int ipw_wx_get_name(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);
James Ketrenosc848d0a2005-08-24 21:56:24 -05008334 down(&priv->sem);
8335 if (priv->status & STATUS_RF_KILL_MASK)
James Ketrenosa613bff2005-08-24 21:43:11 -05008336 strcpy(wrqu->name, "radio off");
James Ketrenosc848d0a2005-08-24 21:56:24 -05008337 else if (!(priv->status & STATUS_ASSOCIATED))
James Ketrenos43f66a62005-03-25 12:31:53 -06008338 strcpy(wrqu->name, "unassociated");
Jeff Garzikbf794512005-07-31 13:07:26 -04008339 else
James Ketrenos43f66a62005-03-25 12:31:53 -06008340 snprintf(wrqu->name, IFNAMSIZ, "IEEE 802.11%c",
8341 ipw_modes[priv->assoc_request.ieee_mode]);
8342 IPW_DEBUG_WX("Name: %s\n", wrqu->name);
James Ketrenosc848d0a2005-08-24 21:56:24 -05008343 up(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06008344 return 0;
8345}
8346
8347static int ipw_set_channel(struct ipw_priv *priv, u8 channel)
8348{
8349 if (channel == 0) {
8350 IPW_DEBUG_INFO("Setting channel to ANY (0)\n");
8351 priv->config &= ~CFG_STATIC_CHANNEL;
James Ketrenosc848d0a2005-08-24 21:56:24 -05008352 IPW_DEBUG_ASSOC("Attempting to associate with new "
8353 "parameters.\n");
8354 ipw_associate(priv);
James Ketrenos43f66a62005-03-25 12:31:53 -06008355 return 0;
8356 }
8357
8358 priv->config |= CFG_STATIC_CHANNEL;
8359
8360 if (priv->channel == channel) {
Jeff Garzik0edd5b42005-09-07 00:48:31 -04008361 IPW_DEBUG_INFO("Request to set channel to current value (%d)\n",
8362 channel);
James Ketrenos43f66a62005-03-25 12:31:53 -06008363 return 0;
8364 }
8365
8366 IPW_DEBUG_INFO("Setting channel to %i\n", (int)channel);
8367 priv->channel = channel;
8368
James Ketrenosb095c382005-08-24 22:04:42 -05008369#ifdef CONFIG_IPW2200_MONITOR
8370 if (priv->ieee->iw_mode == IW_MODE_MONITOR) {
James Ketrenosafbf30a2005-08-25 00:05:33 -05008371 int i;
James Ketrenosb095c382005-08-24 22:04:42 -05008372 if (priv->status & STATUS_SCANNING) {
James Ketrenosafbf30a2005-08-25 00:05:33 -05008373 IPW_DEBUG_SCAN("Scan abort triggered due to "
James Ketrenosb095c382005-08-24 22:04:42 -05008374 "channel change.\n");
James Ketrenosafbf30a2005-08-25 00:05:33 -05008375 ipw_abort_scan(priv);
James Ketrenosb095c382005-08-24 22:04:42 -05008376 }
8377
8378 for (i = 1000; i && (priv->status & STATUS_SCANNING); i--)
8379 udelay(10);
8380
8381 if (priv->status & STATUS_SCANNING)
8382 IPW_DEBUG_SCAN("Still scanning...\n");
8383 else
8384 IPW_DEBUG_SCAN("Took %dms to abort current scan\n",
8385 1000 - i);
8386
8387 return 0;
8388 }
8389#endif /* CONFIG_IPW2200_MONITOR */
8390
James Ketrenosc848d0a2005-08-24 21:56:24 -05008391 /* Network configuration changed -- force [re]association */
8392 IPW_DEBUG_ASSOC("[re]association triggered due to channel change.\n");
8393 if (!ipw_disassociate(priv))
James Ketrenos43f66a62005-03-25 12:31:53 -06008394 ipw_associate(priv);
James Ketrenos43f66a62005-03-25 12:31:53 -06008395
8396 return 0;
8397}
8398
Jeff Garzikbf794512005-07-31 13:07:26 -04008399static int ipw_wx_set_freq(struct net_device *dev,
8400 struct iw_request_info *info,
8401 union iwreq_data *wrqu, char *extra)
James Ketrenos43f66a62005-03-25 12:31:53 -06008402{
8403 struct ipw_priv *priv = ieee80211_priv(dev);
James Ketrenosafbf30a2005-08-25 00:05:33 -05008404 const struct ieee80211_geo *geo = ieee80211_get_geo(priv->ieee);
James Ketrenos43f66a62005-03-25 12:31:53 -06008405 struct iw_freq *fwrq = &wrqu->freq;
James Ketrenosafbf30a2005-08-25 00:05:33 -05008406 int ret = 0, i;
James Ketrenosb095c382005-08-24 22:04:42 -05008407 u8 channel;
8408
8409 if (fwrq->m == 0) {
8410 IPW_DEBUG_WX("SET Freq/Channel -> any\n");
8411 down(&priv->sem);
8412 ret = ipw_set_channel(priv, 0);
8413 up(&priv->sem);
8414 return ret;
8415 }
James Ketrenos43f66a62005-03-25 12:31:53 -06008416 /* if setting by freq convert to channel */
8417 if (fwrq->e == 1) {
James Ketrenosb095c382005-08-24 22:04:42 -05008418 channel = ieee80211_freq_to_channel(priv->ieee, fwrq->m);
8419 if (channel == 0)
8420 return -EINVAL;
8421 } else
8422 channel = fwrq->m;
Jeff Garzikbf794512005-07-31 13:07:26 -04008423
James Ketrenosb095c382005-08-24 22:04:42 -05008424 if (!ieee80211_is_valid_channel(priv->ieee, channel))
8425 return -EINVAL;
James Ketrenos43f66a62005-03-25 12:31:53 -06008426
James Ketrenosafbf30a2005-08-25 00:05:33 -05008427 if (priv->ieee->iw_mode == IW_MODE_ADHOC && priv->ieee->mode & IEEE_A) {
8428 i = ieee80211_channel_to_index(priv->ieee, channel);
8429 if (i == -1)
8430 return -EINVAL;
8431 if (geo->a[i].flags & IEEE80211_CH_PASSIVE_ONLY) {
8432 IPW_DEBUG_WX("Invalid Ad-Hoc channel for 802.11a\n");
8433 return -EINVAL;
8434 }
8435 }
8436
James Ketrenos43f66a62005-03-25 12:31:53 -06008437 IPW_DEBUG_WX("SET Freq/Channel -> %d \n", fwrq->m);
James Ketrenosc848d0a2005-08-24 21:56:24 -05008438 down(&priv->sem);
James Ketrenosb095c382005-08-24 22:04:42 -05008439 ret = ipw_set_channel(priv, channel);
James Ketrenosc848d0a2005-08-24 21:56:24 -05008440 up(&priv->sem);
8441 return ret;
James Ketrenos43f66a62005-03-25 12:31:53 -06008442}
8443
Jeff Garzikbf794512005-07-31 13:07:26 -04008444static int ipw_wx_get_freq(struct net_device *dev,
8445 struct iw_request_info *info,
James Ketrenos43f66a62005-03-25 12:31:53 -06008446 union iwreq_data *wrqu, char *extra)
8447{
8448 struct ipw_priv *priv = ieee80211_priv(dev);
8449
8450 wrqu->freq.e = 0;
8451
8452 /* If we are associated, trying to associate, or have a statically
8453 * configured CHANNEL then return that; otherwise return ANY */
James Ketrenosc848d0a2005-08-24 21:56:24 -05008454 down(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06008455 if (priv->config & CFG_STATIC_CHANNEL ||
8456 priv->status & (STATUS_ASSOCIATING | STATUS_ASSOCIATED))
8457 wrqu->freq.m = priv->channel;
Jeff Garzikbf794512005-07-31 13:07:26 -04008458 else
James Ketrenos43f66a62005-03-25 12:31:53 -06008459 wrqu->freq.m = 0;
8460
James Ketrenosc848d0a2005-08-24 21:56:24 -05008461 up(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06008462 IPW_DEBUG_WX("GET Freq/Channel -> %d \n", priv->channel);
8463 return 0;
8464}
8465
Jeff Garzikbf794512005-07-31 13:07:26 -04008466static int ipw_wx_set_mode(struct net_device *dev,
8467 struct iw_request_info *info,
James Ketrenos43f66a62005-03-25 12:31:53 -06008468 union iwreq_data *wrqu, char *extra)
8469{
8470 struct ipw_priv *priv = ieee80211_priv(dev);
8471 int err = 0;
8472
8473 IPW_DEBUG_WX("Set MODE: %d\n", wrqu->mode);
James Ketrenos43f66a62005-03-25 12:31:53 -06008474
8475 switch (wrqu->mode) {
James Ketrenosb095c382005-08-24 22:04:42 -05008476#ifdef CONFIG_IPW2200_MONITOR
James Ketrenos43f66a62005-03-25 12:31:53 -06008477 case IW_MODE_MONITOR:
8478#endif
8479 case IW_MODE_ADHOC:
8480 case IW_MODE_INFRA:
8481 break;
8482 case IW_MODE_AUTO:
8483 wrqu->mode = IW_MODE_INFRA;
8484 break;
8485 default:
8486 return -EINVAL;
8487 }
James Ketrenosb095c382005-08-24 22:04:42 -05008488 if (wrqu->mode == priv->ieee->iw_mode)
8489 return 0;
James Ketrenos43f66a62005-03-25 12:31:53 -06008490
James Ketrenosb095c382005-08-24 22:04:42 -05008491 down(&priv->sem);
James Ketrenosafbf30a2005-08-25 00:05:33 -05008492
8493 ipw_sw_reset(priv, 0);
8494
James Ketrenosb095c382005-08-24 22:04:42 -05008495#ifdef CONFIG_IPW2200_MONITOR
Jeff Garzikbf794512005-07-31 13:07:26 -04008496 if (priv->ieee->iw_mode == IW_MODE_MONITOR)
James Ketrenos43f66a62005-03-25 12:31:53 -06008497 priv->net_dev->type = ARPHRD_ETHER;
Jeff Garzikbf794512005-07-31 13:07:26 -04008498
8499 if (wrqu->mode == IW_MODE_MONITOR)
James Ketrenos43f66a62005-03-25 12:31:53 -06008500 priv->net_dev->type = ARPHRD_IEEE80211;
James Ketrenosb095c382005-08-24 22:04:42 -05008501#endif /* CONFIG_IPW2200_MONITOR */
Jeff Garzikbf794512005-07-31 13:07:26 -04008502
Jeff Garzikbf794512005-07-31 13:07:26 -04008503 /* Free the existing firmware and reset the fw_loaded
James Ketrenos43f66a62005-03-25 12:31:53 -06008504 * flag so ipw_load() will bring in the new firmawre */
James Ketrenosafbf30a2005-08-25 00:05:33 -05008505 free_firmware();
James Ketrenos43f66a62005-03-25 12:31:53 -06008506
8507 priv->ieee->iw_mode = wrqu->mode;
Jeff Garzikbf794512005-07-31 13:07:26 -04008508
James Ketrenosc848d0a2005-08-24 21:56:24 -05008509 queue_work(priv->workqueue, &priv->adapter_restart);
8510 up(&priv->sem);
Jeff Garzik0edd5b42005-09-07 00:48:31 -04008511 return err;
James Ketrenos43f66a62005-03-25 12:31:53 -06008512}
8513
Jeff Garzikbf794512005-07-31 13:07:26 -04008514static int ipw_wx_get_mode(struct net_device *dev,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04008515 struct iw_request_info *info,
8516 union iwreq_data *wrqu, char *extra)
James Ketrenos43f66a62005-03-25 12:31:53 -06008517{
8518 struct ipw_priv *priv = ieee80211_priv(dev);
James Ketrenosc848d0a2005-08-24 21:56:24 -05008519 down(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06008520 wrqu->mode = priv->ieee->iw_mode;
8521 IPW_DEBUG_WX("Get MODE -> %d\n", wrqu->mode);
James Ketrenosc848d0a2005-08-24 21:56:24 -05008522 up(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06008523 return 0;
8524}
8525
James Ketrenos43f66a62005-03-25 12:31:53 -06008526/* Values are in microsecond */
8527static const s32 timeout_duration[] = {
8528 350000,
8529 250000,
8530 75000,
8531 37000,
8532 25000,
8533};
8534
8535static const s32 period_duration[] = {
8536 400000,
8537 700000,
8538 1000000,
8539 1000000,
8540 1000000
8541};
8542
Jeff Garzikbf794512005-07-31 13:07:26 -04008543static int ipw_wx_get_range(struct net_device *dev,
8544 struct iw_request_info *info,
James Ketrenos43f66a62005-03-25 12:31:53 -06008545 union iwreq_data *wrqu, char *extra)
8546{
8547 struct ipw_priv *priv = ieee80211_priv(dev);
8548 struct iw_range *range = (struct iw_range *)extra;
James Ketrenosb095c382005-08-24 22:04:42 -05008549 const struct ieee80211_geo *geo = ieee80211_get_geo(priv->ieee);
8550 int i = 0, j;
James Ketrenos43f66a62005-03-25 12:31:53 -06008551
8552 wrqu->data.length = sizeof(*range);
8553 memset(range, 0, sizeof(*range));
8554
8555 /* 54Mbs == ~27 Mb/s real (802.11g) */
Jeff Garzikbf794512005-07-31 13:07:26 -04008556 range->throughput = 27 * 1000 * 1000;
James Ketrenos43f66a62005-03-25 12:31:53 -06008557
8558 range->max_qual.qual = 100;
8559 /* TODO: Find real max RSSI and stick here */
8560 range->max_qual.level = 0;
James Ketrenosc848d0a2005-08-24 21:56:24 -05008561 range->max_qual.noise = priv->ieee->worst_rssi + 0x100;
Jeff Garzik0edd5b42005-09-07 00:48:31 -04008562 range->max_qual.updated = 7; /* Updated all three */
James Ketrenos43f66a62005-03-25 12:31:53 -06008563
8564 range->avg_qual.qual = 70;
8565 /* TODO: Find real 'good' to 'bad' threshol value for RSSI */
Jeff Garzik0edd5b42005-09-07 00:48:31 -04008566 range->avg_qual.level = 0; /* FIXME to real average level */
James Ketrenos43f66a62005-03-25 12:31:53 -06008567 range->avg_qual.noise = 0;
Jeff Garzik0edd5b42005-09-07 00:48:31 -04008568 range->avg_qual.updated = 7; /* Updated all three */
James Ketrenosc848d0a2005-08-24 21:56:24 -05008569 down(&priv->sem);
Jeff Garzik0edd5b42005-09-07 00:48:31 -04008570 range->num_bitrates = min(priv->rates.num_rates, (u8) IW_MAX_BITRATES);
James Ketrenos43f66a62005-03-25 12:31:53 -06008571
Jeff Garzikbf794512005-07-31 13:07:26 -04008572 for (i = 0; i < range->num_bitrates; i++)
8573 range->bitrate[i] = (priv->rates.supported_rates[i] & 0x7F) *
Jeff Garzik0edd5b42005-09-07 00:48:31 -04008574 500000;
Jeff Garzikbf794512005-07-31 13:07:26 -04008575
James Ketrenos43f66a62005-03-25 12:31:53 -06008576 range->max_rts = DEFAULT_RTS_THRESHOLD;
8577 range->min_frag = MIN_FRAG_THRESHOLD;
8578 range->max_frag = MAX_FRAG_THRESHOLD;
8579
8580 range->encoding_size[0] = 5;
Jeff Garzikbf794512005-07-31 13:07:26 -04008581 range->encoding_size[1] = 13;
James Ketrenos43f66a62005-03-25 12:31:53 -06008582 range->num_encoding_sizes = 2;
8583 range->max_encoding_tokens = WEP_KEYS;
8584
8585 /* Set the Wireless Extension versions */
8586 range->we_version_compiled = WIRELESS_EXT;
8587 range->we_version_source = 16;
8588
James Ketrenosb095c382005-08-24 22:04:42 -05008589 i = 0;
8590 if (priv->ieee->mode & (IEEE_B | IEEE_G)) {
8591 for (j = 0; j < geo->bg_channels && i < IW_MAX_FREQUENCIES;
8592 i++, j++) {
8593 range->freq[i].i = geo->bg[j].channel;
8594 range->freq[i].m = geo->bg[j].freq * 100000;
8595 range->freq[i].e = 1;
8596 }
James Ketrenos43f66a62005-03-25 12:31:53 -06008597 }
James Ketrenosb095c382005-08-24 22:04:42 -05008598
8599 if (priv->ieee->mode & IEEE_A) {
8600 for (j = 0; j < geo->a_channels && i < IW_MAX_FREQUENCIES;
8601 i++, j++) {
8602 range->freq[i].i = geo->a[j].channel;
8603 range->freq[i].m = geo->a[j].freq * 100000;
8604 range->freq[i].e = 1;
8605 }
8606 }
8607
8608 range->num_channels = i;
8609 range->num_frequency = i;
8610
James Ketrenosc848d0a2005-08-24 21:56:24 -05008611 up(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06008612 IPW_DEBUG_WX("GET Range\n");
8613 return 0;
8614}
8615
Jeff Garzikbf794512005-07-31 13:07:26 -04008616static int ipw_wx_set_wap(struct net_device *dev,
8617 struct iw_request_info *info,
James Ketrenos43f66a62005-03-25 12:31:53 -06008618 union iwreq_data *wrqu, char *extra)
8619{
8620 struct ipw_priv *priv = ieee80211_priv(dev);
8621
8622 static const unsigned char any[] = {
8623 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
8624 };
8625 static const unsigned char off[] = {
8626 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
8627 };
8628
Jeff Garzikbf794512005-07-31 13:07:26 -04008629 if (wrqu->ap_addr.sa_family != ARPHRD_ETHER)
James Ketrenos43f66a62005-03-25 12:31:53 -06008630 return -EINVAL;
James Ketrenosc848d0a2005-08-24 21:56:24 -05008631 down(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06008632 if (!memcmp(any, wrqu->ap_addr.sa_data, ETH_ALEN) ||
8633 !memcmp(off, wrqu->ap_addr.sa_data, ETH_ALEN)) {
8634 /* we disable mandatory BSSID association */
8635 IPW_DEBUG_WX("Setting AP BSSID to ANY\n");
8636 priv->config &= ~CFG_STATIC_BSSID;
James Ketrenosc848d0a2005-08-24 21:56:24 -05008637 IPW_DEBUG_ASSOC("Attempting to associate with new "
8638 "parameters.\n");
8639 ipw_associate(priv);
8640 up(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06008641 return 0;
8642 }
8643
8644 priv->config |= CFG_STATIC_BSSID;
8645 if (!memcmp(priv->bssid, wrqu->ap_addr.sa_data, ETH_ALEN)) {
8646 IPW_DEBUG_WX("BSSID set to current BSSID.\n");
James Ketrenosc848d0a2005-08-24 21:56:24 -05008647 up(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06008648 return 0;
8649 }
8650
8651 IPW_DEBUG_WX("Setting mandatory BSSID to " MAC_FMT "\n",
8652 MAC_ARG(wrqu->ap_addr.sa_data));
8653
8654 memcpy(priv->bssid, wrqu->ap_addr.sa_data, ETH_ALEN);
8655
James Ketrenosc848d0a2005-08-24 21:56:24 -05008656 /* Network configuration changed -- force [re]association */
8657 IPW_DEBUG_ASSOC("[re]association triggered due to BSSID change.\n");
8658 if (!ipw_disassociate(priv))
James Ketrenos43f66a62005-03-25 12:31:53 -06008659 ipw_associate(priv);
James Ketrenos43f66a62005-03-25 12:31:53 -06008660
James Ketrenosc848d0a2005-08-24 21:56:24 -05008661 up(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06008662 return 0;
8663}
8664
Jeff Garzikbf794512005-07-31 13:07:26 -04008665static int ipw_wx_get_wap(struct net_device *dev,
8666 struct iw_request_info *info,
James Ketrenos43f66a62005-03-25 12:31:53 -06008667 union iwreq_data *wrqu, char *extra)
8668{
8669 struct ipw_priv *priv = ieee80211_priv(dev);
8670 /* If we are associated, trying to associate, or have a statically
8671 * configured BSSID then return that; otherwise return ANY */
James Ketrenosc848d0a2005-08-24 21:56:24 -05008672 down(&priv->sem);
Jeff Garzikbf794512005-07-31 13:07:26 -04008673 if (priv->config & CFG_STATIC_BSSID ||
James Ketrenos43f66a62005-03-25 12:31:53 -06008674 priv->status & (STATUS_ASSOCIATED | STATUS_ASSOCIATING)) {
8675 wrqu->ap_addr.sa_family = ARPHRD_ETHER;
James Ketrenosafbf30a2005-08-25 00:05:33 -05008676 memcpy(wrqu->ap_addr.sa_data, priv->bssid, ETH_ALEN);
James Ketrenos43f66a62005-03-25 12:31:53 -06008677 } else
8678 memset(wrqu->ap_addr.sa_data, 0, ETH_ALEN);
8679
8680 IPW_DEBUG_WX("Getting WAP BSSID: " MAC_FMT "\n",
8681 MAC_ARG(wrqu->ap_addr.sa_data));
James Ketrenosc848d0a2005-08-24 21:56:24 -05008682 up(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06008683 return 0;
8684}
8685
Jeff Garzikbf794512005-07-31 13:07:26 -04008686static int ipw_wx_set_essid(struct net_device *dev,
8687 struct iw_request_info *info,
James Ketrenos43f66a62005-03-25 12:31:53 -06008688 union iwreq_data *wrqu, char *extra)
8689{
8690 struct ipw_priv *priv = ieee80211_priv(dev);
Jeff Garzik0edd5b42005-09-07 00:48:31 -04008691 char *essid = ""; /* ANY */
James Ketrenos43f66a62005-03-25 12:31:53 -06008692 int length = 0;
James Ketrenosc848d0a2005-08-24 21:56:24 -05008693 down(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06008694 if (wrqu->essid.flags && wrqu->essid.length) {
8695 length = wrqu->essid.length - 1;
8696 essid = extra;
8697 }
8698 if (length == 0) {
8699 IPW_DEBUG_WX("Setting ESSID to ANY\n");
James Ketrenosafbf30a2005-08-25 00:05:33 -05008700 if ((priv->config & CFG_STATIC_ESSID) &&
8701 !(priv->status & (STATUS_ASSOCIATED |
8702 STATUS_ASSOCIATING))) {
James Ketrenos43f66a62005-03-25 12:31:53 -06008703 IPW_DEBUG_ASSOC("Attempting to associate with new "
8704 "parameters.\n");
James Ketrenosafbf30a2005-08-25 00:05:33 -05008705 priv->config &= ~CFG_STATIC_ESSID;
James Ketrenos43f66a62005-03-25 12:31:53 -06008706 ipw_associate(priv);
8707 }
James Ketrenosc848d0a2005-08-24 21:56:24 -05008708 up(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06008709 return 0;
8710 }
8711
8712 length = min(length, IW_ESSID_MAX_SIZE);
8713
8714 priv->config |= CFG_STATIC_ESSID;
8715
8716 if (priv->essid_len == length && !memcmp(priv->essid, extra, length)) {
8717 IPW_DEBUG_WX("ESSID set to current ESSID.\n");
James Ketrenosc848d0a2005-08-24 21:56:24 -05008718 up(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06008719 return 0;
8720 }
8721
8722 IPW_DEBUG_WX("Setting ESSID: '%s' (%d)\n", escape_essid(essid, length),
8723 length);
8724
8725 priv->essid_len = length;
8726 memcpy(priv->essid, essid, priv->essid_len);
Jeff Garzikbf794512005-07-31 13:07:26 -04008727
James Ketrenosc848d0a2005-08-24 21:56:24 -05008728 /* Network configuration changed -- force [re]association */
8729 IPW_DEBUG_ASSOC("[re]association triggered due to ESSID change.\n");
8730 if (!ipw_disassociate(priv))
James Ketrenos43f66a62005-03-25 12:31:53 -06008731 ipw_associate(priv);
James Ketrenos43f66a62005-03-25 12:31:53 -06008732
James Ketrenosc848d0a2005-08-24 21:56:24 -05008733 up(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06008734 return 0;
8735}
8736
Jeff Garzikbf794512005-07-31 13:07:26 -04008737static int ipw_wx_get_essid(struct net_device *dev,
8738 struct iw_request_info *info,
James Ketrenos43f66a62005-03-25 12:31:53 -06008739 union iwreq_data *wrqu, char *extra)
8740{
8741 struct ipw_priv *priv = ieee80211_priv(dev);
8742
8743 /* If we are associated, trying to associate, or have a statically
8744 * configured ESSID then return that; otherwise return ANY */
James Ketrenosc848d0a2005-08-24 21:56:24 -05008745 down(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06008746 if (priv->config & CFG_STATIC_ESSID ||
Jeff Garzikbf794512005-07-31 13:07:26 -04008747 priv->status & (STATUS_ASSOCIATED | STATUS_ASSOCIATING)) {
8748 IPW_DEBUG_WX("Getting essid: '%s'\n",
James Ketrenos43f66a62005-03-25 12:31:53 -06008749 escape_essid(priv->essid, priv->essid_len));
Jeff Garzikbf794512005-07-31 13:07:26 -04008750 memcpy(extra, priv->essid, priv->essid_len);
James Ketrenos43f66a62005-03-25 12:31:53 -06008751 wrqu->essid.length = priv->essid_len;
Jeff Garzik0edd5b42005-09-07 00:48:31 -04008752 wrqu->essid.flags = 1; /* active */
James Ketrenos43f66a62005-03-25 12:31:53 -06008753 } else {
8754 IPW_DEBUG_WX("Getting essid: ANY\n");
8755 wrqu->essid.length = 0;
Jeff Garzik0edd5b42005-09-07 00:48:31 -04008756 wrqu->essid.flags = 0; /* active */
James Ketrenos43f66a62005-03-25 12:31:53 -06008757 }
James Ketrenosc848d0a2005-08-24 21:56:24 -05008758 up(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06008759 return 0;
8760}
8761
Jeff Garzikbf794512005-07-31 13:07:26 -04008762static int ipw_wx_set_nick(struct net_device *dev,
8763 struct iw_request_info *info,
James Ketrenos43f66a62005-03-25 12:31:53 -06008764 union iwreq_data *wrqu, char *extra)
Jeff Garzikbf794512005-07-31 13:07:26 -04008765{
James Ketrenos43f66a62005-03-25 12:31:53 -06008766 struct ipw_priv *priv = ieee80211_priv(dev);
8767
8768 IPW_DEBUG_WX("Setting nick to '%s'\n", extra);
8769 if (wrqu->data.length > IW_ESSID_MAX_SIZE)
8770 return -E2BIG;
James Ketrenosc848d0a2005-08-24 21:56:24 -05008771 down(&priv->sem);
Jeff Garzik0edd5b42005-09-07 00:48:31 -04008772 wrqu->data.length = min((size_t) wrqu->data.length, sizeof(priv->nick));
James Ketrenos43f66a62005-03-25 12:31:53 -06008773 memset(priv->nick, 0, sizeof(priv->nick));
Jeff Garzik0edd5b42005-09-07 00:48:31 -04008774 memcpy(priv->nick, extra, wrqu->data.length);
James Ketrenos43f66a62005-03-25 12:31:53 -06008775 IPW_DEBUG_TRACE("<<\n");
James Ketrenosc848d0a2005-08-24 21:56:24 -05008776 up(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06008777 return 0;
8778
8779}
8780
Jeff Garzikbf794512005-07-31 13:07:26 -04008781static int ipw_wx_get_nick(struct net_device *dev,
8782 struct iw_request_info *info,
James Ketrenos43f66a62005-03-25 12:31:53 -06008783 union iwreq_data *wrqu, char *extra)
Jeff Garzikbf794512005-07-31 13:07:26 -04008784{
James Ketrenos43f66a62005-03-25 12:31:53 -06008785 struct ipw_priv *priv = ieee80211_priv(dev);
8786 IPW_DEBUG_WX("Getting nick\n");
James Ketrenosc848d0a2005-08-24 21:56:24 -05008787 down(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06008788 wrqu->data.length = strlen(priv->nick) + 1;
8789 memcpy(extra, priv->nick, wrqu->data.length);
Jeff Garzik0edd5b42005-09-07 00:48:31 -04008790 wrqu->data.flags = 1; /* active */
James Ketrenosc848d0a2005-08-24 21:56:24 -05008791 up(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06008792 return 0;
8793}
8794
James Ketrenos43f66a62005-03-25 12:31:53 -06008795static int ipw_wx_set_rate(struct net_device *dev,
8796 struct iw_request_info *info,
8797 union iwreq_data *wrqu, char *extra)
Jeff Garzikbf794512005-07-31 13:07:26 -04008798{
James Ketrenosea2b26e2005-08-24 21:25:16 -05008799 /* TODO: We should use semaphores or locks for access to priv */
8800 struct ipw_priv *priv = ieee80211_priv(dev);
8801 u32 target_rate = wrqu->bitrate.value;
8802 u32 fixed, mask;
8803
8804 /* value = -1, fixed = 0 means auto only, so we should use all rates offered by AP */
8805 /* value = X, fixed = 1 means only rate X */
8806 /* value = X, fixed = 0 means all rates lower equal X */
8807
8808 if (target_rate == -1) {
8809 fixed = 0;
8810 mask = IEEE80211_DEFAULT_RATES_MASK;
8811 /* Now we should reassociate */
8812 goto apply;
8813 }
8814
8815 mask = 0;
8816 fixed = wrqu->bitrate.fixed;
8817
8818 if (target_rate == 1000000 || !fixed)
8819 mask |= IEEE80211_CCK_RATE_1MB_MASK;
8820 if (target_rate == 1000000)
8821 goto apply;
8822
8823 if (target_rate == 2000000 || !fixed)
8824 mask |= IEEE80211_CCK_RATE_2MB_MASK;
8825 if (target_rate == 2000000)
8826 goto apply;
8827
8828 if (target_rate == 5500000 || !fixed)
8829 mask |= IEEE80211_CCK_RATE_5MB_MASK;
8830 if (target_rate == 5500000)
8831 goto apply;
8832
8833 if (target_rate == 6000000 || !fixed)
8834 mask |= IEEE80211_OFDM_RATE_6MB_MASK;
8835 if (target_rate == 6000000)
8836 goto apply;
8837
8838 if (target_rate == 9000000 || !fixed)
8839 mask |= IEEE80211_OFDM_RATE_9MB_MASK;
8840 if (target_rate == 9000000)
8841 goto apply;
8842
8843 if (target_rate == 11000000 || !fixed)
8844 mask |= IEEE80211_CCK_RATE_11MB_MASK;
8845 if (target_rate == 11000000)
8846 goto apply;
8847
8848 if (target_rate == 12000000 || !fixed)
8849 mask |= IEEE80211_OFDM_RATE_12MB_MASK;
8850 if (target_rate == 12000000)
8851 goto apply;
8852
8853 if (target_rate == 18000000 || !fixed)
8854 mask |= IEEE80211_OFDM_RATE_18MB_MASK;
8855 if (target_rate == 18000000)
8856 goto apply;
8857
8858 if (target_rate == 24000000 || !fixed)
8859 mask |= IEEE80211_OFDM_RATE_24MB_MASK;
8860 if (target_rate == 24000000)
8861 goto apply;
8862
8863 if (target_rate == 36000000 || !fixed)
8864 mask |= IEEE80211_OFDM_RATE_36MB_MASK;
8865 if (target_rate == 36000000)
8866 goto apply;
8867
8868 if (target_rate == 48000000 || !fixed)
8869 mask |= IEEE80211_OFDM_RATE_48MB_MASK;
8870 if (target_rate == 48000000)
8871 goto apply;
8872
8873 if (target_rate == 54000000 || !fixed)
8874 mask |= IEEE80211_OFDM_RATE_54MB_MASK;
8875 if (target_rate == 54000000)
8876 goto apply;
8877
8878 IPW_DEBUG_WX("invalid rate specified, returning error\n");
8879 return -EINVAL;
8880
8881 apply:
8882 IPW_DEBUG_WX("Setting rate mask to 0x%08X [%s]\n",
8883 mask, fixed ? "fixed" : "sub-rates");
James Ketrenosc848d0a2005-08-24 21:56:24 -05008884 down(&priv->sem);
James Ketrenosb095c382005-08-24 22:04:42 -05008885 if (mask == IEEE80211_DEFAULT_RATES_MASK) {
James Ketrenosea2b26e2005-08-24 21:25:16 -05008886 priv->config &= ~CFG_FIXED_RATE;
James Ketrenosb095c382005-08-24 22:04:42 -05008887 ipw_set_fixed_rate(priv, priv->ieee->mode);
8888 } else
James Ketrenosea2b26e2005-08-24 21:25:16 -05008889 priv->config |= CFG_FIXED_RATE;
8890
James Ketrenosc848d0a2005-08-24 21:56:24 -05008891 if (priv->rates_mask == mask) {
8892 IPW_DEBUG_WX("Mask set to current mask.\n");
8893 up(&priv->sem);
8894 return 0;
James Ketrenosea2b26e2005-08-24 21:25:16 -05008895 }
8896
James Ketrenosc848d0a2005-08-24 21:56:24 -05008897 priv->rates_mask = mask;
8898
8899 /* Network configuration changed -- force [re]association */
8900 IPW_DEBUG_ASSOC("[re]association triggered due to rates change.\n");
8901 if (!ipw_disassociate(priv))
8902 ipw_associate(priv);
8903
8904 up(&priv->sem);
James Ketrenosea2b26e2005-08-24 21:25:16 -05008905 return 0;
James Ketrenos43f66a62005-03-25 12:31:53 -06008906}
8907
Jeff Garzikbf794512005-07-31 13:07:26 -04008908static int ipw_wx_get_rate(struct net_device *dev,
8909 struct iw_request_info *info,
James Ketrenos43f66a62005-03-25 12:31:53 -06008910 union iwreq_data *wrqu, char *extra)
Jeff Garzikbf794512005-07-31 13:07:26 -04008911{
Jeff Garzik0edd5b42005-09-07 00:48:31 -04008912 struct ipw_priv *priv = ieee80211_priv(dev);
James Ketrenosc848d0a2005-08-24 21:56:24 -05008913 down(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06008914 wrqu->bitrate.value = priv->last_rate;
James Ketrenosc848d0a2005-08-24 21:56:24 -05008915 up(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06008916 IPW_DEBUG_WX("GET Rate -> %d \n", wrqu->bitrate.value);
8917 return 0;
8918}
8919
Jeff Garzikbf794512005-07-31 13:07:26 -04008920static int ipw_wx_set_rts(struct net_device *dev,
8921 struct iw_request_info *info,
James Ketrenos43f66a62005-03-25 12:31:53 -06008922 union iwreq_data *wrqu, char *extra)
Jeff Garzikbf794512005-07-31 13:07:26 -04008923{
James Ketrenos43f66a62005-03-25 12:31:53 -06008924 struct ipw_priv *priv = ieee80211_priv(dev);
James Ketrenosc848d0a2005-08-24 21:56:24 -05008925 down(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06008926 if (wrqu->rts.disabled)
8927 priv->rts_threshold = DEFAULT_RTS_THRESHOLD;
8928 else {
8929 if (wrqu->rts.value < MIN_RTS_THRESHOLD ||
James Ketrenosc848d0a2005-08-24 21:56:24 -05008930 wrqu->rts.value > MAX_RTS_THRESHOLD) {
8931 up(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06008932 return -EINVAL;
James Ketrenosc848d0a2005-08-24 21:56:24 -05008933 }
James Ketrenos43f66a62005-03-25 12:31:53 -06008934 priv->rts_threshold = wrqu->rts.value;
8935 }
8936
8937 ipw_send_rts_threshold(priv, priv->rts_threshold);
James Ketrenosc848d0a2005-08-24 21:56:24 -05008938 up(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06008939 IPW_DEBUG_WX("SET RTS Threshold -> %d \n", priv->rts_threshold);
8940 return 0;
8941}
8942
Jeff Garzikbf794512005-07-31 13:07:26 -04008943static int ipw_wx_get_rts(struct net_device *dev,
8944 struct iw_request_info *info,
James Ketrenos43f66a62005-03-25 12:31:53 -06008945 union iwreq_data *wrqu, char *extra)
Jeff Garzikbf794512005-07-31 13:07:26 -04008946{
James Ketrenos43f66a62005-03-25 12:31:53 -06008947 struct ipw_priv *priv = ieee80211_priv(dev);
James Ketrenosc848d0a2005-08-24 21:56:24 -05008948 down(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06008949 wrqu->rts.value = priv->rts_threshold;
8950 wrqu->rts.fixed = 0; /* no auto select */
Jeff Garzik0edd5b42005-09-07 00:48:31 -04008951 wrqu->rts.disabled = (wrqu->rts.value == DEFAULT_RTS_THRESHOLD);
James Ketrenosc848d0a2005-08-24 21:56:24 -05008952 up(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06008953 IPW_DEBUG_WX("GET RTS Threshold -> %d \n", wrqu->rts.value);
8954 return 0;
8955}
8956
Jeff Garzikbf794512005-07-31 13:07:26 -04008957static int ipw_wx_set_txpow(struct net_device *dev,
8958 struct iw_request_info *info,
James Ketrenos43f66a62005-03-25 12:31:53 -06008959 union iwreq_data *wrqu, char *extra)
Jeff Garzikbf794512005-07-31 13:07:26 -04008960{
James Ketrenos43f66a62005-03-25 12:31:53 -06008961 struct ipw_priv *priv = ieee80211_priv(dev);
Zhu Yi6de9f7f2005-08-11 14:39:33 +08008962 int err = 0;
James Ketrenosb095c382005-08-24 22:04:42 -05008963
James Ketrenosc848d0a2005-08-24 21:56:24 -05008964 down(&priv->sem);
8965 if (ipw_radio_kill_sw(priv, wrqu->power.disabled)) {
Zhu Yi6de9f7f2005-08-11 14:39:33 +08008966 err = -EINPROGRESS;
8967 goto out;
James Ketrenosc848d0a2005-08-24 21:56:24 -05008968 }
James Ketrenos43f66a62005-03-25 12:31:53 -06008969
James Ketrenosb095c382005-08-24 22:04:42 -05008970 if (!wrqu->power.fixed)
8971 wrqu->power.value = IPW_TX_POWER_DEFAULT;
8972
James Ketrenosc848d0a2005-08-24 21:56:24 -05008973 if (wrqu->power.flags != IW_TXPOW_DBM) {
Zhu Yi6de9f7f2005-08-11 14:39:33 +08008974 err = -EINVAL;
8975 goto out;
James Ketrenosc848d0a2005-08-24 21:56:24 -05008976 }
James Ketrenos43f66a62005-03-25 12:31:53 -06008977
James Ketrenosb095c382005-08-24 22:04:42 -05008978 if ((wrqu->power.value > IPW_TX_POWER_MAX) ||
James Ketrenosafbf30a2005-08-25 00:05:33 -05008979 (wrqu->power.value < IPW_TX_POWER_MIN)) {
Zhu Yi6de9f7f2005-08-11 14:39:33 +08008980 err = -EINVAL;
8981 goto out;
James Ketrenosc848d0a2005-08-24 21:56:24 -05008982 }
James Ketrenos43f66a62005-03-25 12:31:53 -06008983
8984 priv->tx_power = wrqu->power.value;
Zhu Yi6de9f7f2005-08-11 14:39:33 +08008985 err = ipw_set_tx_power(priv);
8986 out:
James Ketrenosc848d0a2005-08-24 21:56:24 -05008987 up(&priv->sem);
Zhu Yi6de9f7f2005-08-11 14:39:33 +08008988 return err;
James Ketrenos43f66a62005-03-25 12:31:53 -06008989}
8990
Jeff Garzikbf794512005-07-31 13:07:26 -04008991static int ipw_wx_get_txpow(struct net_device *dev,
8992 struct iw_request_info *info,
James Ketrenos43f66a62005-03-25 12:31:53 -06008993 union iwreq_data *wrqu, char *extra)
Jeff Garzikbf794512005-07-31 13:07:26 -04008994{
James Ketrenos43f66a62005-03-25 12:31:53 -06008995 struct ipw_priv *priv = ieee80211_priv(dev);
James Ketrenosc848d0a2005-08-24 21:56:24 -05008996 down(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06008997 wrqu->power.value = priv->tx_power;
8998 wrqu->power.fixed = 1;
8999 wrqu->power.flags = IW_TXPOW_DBM;
9000 wrqu->power.disabled = (priv->status & STATUS_RF_KILL_MASK) ? 1 : 0;
James Ketrenosc848d0a2005-08-24 21:56:24 -05009001 up(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06009002
Jeff Garzikbf794512005-07-31 13:07:26 -04009003 IPW_DEBUG_WX("GET TX Power -> %s %d \n",
Zhu Yi22501c82005-08-11 10:49:17 +08009004 wrqu->power.disabled ? "OFF" : "ON", wrqu->power.value);
James Ketrenos43f66a62005-03-25 12:31:53 -06009005
9006 return 0;
9007}
9008
Jeff Garzikbf794512005-07-31 13:07:26 -04009009static int ipw_wx_set_frag(struct net_device *dev,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04009010 struct iw_request_info *info,
9011 union iwreq_data *wrqu, char *extra)
James Ketrenos43f66a62005-03-25 12:31:53 -06009012{
9013 struct ipw_priv *priv = ieee80211_priv(dev);
James Ketrenosc848d0a2005-08-24 21:56:24 -05009014 down(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06009015 if (wrqu->frag.disabled)
9016 priv->ieee->fts = DEFAULT_FTS;
9017 else {
9018 if (wrqu->frag.value < MIN_FRAG_THRESHOLD ||
James Ketrenosb095c382005-08-24 22:04:42 -05009019 wrqu->frag.value > MAX_FRAG_THRESHOLD) {
9020 up(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06009021 return -EINVAL;
James Ketrenosb095c382005-08-24 22:04:42 -05009022 }
Jeff Garzikbf794512005-07-31 13:07:26 -04009023
James Ketrenos43f66a62005-03-25 12:31:53 -06009024 priv->ieee->fts = wrqu->frag.value & ~0x1;
9025 }
9026
9027 ipw_send_frag_threshold(priv, wrqu->frag.value);
James Ketrenosc848d0a2005-08-24 21:56:24 -05009028 up(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06009029 IPW_DEBUG_WX("SET Frag Threshold -> %d \n", wrqu->frag.value);
9030 return 0;
9031}
9032
Jeff Garzikbf794512005-07-31 13:07:26 -04009033static int ipw_wx_get_frag(struct net_device *dev,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04009034 struct iw_request_info *info,
9035 union iwreq_data *wrqu, char *extra)
James Ketrenos43f66a62005-03-25 12:31:53 -06009036{
9037 struct ipw_priv *priv = ieee80211_priv(dev);
James Ketrenosc848d0a2005-08-24 21:56:24 -05009038 down(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06009039 wrqu->frag.value = priv->ieee->fts;
9040 wrqu->frag.fixed = 0; /* no auto select */
Jeff Garzik0edd5b42005-09-07 00:48:31 -04009041 wrqu->frag.disabled = (wrqu->frag.value == DEFAULT_FTS);
James Ketrenosc848d0a2005-08-24 21:56:24 -05009042 up(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06009043 IPW_DEBUG_WX("GET Frag Threshold -> %d \n", wrqu->frag.value);
9044
9045 return 0;
9046}
9047
Jeff Garzikbf794512005-07-31 13:07:26 -04009048static int ipw_wx_set_retry(struct net_device *dev,
9049 struct iw_request_info *info,
James Ketrenos43f66a62005-03-25 12:31:53 -06009050 union iwreq_data *wrqu, char *extra)
Jeff Garzikbf794512005-07-31 13:07:26 -04009051{
James Ketrenosafbf30a2005-08-25 00:05:33 -05009052 struct ipw_priv *priv = ieee80211_priv(dev);
9053
9054 if (wrqu->retry.flags & IW_RETRY_LIFETIME || wrqu->retry.disabled)
9055 return -EINVAL;
9056
9057 if (!(wrqu->retry.flags & IW_RETRY_LIMIT))
9058 return 0;
9059
9060 if (wrqu->retry.value < 0 || wrqu->retry.value > 255)
9061 return -EINVAL;
9062
9063 down(&priv->sem);
9064 if (wrqu->retry.flags & IW_RETRY_MIN)
9065 priv->short_retry_limit = (u8) wrqu->retry.value;
9066 else if (wrqu->retry.flags & IW_RETRY_MAX)
9067 priv->long_retry_limit = (u8) wrqu->retry.value;
9068 else {
9069 priv->short_retry_limit = (u8) wrqu->retry.value;
9070 priv->long_retry_limit = (u8) wrqu->retry.value;
9071 }
9072
9073 ipw_send_retry_limit(priv, priv->short_retry_limit,
9074 priv->long_retry_limit);
9075 up(&priv->sem);
9076 IPW_DEBUG_WX("SET retry limit -> short:%d long:%d\n",
9077 priv->short_retry_limit, priv->long_retry_limit);
9078 return 0;
James Ketrenos43f66a62005-03-25 12:31:53 -06009079}
9080
Jeff Garzikbf794512005-07-31 13:07:26 -04009081static int ipw_wx_get_retry(struct net_device *dev,
9082 struct iw_request_info *info,
James Ketrenos43f66a62005-03-25 12:31:53 -06009083 union iwreq_data *wrqu, char *extra)
Jeff Garzikbf794512005-07-31 13:07:26 -04009084{
James Ketrenosafbf30a2005-08-25 00:05:33 -05009085 struct ipw_priv *priv = ieee80211_priv(dev);
9086
9087 down(&priv->sem);
9088 wrqu->retry.disabled = 0;
9089
9090 if ((wrqu->retry.flags & IW_RETRY_TYPE) == IW_RETRY_LIFETIME) {
9091 up(&priv->sem);
9092 return -EINVAL;
9093 }
9094
9095 if (wrqu->retry.flags & IW_RETRY_MAX) {
9096 wrqu->retry.flags = IW_RETRY_LIMIT | IW_RETRY_MAX;
9097 wrqu->retry.value = priv->long_retry_limit;
9098 } else if (wrqu->retry.flags & IW_RETRY_MIN) {
9099 wrqu->retry.flags = IW_RETRY_LIMIT | IW_RETRY_MIN;
9100 wrqu->retry.value = priv->short_retry_limit;
9101 } else {
9102 wrqu->retry.flags = IW_RETRY_LIMIT;
9103 wrqu->retry.value = priv->short_retry_limit;
9104 }
9105 up(&priv->sem);
9106
9107 IPW_DEBUG_WX("GET retry -> %d \n", wrqu->retry.value);
9108
9109 return 0;
James Ketrenos43f66a62005-03-25 12:31:53 -06009110}
9111
James Ketrenosafbf30a2005-08-25 00:05:33 -05009112#if WIRELESS_EXT > 17
9113static int ipw_request_direct_scan(struct ipw_priv *priv, char *essid,
9114 int essid_len)
9115{
9116 struct ipw_scan_request_ext scan;
9117 int err = 0, scan_type;
9118
9119 down(&priv->sem);
9120
9121 if (priv->status & STATUS_RF_KILL_MASK) {
9122 IPW_DEBUG_HC("Aborting scan due to RF kill activation\n");
9123 priv->status |= STATUS_SCAN_PENDING;
9124 goto done;
9125 }
9126
9127 IPW_DEBUG_HC("starting request direct scan!\n");
9128
9129 if (priv->status & (STATUS_SCANNING | STATUS_SCAN_ABORTING)) {
9130 err = wait_event_interruptible(priv->wait_state,
9131 !(priv->
9132 status & (STATUS_SCANNING |
9133 STATUS_SCAN_ABORTING)));
9134 if (err) {
9135 IPW_DEBUG_HC("aborting direct scan");
9136 goto done;
9137 }
9138 }
9139 memset(&scan, 0, sizeof(scan));
9140
9141 if (priv->config & CFG_SPEED_SCAN)
9142 scan.dwell_time[IPW_SCAN_ACTIVE_BROADCAST_SCAN] =
9143 cpu_to_le16(30);
9144 else
9145 scan.dwell_time[IPW_SCAN_ACTIVE_BROADCAST_SCAN] =
9146 cpu_to_le16(20);
9147
9148 scan.dwell_time[IPW_SCAN_ACTIVE_BROADCAST_AND_DIRECT_SCAN] =
9149 cpu_to_le16(20);
9150 scan.dwell_time[IPW_SCAN_PASSIVE_FULL_DWELL_SCAN] = cpu_to_le16(20);
9151 scan.dwell_time[IPW_SCAN_ACTIVE_DIRECT_SCAN] = cpu_to_le16(20);
9152
9153 scan.full_scan_index = cpu_to_le32(ieee80211_get_scans(priv->ieee));
9154
9155 err = ipw_send_ssid(priv, essid, essid_len);
9156 if (err) {
9157 IPW_DEBUG_HC("Attempt to send SSID command failed\n");
9158 goto done;
9159 }
9160 scan_type = IPW_SCAN_ACTIVE_BROADCAST_AND_DIRECT_SCAN;
9161
9162 ipw_add_scan_channels(priv, &scan, scan_type);
9163
9164 err = ipw_send_scan_request_ext(priv, &scan);
9165 if (err) {
9166 IPW_DEBUG_HC("Sending scan command failed: %08X\n", err);
9167 goto done;
9168 }
9169
9170 priv->status |= STATUS_SCANNING;
9171
9172 done:
9173 up(&priv->sem);
9174 return err;
9175}
9176#endif /* WIRELESS_EXT > 17 */
9177
Jeff Garzikbf794512005-07-31 13:07:26 -04009178static int ipw_wx_set_scan(struct net_device *dev,
9179 struct iw_request_info *info,
James Ketrenos43f66a62005-03-25 12:31:53 -06009180 union iwreq_data *wrqu, char *extra)
9181{
9182 struct ipw_priv *priv = ieee80211_priv(dev);
James Ketrenosafbf30a2005-08-25 00:05:33 -05009183#if WIRELESS_EXT > 17
9184 struct iw_scan_req *req = NULL;
9185 if (wrqu->data.length
9186 && wrqu->data.length == sizeof(struct iw_scan_req)) {
9187 req = (struct iw_scan_req *)extra;
9188 if (wrqu->data.flags & IW_SCAN_THIS_ESSID) {
9189 ipw_request_direct_scan(priv, req->essid,
9190 req->essid_len);
9191 return 0;
9192 }
9193 }
9194#endif
James Ketrenos43f66a62005-03-25 12:31:53 -06009195 IPW_DEBUG_WX("Start scan\n");
James Ketrenosb095c382005-08-24 22:04:42 -05009196
9197 queue_work(priv->workqueue, &priv->request_scan);
9198
James Ketrenos43f66a62005-03-25 12:31:53 -06009199 return 0;
9200}
9201
Jeff Garzikbf794512005-07-31 13:07:26 -04009202static int ipw_wx_get_scan(struct net_device *dev,
9203 struct iw_request_info *info,
James Ketrenos43f66a62005-03-25 12:31:53 -06009204 union iwreq_data *wrqu, char *extra)
Jeff Garzikbf794512005-07-31 13:07:26 -04009205{
James Ketrenos43f66a62005-03-25 12:31:53 -06009206 struct ipw_priv *priv = ieee80211_priv(dev);
9207 return ieee80211_wx_get_scan(priv->ieee, info, wrqu, extra);
9208}
9209
Jeff Garzikbf794512005-07-31 13:07:26 -04009210static int ipw_wx_set_encode(struct net_device *dev,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04009211 struct iw_request_info *info,
9212 union iwreq_data *wrqu, char *key)
James Ketrenos43f66a62005-03-25 12:31:53 -06009213{
9214 struct ipw_priv *priv = ieee80211_priv(dev);
James Ketrenosafbf30a2005-08-25 00:05:33 -05009215 int ret;
Hong Liucaeff812005-08-05 17:25:50 +08009216 u32 cap = priv->capability;
James Ketrenosafbf30a2005-08-25 00:05:33 -05009217
9218 down(&priv->sem);
9219 ret = ieee80211_wx_set_encode(priv->ieee, info, wrqu, key);
James Ketrenosafbf30a2005-08-25 00:05:33 -05009220
Hong Liucaeff812005-08-05 17:25:50 +08009221 /* In IBSS mode, we need to notify the firmware to update
9222 * the beacon info after we changed the capability. */
9223 if (cap != priv->capability &&
9224 priv->ieee->iw_mode == IW_MODE_ADHOC &&
9225 priv->status & STATUS_ASSOCIATED)
9226 ipw_disassociate(priv);
9227
9228 up(&priv->sem);
James Ketrenosafbf30a2005-08-25 00:05:33 -05009229 return ret;
James Ketrenos43f66a62005-03-25 12:31:53 -06009230}
9231
Jeff Garzikbf794512005-07-31 13:07:26 -04009232static int ipw_wx_get_encode(struct net_device *dev,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04009233 struct iw_request_info *info,
9234 union iwreq_data *wrqu, char *key)
James Ketrenos43f66a62005-03-25 12:31:53 -06009235{
9236 struct ipw_priv *priv = ieee80211_priv(dev);
9237 return ieee80211_wx_get_encode(priv->ieee, info, wrqu, key);
9238}
9239
Jeff Garzikbf794512005-07-31 13:07:26 -04009240static int ipw_wx_set_power(struct net_device *dev,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04009241 struct iw_request_info *info,
9242 union iwreq_data *wrqu, char *extra)
James Ketrenos43f66a62005-03-25 12:31:53 -06009243{
9244 struct ipw_priv *priv = ieee80211_priv(dev);
9245 int err;
James Ketrenosc848d0a2005-08-24 21:56:24 -05009246 down(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06009247 if (wrqu->power.disabled) {
9248 priv->power_mode = IPW_POWER_LEVEL(priv->power_mode);
9249 err = ipw_send_power_mode(priv, IPW_POWER_MODE_CAM);
9250 if (err) {
9251 IPW_DEBUG_WX("failed setting power mode.\n");
James Ketrenosc848d0a2005-08-24 21:56:24 -05009252 up(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06009253 return err;
9254 }
James Ketrenos43f66a62005-03-25 12:31:53 -06009255 IPW_DEBUG_WX("SET Power Management Mode -> off\n");
James Ketrenosc848d0a2005-08-24 21:56:24 -05009256 up(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06009257 return 0;
Jeff Garzikbf794512005-07-31 13:07:26 -04009258 }
James Ketrenos43f66a62005-03-25 12:31:53 -06009259
9260 switch (wrqu->power.flags & IW_POWER_MODE) {
Jeff Garzik0edd5b42005-09-07 00:48:31 -04009261 case IW_POWER_ON: /* If not specified */
9262 case IW_POWER_MODE: /* If set all mask */
9263 case IW_POWER_ALL_R: /* If explicitely state all */
James Ketrenos43f66a62005-03-25 12:31:53 -06009264 break;
Jeff Garzik0edd5b42005-09-07 00:48:31 -04009265 default: /* Otherwise we don't support it */
James Ketrenos43f66a62005-03-25 12:31:53 -06009266 IPW_DEBUG_WX("SET PM Mode: %X not supported.\n",
9267 wrqu->power.flags);
James Ketrenosc848d0a2005-08-24 21:56:24 -05009268 up(&priv->sem);
Jeff Garzikbf794512005-07-31 13:07:26 -04009269 return -EOPNOTSUPP;
James Ketrenos43f66a62005-03-25 12:31:53 -06009270 }
Jeff Garzikbf794512005-07-31 13:07:26 -04009271
James Ketrenos43f66a62005-03-25 12:31:53 -06009272 /* If the user hasn't specified a power management mode yet, default
9273 * to BATTERY */
Jeff Garzik0edd5b42005-09-07 00:48:31 -04009274 if (IPW_POWER_LEVEL(priv->power_mode) == IPW_POWER_AC)
James Ketrenos43f66a62005-03-25 12:31:53 -06009275 priv->power_mode = IPW_POWER_ENABLED | IPW_POWER_BATTERY;
Jeff Garzikbf794512005-07-31 13:07:26 -04009276 else
James Ketrenos43f66a62005-03-25 12:31:53 -06009277 priv->power_mode = IPW_POWER_ENABLED | priv->power_mode;
9278 err = ipw_send_power_mode(priv, IPW_POWER_LEVEL(priv->power_mode));
9279 if (err) {
9280 IPW_DEBUG_WX("failed setting power mode.\n");
James Ketrenosc848d0a2005-08-24 21:56:24 -05009281 up(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06009282 return err;
9283 }
9284
Jeff Garzik0edd5b42005-09-07 00:48:31 -04009285 IPW_DEBUG_WX("SET Power Management Mode -> 0x%02X\n", priv->power_mode);
James Ketrenosc848d0a2005-08-24 21:56:24 -05009286 up(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06009287 return 0;
9288}
9289
Jeff Garzikbf794512005-07-31 13:07:26 -04009290static int ipw_wx_get_power(struct net_device *dev,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04009291 struct iw_request_info *info,
9292 union iwreq_data *wrqu, char *extra)
James Ketrenos43f66a62005-03-25 12:31:53 -06009293{
9294 struct ipw_priv *priv = ieee80211_priv(dev);
James Ketrenosc848d0a2005-08-24 21:56:24 -05009295 down(&priv->sem);
James Ketrenosa613bff2005-08-24 21:43:11 -05009296 if (!(priv->power_mode & IPW_POWER_ENABLED))
James Ketrenos43f66a62005-03-25 12:31:53 -06009297 wrqu->power.disabled = 1;
James Ketrenosa613bff2005-08-24 21:43:11 -05009298 else
James Ketrenos43f66a62005-03-25 12:31:53 -06009299 wrqu->power.disabled = 0;
James Ketrenos43f66a62005-03-25 12:31:53 -06009300
James Ketrenosc848d0a2005-08-24 21:56:24 -05009301 up(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06009302 IPW_DEBUG_WX("GET Power Management Mode -> %02X\n", priv->power_mode);
Jeff Garzikbf794512005-07-31 13:07:26 -04009303
James Ketrenos43f66a62005-03-25 12:31:53 -06009304 return 0;
9305}
9306
Jeff Garzikbf794512005-07-31 13:07:26 -04009307static int ipw_wx_set_powermode(struct net_device *dev,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04009308 struct iw_request_info *info,
9309 union iwreq_data *wrqu, char *extra)
James Ketrenos43f66a62005-03-25 12:31:53 -06009310{
9311 struct ipw_priv *priv = ieee80211_priv(dev);
9312 int mode = *(int *)extra;
9313 int err;
James Ketrenosc848d0a2005-08-24 21:56:24 -05009314 down(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06009315 if ((mode < 1) || (mode > IPW_POWER_LIMIT)) {
9316 mode = IPW_POWER_AC;
9317 priv->power_mode = mode;
9318 } else {
9319 priv->power_mode = IPW_POWER_ENABLED | mode;
9320 }
Jeff Garzikbf794512005-07-31 13:07:26 -04009321
James Ketrenos43f66a62005-03-25 12:31:53 -06009322 if (priv->power_mode != mode) {
9323 err = ipw_send_power_mode(priv, mode);
Jeff Garzikbf794512005-07-31 13:07:26 -04009324
James Ketrenos43f66a62005-03-25 12:31:53 -06009325 if (err) {
9326 IPW_DEBUG_WX("failed setting power mode.\n");
James Ketrenosc848d0a2005-08-24 21:56:24 -05009327 up(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06009328 return err;
9329 }
9330 }
James Ketrenosc848d0a2005-08-24 21:56:24 -05009331 up(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06009332 return 0;
9333}
9334
9335#define MAX_WX_STRING 80
Jeff Garzikbf794512005-07-31 13:07:26 -04009336static int ipw_wx_get_powermode(struct net_device *dev,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04009337 struct iw_request_info *info,
9338 union iwreq_data *wrqu, char *extra)
James Ketrenos43f66a62005-03-25 12:31:53 -06009339{
9340 struct ipw_priv *priv = ieee80211_priv(dev);
9341 int level = IPW_POWER_LEVEL(priv->power_mode);
9342 char *p = extra;
9343
9344 p += snprintf(p, MAX_WX_STRING, "Power save level: %d ", level);
9345
9346 switch (level) {
9347 case IPW_POWER_AC:
9348 p += snprintf(p, MAX_WX_STRING - (p - extra), "(AC)");
9349 break;
9350 case IPW_POWER_BATTERY:
9351 p += snprintf(p, MAX_WX_STRING - (p - extra), "(BATTERY)");
9352 break;
9353 default:
9354 p += snprintf(p, MAX_WX_STRING - (p - extra),
Jeff Garzikbf794512005-07-31 13:07:26 -04009355 "(Timeout %dms, Period %dms)",
James Ketrenos43f66a62005-03-25 12:31:53 -06009356 timeout_duration[level - 1] / 1000,
9357 period_duration[level - 1] / 1000);
9358 }
9359
9360 if (!(priv->power_mode & IPW_POWER_ENABLED))
Jeff Garzik0edd5b42005-09-07 00:48:31 -04009361 p += snprintf(p, MAX_WX_STRING - (p - extra), " OFF");
James Ketrenos43f66a62005-03-25 12:31:53 -06009362
9363 wrqu->data.length = p - extra + 1;
9364
9365 return 0;
9366}
9367
9368static int ipw_wx_set_wireless_mode(struct net_device *dev,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04009369 struct iw_request_info *info,
9370 union iwreq_data *wrqu, char *extra)
James Ketrenos43f66a62005-03-25 12:31:53 -06009371{
Jeff Garzik0edd5b42005-09-07 00:48:31 -04009372 struct ipw_priv *priv = ieee80211_priv(dev);
James Ketrenos43f66a62005-03-25 12:31:53 -06009373 int mode = *(int *)extra;
9374 u8 band = 0, modulation = 0;
9375
9376 if (mode == 0 || mode & ~IEEE_MODE_MASK) {
Jeff Garzik0edd5b42005-09-07 00:48:31 -04009377 IPW_WARNING("Attempt to set invalid wireless mode: %d\n", mode);
James Ketrenos43f66a62005-03-25 12:31:53 -06009378 return -EINVAL;
9379 }
James Ketrenosc848d0a2005-08-24 21:56:24 -05009380 down(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06009381 if (priv->adapter == IPW_2915ABG) {
James Ketrenosa33a1982005-09-14 14:28:59 -05009382 priv->ieee->abg_true = 1;
James Ketrenos43f66a62005-03-25 12:31:53 -06009383 if (mode & IEEE_A) {
9384 band |= IEEE80211_52GHZ_BAND;
9385 modulation |= IEEE80211_OFDM_MODULATION;
9386 } else
James Ketrenosa33a1982005-09-14 14:28:59 -05009387 priv->ieee->abg_true = 0;
James Ketrenos43f66a62005-03-25 12:31:53 -06009388 } else {
9389 if (mode & IEEE_A) {
9390 IPW_WARNING("Attempt to set 2200BG into "
9391 "802.11a mode\n");
James Ketrenosc848d0a2005-08-24 21:56:24 -05009392 up(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06009393 return -EINVAL;
9394 }
9395
James Ketrenosa33a1982005-09-14 14:28:59 -05009396 priv->ieee->abg_true = 0;
James Ketrenos43f66a62005-03-25 12:31:53 -06009397 }
9398
9399 if (mode & IEEE_B) {
9400 band |= IEEE80211_24GHZ_BAND;
9401 modulation |= IEEE80211_CCK_MODULATION;
9402 } else
James Ketrenosa33a1982005-09-14 14:28:59 -05009403 priv->ieee->abg_true = 0;
Jeff Garzikbf794512005-07-31 13:07:26 -04009404
James Ketrenos43f66a62005-03-25 12:31:53 -06009405 if (mode & IEEE_G) {
9406 band |= IEEE80211_24GHZ_BAND;
9407 modulation |= IEEE80211_OFDM_MODULATION;
9408 } else
James Ketrenosa33a1982005-09-14 14:28:59 -05009409 priv->ieee->abg_true = 0;
James Ketrenos43f66a62005-03-25 12:31:53 -06009410
9411 priv->ieee->mode = mode;
9412 priv->ieee->freq_band = band;
9413 priv->ieee->modulation = modulation;
Jeff Garzik0edd5b42005-09-07 00:48:31 -04009414 init_supported_rates(priv, &priv->rates);
James Ketrenos43f66a62005-03-25 12:31:53 -06009415
James Ketrenosc848d0a2005-08-24 21:56:24 -05009416 /* Network configuration changed -- force [re]association */
9417 IPW_DEBUG_ASSOC("[re]association triggered due to mode change.\n");
9418 if (!ipw_disassociate(priv)) {
James Ketrenos43f66a62005-03-25 12:31:53 -06009419 ipw_send_supported_rates(priv, &priv->rates);
James Ketrenosc848d0a2005-08-24 21:56:24 -05009420 ipw_associate(priv);
9421 }
James Ketrenos43f66a62005-03-25 12:31:53 -06009422
James Ketrenosa613bff2005-08-24 21:43:11 -05009423 /* Update the band LEDs */
9424 ipw_led_band_on(priv);
9425
Jeff Garzikbf794512005-07-31 13:07:26 -04009426 IPW_DEBUG_WX("PRIV SET MODE: %c%c%c\n",
James Ketrenos43f66a62005-03-25 12:31:53 -06009427 mode & IEEE_A ? 'a' : '.',
Jeff Garzik0edd5b42005-09-07 00:48:31 -04009428 mode & IEEE_B ? 'b' : '.', mode & IEEE_G ? 'g' : '.');
James Ketrenosc848d0a2005-08-24 21:56:24 -05009429 up(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06009430 return 0;
9431}
9432
9433static int ipw_wx_get_wireless_mode(struct net_device *dev,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04009434 struct iw_request_info *info,
9435 union iwreq_data *wrqu, char *extra)
James Ketrenos43f66a62005-03-25 12:31:53 -06009436{
Jeff Garzik0edd5b42005-09-07 00:48:31 -04009437 struct ipw_priv *priv = ieee80211_priv(dev);
James Ketrenosc848d0a2005-08-24 21:56:24 -05009438 down(&priv->sem);
James Ketrenosea2b26e2005-08-24 21:25:16 -05009439 switch (priv->ieee->mode) {
9440 case IEEE_A:
James Ketrenos43f66a62005-03-25 12:31:53 -06009441 strncpy(extra, "802.11a (1)", MAX_WX_STRING);
9442 break;
James Ketrenosea2b26e2005-08-24 21:25:16 -05009443 case IEEE_B:
9444 strncpy(extra, "802.11b (2)", MAX_WX_STRING);
9445 break;
9446 case IEEE_A | IEEE_B:
9447 strncpy(extra, "802.11ab (3)", MAX_WX_STRING);
9448 break;
9449 case IEEE_G:
9450 strncpy(extra, "802.11g (4)", MAX_WX_STRING);
9451 break;
9452 case IEEE_A | IEEE_G:
9453 strncpy(extra, "802.11ag (5)", MAX_WX_STRING);
9454 break;
9455 case IEEE_B | IEEE_G:
9456 strncpy(extra, "802.11bg (6)", MAX_WX_STRING);
9457 break;
9458 case IEEE_A | IEEE_B | IEEE_G:
9459 strncpy(extra, "802.11abg (7)", MAX_WX_STRING);
9460 break;
9461 default:
9462 strncpy(extra, "unknown", MAX_WX_STRING);
James Ketrenos43f66a62005-03-25 12:31:53 -06009463 break;
Jeff Garzikbf794512005-07-31 13:07:26 -04009464 }
9465
James Ketrenos43f66a62005-03-25 12:31:53 -06009466 IPW_DEBUG_WX("PRIV GET MODE: %s\n", extra);
9467
Jeff Garzik0edd5b42005-09-07 00:48:31 -04009468 wrqu->data.length = strlen(extra) + 1;
James Ketrenosc848d0a2005-08-24 21:56:24 -05009469 up(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06009470
Jeff Garzik0edd5b42005-09-07 00:48:31 -04009471 return 0;
James Ketrenos43f66a62005-03-25 12:31:53 -06009472}
9473
James Ketrenosea2b26e2005-08-24 21:25:16 -05009474static int ipw_wx_set_preamble(struct net_device *dev,
9475 struct iw_request_info *info,
9476 union iwreq_data *wrqu, char *extra)
9477{
9478 struct ipw_priv *priv = ieee80211_priv(dev);
9479 int mode = *(int *)extra;
James Ketrenosc848d0a2005-08-24 21:56:24 -05009480 down(&priv->sem);
James Ketrenosea2b26e2005-08-24 21:25:16 -05009481 /* Switching from SHORT -> LONG requires a disassociation */
9482 if (mode == 1) {
9483 if (!(priv->config & CFG_PREAMBLE_LONG)) {
9484 priv->config |= CFG_PREAMBLE_LONG;
James Ketrenosc848d0a2005-08-24 21:56:24 -05009485
9486 /* Network configuration changed -- force [re]association */
9487 IPW_DEBUG_ASSOC
9488 ("[re]association triggered due to preamble change.\n");
9489 if (!ipw_disassociate(priv))
9490 ipw_associate(priv);
James Ketrenosea2b26e2005-08-24 21:25:16 -05009491 }
9492 goto done;
9493 }
9494
9495 if (mode == 0) {
9496 priv->config &= ~CFG_PREAMBLE_LONG;
9497 goto done;
9498 }
James Ketrenosc848d0a2005-08-24 21:56:24 -05009499 up(&priv->sem);
James Ketrenosea2b26e2005-08-24 21:25:16 -05009500 return -EINVAL;
9501
9502 done:
James Ketrenosc848d0a2005-08-24 21:56:24 -05009503 up(&priv->sem);
James Ketrenosea2b26e2005-08-24 21:25:16 -05009504 return 0;
9505}
9506
9507static int ipw_wx_get_preamble(struct net_device *dev,
9508 struct iw_request_info *info,
9509 union iwreq_data *wrqu, char *extra)
9510{
9511 struct ipw_priv *priv = ieee80211_priv(dev);
James Ketrenosc848d0a2005-08-24 21:56:24 -05009512 down(&priv->sem);
James Ketrenosea2b26e2005-08-24 21:25:16 -05009513 if (priv->config & CFG_PREAMBLE_LONG)
9514 snprintf(wrqu->name, IFNAMSIZ, "long (1)");
9515 else
9516 snprintf(wrqu->name, IFNAMSIZ, "auto (0)");
James Ketrenosc848d0a2005-08-24 21:56:24 -05009517 up(&priv->sem);
James Ketrenosea2b26e2005-08-24 21:25:16 -05009518 return 0;
9519}
9520
James Ketrenosb095c382005-08-24 22:04:42 -05009521#ifdef CONFIG_IPW2200_MONITOR
James Ketrenosea2b26e2005-08-24 21:25:16 -05009522static int ipw_wx_set_monitor(struct net_device *dev,
Jeff Garzikbf794512005-07-31 13:07:26 -04009523 struct iw_request_info *info,
James Ketrenos43f66a62005-03-25 12:31:53 -06009524 union iwreq_data *wrqu, char *extra)
Jeff Garzikbf794512005-07-31 13:07:26 -04009525{
James Ketrenos43f66a62005-03-25 12:31:53 -06009526 struct ipw_priv *priv = ieee80211_priv(dev);
9527 int *parms = (int *)extra;
9528 int enable = (parms[0] > 0);
James Ketrenosc848d0a2005-08-24 21:56:24 -05009529 down(&priv->sem);
James Ketrenosea2b26e2005-08-24 21:25:16 -05009530 IPW_DEBUG_WX("SET MONITOR: %d %d\n", enable, parms[1]);
James Ketrenos43f66a62005-03-25 12:31:53 -06009531 if (enable) {
9532 if (priv->ieee->iw_mode != IW_MODE_MONITOR) {
9533 priv->net_dev->type = ARPHRD_IEEE80211;
James Ketrenosa613bff2005-08-24 21:43:11 -05009534 queue_work(priv->workqueue, &priv->adapter_restart);
James Ketrenos43f66a62005-03-25 12:31:53 -06009535 }
Jeff Garzikbf794512005-07-31 13:07:26 -04009536
James Ketrenos43f66a62005-03-25 12:31:53 -06009537 ipw_set_channel(priv, parms[1]);
9538 } else {
James Ketrenosc848d0a2005-08-24 21:56:24 -05009539 if (priv->ieee->iw_mode != IW_MODE_MONITOR) {
9540 up(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06009541 return 0;
James Ketrenosc848d0a2005-08-24 21:56:24 -05009542 }
James Ketrenos43f66a62005-03-25 12:31:53 -06009543 priv->net_dev->type = ARPHRD_ETHER;
James Ketrenosa613bff2005-08-24 21:43:11 -05009544 queue_work(priv->workqueue, &priv->adapter_restart);
James Ketrenos43f66a62005-03-25 12:31:53 -06009545 }
James Ketrenosc848d0a2005-08-24 21:56:24 -05009546 up(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06009547 return 0;
9548}
9549
James Ketrenosb095c382005-08-24 22:04:42 -05009550#endif // CONFIG_IPW2200_MONITOR
9551
Jeff Garzikbf794512005-07-31 13:07:26 -04009552static int ipw_wx_reset(struct net_device *dev,
9553 struct iw_request_info *info,
James Ketrenos43f66a62005-03-25 12:31:53 -06009554 union iwreq_data *wrqu, char *extra)
Jeff Garzikbf794512005-07-31 13:07:26 -04009555{
James Ketrenos43f66a62005-03-25 12:31:53 -06009556 struct ipw_priv *priv = ieee80211_priv(dev);
9557 IPW_DEBUG_WX("RESET\n");
James Ketrenosa613bff2005-08-24 21:43:11 -05009558 queue_work(priv->workqueue, &priv->adapter_restart);
James Ketrenos43f66a62005-03-25 12:31:53 -06009559 return 0;
9560}
James Ketrenosb095c382005-08-24 22:04:42 -05009561
James Ketrenosb095c382005-08-24 22:04:42 -05009562static int ipw_wx_sw_reset(struct net_device *dev,
9563 struct iw_request_info *info,
9564 union iwreq_data *wrqu, char *extra)
9565{
9566 struct ipw_priv *priv = ieee80211_priv(dev);
9567 union iwreq_data wrqu_sec = {
9568 .encoding = {
9569 .flags = IW_ENCODE_DISABLED,
9570 },
9571 };
James Ketrenosafbf30a2005-08-25 00:05:33 -05009572 int ret;
James Ketrenosb095c382005-08-24 22:04:42 -05009573
9574 IPW_DEBUG_WX("SW_RESET\n");
9575
9576 down(&priv->sem);
9577
James Ketrenosafbf30a2005-08-25 00:05:33 -05009578 ret = ipw_sw_reset(priv, 0);
9579 if (!ret) {
9580 free_firmware();
9581 ipw_adapter_restart(priv);
9582 }
James Ketrenosb095c382005-08-24 22:04:42 -05009583
9584 /* The SW reset bit might have been toggled on by the 'disable'
9585 * module parameter, so take appropriate action */
9586 ipw_radio_kill_sw(priv, priv->status & STATUS_RF_KILL_SW);
9587
9588 up(&priv->sem);
9589 ieee80211_wx_set_encode(priv->ieee, info, &wrqu_sec, NULL);
9590 down(&priv->sem);
9591
9592 if (!(priv->status & STATUS_RF_KILL_MASK)) {
9593 /* Configuration likely changed -- force [re]association */
9594 IPW_DEBUG_ASSOC("[re]association triggered due to sw "
9595 "reset.\n");
9596 if (!ipw_disassociate(priv))
9597 ipw_associate(priv);
9598 }
9599
9600 up(&priv->sem);
9601
9602 return 0;
9603}
James Ketrenos43f66a62005-03-25 12:31:53 -06009604
9605/* Rebase the WE IOCTLs to zero for the handler array */
9606#define IW_IOCTL(x) [(x)-SIOCSIWCOMMIT]
Jeff Garzik0edd5b42005-09-07 00:48:31 -04009607static iw_handler ipw_wx_handlers[] = {
James Ketrenosea2b26e2005-08-24 21:25:16 -05009608 IW_IOCTL(SIOCGIWNAME) = ipw_wx_get_name,
9609 IW_IOCTL(SIOCSIWFREQ) = ipw_wx_set_freq,
9610 IW_IOCTL(SIOCGIWFREQ) = ipw_wx_get_freq,
9611 IW_IOCTL(SIOCSIWMODE) = ipw_wx_set_mode,
9612 IW_IOCTL(SIOCGIWMODE) = ipw_wx_get_mode,
9613 IW_IOCTL(SIOCGIWRANGE) = ipw_wx_get_range,
9614 IW_IOCTL(SIOCSIWAP) = ipw_wx_set_wap,
9615 IW_IOCTL(SIOCGIWAP) = ipw_wx_get_wap,
9616 IW_IOCTL(SIOCSIWSCAN) = ipw_wx_set_scan,
9617 IW_IOCTL(SIOCGIWSCAN) = ipw_wx_get_scan,
9618 IW_IOCTL(SIOCSIWESSID) = ipw_wx_set_essid,
9619 IW_IOCTL(SIOCGIWESSID) = ipw_wx_get_essid,
9620 IW_IOCTL(SIOCSIWNICKN) = ipw_wx_set_nick,
9621 IW_IOCTL(SIOCGIWNICKN) = ipw_wx_get_nick,
9622 IW_IOCTL(SIOCSIWRATE) = ipw_wx_set_rate,
9623 IW_IOCTL(SIOCGIWRATE) = ipw_wx_get_rate,
9624 IW_IOCTL(SIOCSIWRTS) = ipw_wx_set_rts,
9625 IW_IOCTL(SIOCGIWRTS) = ipw_wx_get_rts,
9626 IW_IOCTL(SIOCSIWFRAG) = ipw_wx_set_frag,
9627 IW_IOCTL(SIOCGIWFRAG) = ipw_wx_get_frag,
9628 IW_IOCTL(SIOCSIWTXPOW) = ipw_wx_set_txpow,
9629 IW_IOCTL(SIOCGIWTXPOW) = ipw_wx_get_txpow,
9630 IW_IOCTL(SIOCSIWRETRY) = ipw_wx_set_retry,
9631 IW_IOCTL(SIOCGIWRETRY) = ipw_wx_get_retry,
9632 IW_IOCTL(SIOCSIWENCODE) = ipw_wx_set_encode,
9633 IW_IOCTL(SIOCGIWENCODE) = ipw_wx_get_encode,
9634 IW_IOCTL(SIOCSIWPOWER) = ipw_wx_set_power,
9635 IW_IOCTL(SIOCGIWPOWER) = ipw_wx_get_power,
James Ketrenosa613bff2005-08-24 21:43:11 -05009636 IW_IOCTL(SIOCSIWSPY) = iw_handler_set_spy,
9637 IW_IOCTL(SIOCGIWSPY) = iw_handler_get_spy,
9638 IW_IOCTL(SIOCSIWTHRSPY) = iw_handler_set_thrspy,
9639 IW_IOCTL(SIOCGIWTHRSPY) = iw_handler_get_thrspy,
James Ketrenosafbf30a2005-08-25 00:05:33 -05009640#if WIRELESS_EXT > 17
9641 IW_IOCTL(SIOCSIWGENIE) = ipw_wx_set_genie,
9642 IW_IOCTL(SIOCGIWGENIE) = ipw_wx_get_genie,
9643 IW_IOCTL(SIOCSIWMLME) = ipw_wx_set_mlme,
9644 IW_IOCTL(SIOCSIWAUTH) = ipw_wx_set_auth,
9645 IW_IOCTL(SIOCGIWAUTH) = ipw_wx_get_auth,
9646 IW_IOCTL(SIOCSIWENCODEEXT) = ipw_wx_set_encodeext,
9647 IW_IOCTL(SIOCGIWENCODEEXT) = ipw_wx_get_encodeext,
9648#endif
James Ketrenos43f66a62005-03-25 12:31:53 -06009649};
9650
James Ketrenosb095c382005-08-24 22:04:42 -05009651enum {
9652 IPW_PRIV_SET_POWER = SIOCIWFIRSTPRIV,
9653 IPW_PRIV_GET_POWER,
9654 IPW_PRIV_SET_MODE,
9655 IPW_PRIV_GET_MODE,
9656 IPW_PRIV_SET_PREAMBLE,
9657 IPW_PRIV_GET_PREAMBLE,
9658 IPW_PRIV_RESET,
9659 IPW_PRIV_SW_RESET,
9660#ifdef CONFIG_IPW2200_MONITOR
9661 IPW_PRIV_SET_MONITOR,
9662#endif
9663};
James Ketrenos43f66a62005-03-25 12:31:53 -06009664
Jeff Garzikbf794512005-07-31 13:07:26 -04009665static struct iw_priv_args ipw_priv_args[] = {
James Ketrenos43f66a62005-03-25 12:31:53 -06009666 {
Jeff Garzik0edd5b42005-09-07 00:48:31 -04009667 .cmd = IPW_PRIV_SET_POWER,
9668 .set_args = IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
9669 .name = "set_power"},
James Ketrenos43f66a62005-03-25 12:31:53 -06009670 {
Jeff Garzik0edd5b42005-09-07 00:48:31 -04009671 .cmd = IPW_PRIV_GET_POWER,
9672 .get_args = IW_PRIV_TYPE_CHAR | IW_PRIV_SIZE_FIXED | MAX_WX_STRING,
9673 .name = "get_power"},
James Ketrenos43f66a62005-03-25 12:31:53 -06009674 {
Jeff Garzik0edd5b42005-09-07 00:48:31 -04009675 .cmd = IPW_PRIV_SET_MODE,
9676 .set_args = IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
9677 .name = "set_mode"},
James Ketrenos43f66a62005-03-25 12:31:53 -06009678 {
Jeff Garzik0edd5b42005-09-07 00:48:31 -04009679 .cmd = IPW_PRIV_GET_MODE,
9680 .get_args = IW_PRIV_TYPE_CHAR | IW_PRIV_SIZE_FIXED | MAX_WX_STRING,
9681 .name = "get_mode"},
James Ketrenos43f66a62005-03-25 12:31:53 -06009682 {
James Ketrenosea2b26e2005-08-24 21:25:16 -05009683 .cmd = IPW_PRIV_SET_PREAMBLE,
9684 .set_args = IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
9685 .name = "set_preamble"},
9686 {
9687 .cmd = IPW_PRIV_GET_PREAMBLE,
9688 .get_args = IW_PRIV_TYPE_CHAR | IW_PRIV_SIZE_FIXED | IFNAMSIZ,
9689 .name = "get_preamble"},
James Ketrenos43f66a62005-03-25 12:31:53 -06009690 {
Jeff Garzik0edd5b42005-09-07 00:48:31 -04009691 IPW_PRIV_RESET,
9692 IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 0, 0, "reset"},
James Ketrenosb095c382005-08-24 22:04:42 -05009693 {
9694 IPW_PRIV_SW_RESET,
9695 IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 0, 0, "sw_reset"},
9696#ifdef CONFIG_IPW2200_MONITOR
9697 {
9698 IPW_PRIV_SET_MONITOR,
9699 IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 2, 0, "monitor"},
9700#endif /* CONFIG_IPW2200_MONITOR */
James Ketrenos43f66a62005-03-25 12:31:53 -06009701};
9702
9703static iw_handler ipw_priv_handler[] = {
9704 ipw_wx_set_powermode,
9705 ipw_wx_get_powermode,
9706 ipw_wx_set_wireless_mode,
9707 ipw_wx_get_wireless_mode,
James Ketrenosea2b26e2005-08-24 21:25:16 -05009708 ipw_wx_set_preamble,
9709 ipw_wx_get_preamble,
Jeff Garzikbf794512005-07-31 13:07:26 -04009710 ipw_wx_reset,
James Ketrenosb095c382005-08-24 22:04:42 -05009711 ipw_wx_sw_reset,
9712#ifdef CONFIG_IPW2200_MONITOR
9713 ipw_wx_set_monitor,
James Ketrenos43f66a62005-03-25 12:31:53 -06009714#endif
9715};
9716
Jeff Garzik0edd5b42005-09-07 00:48:31 -04009717static struct iw_handler_def ipw_wx_handler_def = {
James Ketrenosea2b26e2005-08-24 21:25:16 -05009718 .standard = ipw_wx_handlers,
9719 .num_standard = ARRAY_SIZE(ipw_wx_handlers),
9720 .num_private = ARRAY_SIZE(ipw_priv_handler),
9721 .num_private_args = ARRAY_SIZE(ipw_priv_args),
9722 .private = ipw_priv_handler,
9723 .private_args = ipw_priv_args,
James Ketrenos43f66a62005-03-25 12:31:53 -06009724};
9725
James Ketrenosa613bff2005-08-24 21:43:11 -05009726static struct iw_public_data ipw_wx_data;
9727
James Ketrenos43f66a62005-03-25 12:31:53 -06009728/*
9729 * Get wireless statistics.
9730 * Called by /proc/net/wireless
9731 * Also called by SIOCGIWSTATS
9732 */
Jeff Garzik0edd5b42005-09-07 00:48:31 -04009733static struct iw_statistics *ipw_get_wireless_stats(struct net_device *dev)
James Ketrenos43f66a62005-03-25 12:31:53 -06009734{
9735 struct ipw_priv *priv = ieee80211_priv(dev);
9736 struct iw_statistics *wstats;
Jeff Garzikbf794512005-07-31 13:07:26 -04009737
James Ketrenos43f66a62005-03-25 12:31:53 -06009738 wstats = &priv->wstats;
9739
James Ketrenosea2b26e2005-08-24 21:25:16 -05009740 /* if hw is disabled, then ipw_get_ordinal() can't be called.
James Ketrenosafbf30a2005-08-25 00:05:33 -05009741 * netdev->get_wireless_stats seems to be called before fw is
James Ketrenos43f66a62005-03-25 12:31:53 -06009742 * initialized. STATUS_ASSOCIATED will only be set if the hw is up
9743 * and associated; if not associcated, the values are all meaningless
9744 * anyway, so set them all to NULL and INVALID */
9745 if (!(priv->status & STATUS_ASSOCIATED)) {
9746 wstats->miss.beacon = 0;
9747 wstats->discard.retries = 0;
9748 wstats->qual.qual = 0;
9749 wstats->qual.level = 0;
9750 wstats->qual.noise = 0;
9751 wstats->qual.updated = 7;
9752 wstats->qual.updated |= IW_QUAL_NOISE_INVALID |
Jeff Garzik0edd5b42005-09-07 00:48:31 -04009753 IW_QUAL_QUAL_INVALID | IW_QUAL_LEVEL_INVALID;
James Ketrenos43f66a62005-03-25 12:31:53 -06009754 return wstats;
Jeff Garzikbf794512005-07-31 13:07:26 -04009755 }
James Ketrenos43f66a62005-03-25 12:31:53 -06009756
9757 wstats->qual.qual = priv->quality;
9758 wstats->qual.level = average_value(&priv->average_rssi);
9759 wstats->qual.noise = average_value(&priv->average_noise);
9760 wstats->qual.updated = IW_QUAL_QUAL_UPDATED | IW_QUAL_LEVEL_UPDATED |
Jeff Garzik0edd5b42005-09-07 00:48:31 -04009761 IW_QUAL_NOISE_UPDATED;
James Ketrenos43f66a62005-03-25 12:31:53 -06009762
9763 wstats->miss.beacon = average_value(&priv->average_missed_beacons);
9764 wstats->discard.retries = priv->last_tx_failures;
9765 wstats->discard.code = priv->ieee->ieee_stats.rx_discards_undecryptable;
Jeff Garzikbf794512005-07-31 13:07:26 -04009766
James Ketrenos43f66a62005-03-25 12:31:53 -06009767/* if (ipw_get_ordinal(priv, IPW_ORD_STAT_TX_RETRY, &tx_retry, &len))
9768 goto fail_get_ordinal;
9769 wstats->discard.retries += tx_retry; */
Jeff Garzikbf794512005-07-31 13:07:26 -04009770
James Ketrenos43f66a62005-03-25 12:31:53 -06009771 return wstats;
9772}
9773
James Ketrenos43f66a62005-03-25 12:31:53 -06009774/* net device stuff */
9775
9776static inline void init_sys_config(struct ipw_sys_config *sys_config)
9777{
Jeff Garzik0edd5b42005-09-07 00:48:31 -04009778 memset(sys_config, 0, sizeof(struct ipw_sys_config));
9779 sys_config->bt_coexistence = 1; /* We may need to look into prvStaBtConfig */
James Ketrenos43f66a62005-03-25 12:31:53 -06009780 sys_config->answer_broadcast_ssid_probe = 0;
9781 sys_config->accept_all_data_frames = 0;
9782 sys_config->accept_non_directed_frames = 1;
9783 sys_config->exclude_unicast_unencrypted = 0;
9784 sys_config->disable_unicast_decryption = 1;
9785 sys_config->exclude_multicast_unencrypted = 0;
9786 sys_config->disable_multicast_decryption = 1;
9787 sys_config->antenna_diversity = CFG_SYS_ANTENNA_BOTH;
Jeff Garzik0edd5b42005-09-07 00:48:31 -04009788 sys_config->pass_crc_to_host = 0; /* TODO: See if 1 gives us FCS */
James Ketrenos43f66a62005-03-25 12:31:53 -06009789 sys_config->dot11g_auto_detection = 0;
Jeff Garzikbf794512005-07-31 13:07:26 -04009790 sys_config->enable_cts_to_self = 0;
James Ketrenos43f66a62005-03-25 12:31:53 -06009791 sys_config->bt_coexist_collision_thr = 0;
James Ketrenosc848d0a2005-08-24 21:56:24 -05009792 sys_config->pass_noise_stats_to_host = 1; //1 -- fix for 256
James Ketrenos43f66a62005-03-25 12:31:53 -06009793}
9794
9795static int ipw_net_open(struct net_device *dev)
9796{
9797 struct ipw_priv *priv = ieee80211_priv(dev);
9798 IPW_DEBUG_INFO("dev->open\n");
9799 /* we should be verifying the device is ready to be opened */
James Ketrenosc848d0a2005-08-24 21:56:24 -05009800 down(&priv->sem);
Jeff Garzikbf794512005-07-31 13:07:26 -04009801 if (!(priv->status & STATUS_RF_KILL_MASK) &&
9802 (priv->status & STATUS_ASSOCIATED))
James Ketrenos43f66a62005-03-25 12:31:53 -06009803 netif_start_queue(dev);
James Ketrenosc848d0a2005-08-24 21:56:24 -05009804 up(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06009805 return 0;
9806}
9807
9808static int ipw_net_stop(struct net_device *dev)
9809{
9810 IPW_DEBUG_INFO("dev->close\n");
9811 netif_stop_queue(dev);
9812 return 0;
9813}
9814
9815/*
9816todo:
9817
9818modify to send one tfd per fragment instead of using chunking. otherwise
9819we need to heavily modify the ieee80211_skb_to_txb.
9820*/
9821
James Ketrenos227d2dc2005-07-28 16:25:55 -05009822static inline int ipw_tx_skb(struct ipw_priv *priv, struct ieee80211_txb *txb,
9823 int pri)
James Ketrenos43f66a62005-03-25 12:31:53 -06009824{
James Ketrenos0dacca12005-09-21 12:23:41 -05009825 struct ieee80211_hdr_3addr *hdr = (struct ieee80211_hdr_3addr *)
Jeff Garzik0edd5b42005-09-07 00:48:31 -04009826 txb->fragments[0]->data;
James Ketrenos43f66a62005-03-25 12:31:53 -06009827 int i = 0;
9828 struct tfd_frame *tfd;
James Ketrenosb095c382005-08-24 22:04:42 -05009829#ifdef CONFIG_IPW_QOS
9830 int tx_id = ipw_get_tx_queue_number(priv, pri);
9831 struct clx2_tx_queue *txq = &priv->txq[tx_id];
9832#else
James Ketrenos43f66a62005-03-25 12:31:53 -06009833 struct clx2_tx_queue *txq = &priv->txq[0];
James Ketrenosb095c382005-08-24 22:04:42 -05009834#endif
James Ketrenos43f66a62005-03-25 12:31:53 -06009835 struct clx2_queue *q = &txq->q;
9836 u8 id, hdr_len, unicast;
9837 u16 remaining_bytes;
James Ketrenosc848d0a2005-08-24 21:56:24 -05009838 int fc;
James Ketrenos43f66a62005-03-25 12:31:53 -06009839
James Ketrenos227d2dc2005-07-28 16:25:55 -05009840 /* If there isn't room in the queue, we return busy and let the
9841 * network stack requeue the packet for us */
9842 if (ipw_queue_space(q) < q->high_mark)
9843 return NETDEV_TX_BUSY;
9844
James Ketrenos43f66a62005-03-25 12:31:53 -06009845 switch (priv->ieee->iw_mode) {
9846 case IW_MODE_ADHOC:
9847 hdr_len = IEEE80211_3ADDR_LEN;
James Ketrenosafbf30a2005-08-25 00:05:33 -05009848 unicast = !is_multicast_ether_addr(hdr->addr1);
James Ketrenos43f66a62005-03-25 12:31:53 -06009849 id = ipw_find_station(priv, hdr->addr1);
9850 if (id == IPW_INVALID_STATION) {
9851 id = ipw_add_station(priv, hdr->addr1);
9852 if (id == IPW_INVALID_STATION) {
9853 IPW_WARNING("Attempt to send data to "
Jeff Garzikbf794512005-07-31 13:07:26 -04009854 "invalid cell: " MAC_FMT "\n",
James Ketrenos43f66a62005-03-25 12:31:53 -06009855 MAC_ARG(hdr->addr1));
9856 goto drop;
9857 }
9858 }
9859 break;
9860
9861 case IW_MODE_INFRA:
9862 default:
James Ketrenosafbf30a2005-08-25 00:05:33 -05009863 unicast = !is_multicast_ether_addr(hdr->addr3);
James Ketrenos43f66a62005-03-25 12:31:53 -06009864 hdr_len = IEEE80211_3ADDR_LEN;
9865 id = 0;
9866 break;
9867 }
9868
9869 tfd = &txq->bd[q->first_empty];
9870 txq->txb[q->first_empty] = txb;
9871 memset(tfd, 0, sizeof(*tfd));
9872 tfd->u.data.station_number = id;
9873
9874 tfd->control_flags.message_type = TX_FRAME_TYPE;
9875 tfd->control_flags.control_bits = TFD_NEED_IRQ_MASK;
9876
9877 tfd->u.data.cmd_id = DINO_CMD_TX;
James Ketrenosa613bff2005-08-24 21:43:11 -05009878 tfd->u.data.len = cpu_to_le16(txb->payload_size);
James Ketrenos43f66a62005-03-25 12:31:53 -06009879 remaining_bytes = txb->payload_size;
Jeff Garzikbf794512005-07-31 13:07:26 -04009880
James Ketrenos43f66a62005-03-25 12:31:53 -06009881 if (priv->assoc_request.ieee_mode == IPW_B_MODE)
James Ketrenosb095c382005-08-24 22:04:42 -05009882 tfd->u.data.tx_flags_ext |= DCT_FLAG_EXT_MODE_CCK;
James Ketrenos43f66a62005-03-25 12:31:53 -06009883 else
James Ketrenosb095c382005-08-24 22:04:42 -05009884 tfd->u.data.tx_flags_ext |= DCT_FLAG_EXT_MODE_OFDM;
James Ketrenos43f66a62005-03-25 12:31:53 -06009885
James Ketrenosea2b26e2005-08-24 21:25:16 -05009886 if (priv->assoc_request.preamble_length == DCT_FLAG_SHORT_PREAMBLE)
9887 tfd->u.data.tx_flags |= DCT_FLAG_SHORT_PREAMBLE;
James Ketrenos43f66a62005-03-25 12:31:53 -06009888
James Ketrenosc848d0a2005-08-24 21:56:24 -05009889 fc = le16_to_cpu(hdr->frame_ctl);
9890 hdr->frame_ctl = cpu_to_le16(fc & ~IEEE80211_FCTL_MOREFRAGS);
9891
James Ketrenos43f66a62005-03-25 12:31:53 -06009892 memcpy(&tfd->u.data.tfd.tfd_24.mchdr, hdr, hdr_len);
9893
James Ketrenosb095c382005-08-24 22:04:42 -05009894 if (likely(unicast))
9895 tfd->u.data.tx_flags |= DCT_FLAG_ACK_REQD;
9896
9897 if (txb->encrypted && !priv->ieee->host_encrypt) {
9898 switch (priv->ieee->sec.level) {
9899 case SEC_LEVEL_3:
9900 tfd->u.data.tfd.tfd_24.mchdr.frame_ctl |=
9901 IEEE80211_FCTL_PROTECTED;
9902 /* XXX: ACK flag must be set for CCMP even if it
9903 * is a multicast/broadcast packet, because CCMP
9904 * group communication encrypted by GTK is
9905 * actually done by the AP. */
9906 if (!unicast)
9907 tfd->u.data.tx_flags |= DCT_FLAG_ACK_REQD;
9908
9909 tfd->u.data.tx_flags &= ~DCT_FLAG_NO_WEP;
9910 tfd->u.data.tx_flags_ext |= DCT_FLAG_EXT_SECURITY_CCM;
9911 tfd->u.data.key_index = 0;
9912 tfd->u.data.key_index |= DCT_WEP_INDEX_USE_IMMEDIATE;
9913 break;
9914 case SEC_LEVEL_2:
9915 tfd->u.data.tfd.tfd_24.mchdr.frame_ctl |=
9916 IEEE80211_FCTL_PROTECTED;
9917 tfd->u.data.tx_flags &= ~DCT_FLAG_NO_WEP;
9918 tfd->u.data.tx_flags_ext |= DCT_FLAG_EXT_SECURITY_TKIP;
9919 tfd->u.data.key_index = DCT_WEP_INDEX_USE_IMMEDIATE;
9920 break;
9921 case SEC_LEVEL_1:
9922 tfd->u.data.tfd.tfd_24.mchdr.frame_ctl |=
9923 IEEE80211_FCTL_PROTECTED;
9924 tfd->u.data.key_index = priv->ieee->tx_keyidx;
9925 if (priv->ieee->sec.key_sizes[priv->ieee->tx_keyidx] <=
9926 40)
9927 tfd->u.data.key_index |= DCT_WEP_KEY_64Bit;
9928 else
9929 tfd->u.data.key_index |= DCT_WEP_KEY_128Bit;
9930 break;
9931 case SEC_LEVEL_0:
9932 break;
9933 default:
9934 printk(KERN_ERR "Unknow security level %d\n",
9935 priv->ieee->sec.level);
9936 break;
9937 }
9938 } else
9939 /* No hardware encryption */
9940 tfd->u.data.tx_flags |= DCT_FLAG_NO_WEP;
9941
9942#ifdef CONFIG_IPW_QOS
9943 ipw_qos_set_tx_queue_command(priv, pri, &(tfd->u.data), unicast);
9944#endif /* CONFIG_IPW_QOS */
9945
James Ketrenos43f66a62005-03-25 12:31:53 -06009946 /* payload */
James Ketrenosa613bff2005-08-24 21:43:11 -05009947 tfd->u.data.num_chunks = cpu_to_le32(min((u8) (NUM_TFD_CHUNKS - 2),
9948 txb->nr_frags));
9949 IPW_DEBUG_FRAG("%i fragments being sent as %i chunks.\n",
9950 txb->nr_frags, le32_to_cpu(tfd->u.data.num_chunks));
9951 for (i = 0; i < le32_to_cpu(tfd->u.data.num_chunks); i++) {
9952 IPW_DEBUG_FRAG("Adding fragment %i of %i (%d bytes).\n",
9953 i, le32_to_cpu(tfd->u.data.num_chunks),
9954 txb->fragments[i]->len - hdr_len);
Jeff Garzikbf794512005-07-31 13:07:26 -04009955 IPW_DEBUG_TX("Dumping TX packet frag %i of %i (%d bytes):\n",
James Ketrenos43f66a62005-03-25 12:31:53 -06009956 i, tfd->u.data.num_chunks,
9957 txb->fragments[i]->len - hdr_len);
Jeff Garzikbf794512005-07-31 13:07:26 -04009958 printk_buf(IPW_DL_TX, txb->fragments[i]->data + hdr_len,
James Ketrenos43f66a62005-03-25 12:31:53 -06009959 txb->fragments[i]->len - hdr_len);
9960
Jeff Garzik0edd5b42005-09-07 00:48:31 -04009961 tfd->u.data.chunk_ptr[i] =
James Ketrenosa613bff2005-08-24 21:43:11 -05009962 cpu_to_le32(pci_map_single
9963 (priv->pci_dev,
9964 txb->fragments[i]->data + hdr_len,
9965 txb->fragments[i]->len - hdr_len,
9966 PCI_DMA_TODEVICE));
9967 tfd->u.data.chunk_len[i] =
9968 cpu_to_le16(txb->fragments[i]->len - hdr_len);
James Ketrenos43f66a62005-03-25 12:31:53 -06009969 }
9970
9971 if (i != txb->nr_frags) {
9972 struct sk_buff *skb;
9973 u16 remaining_bytes = 0;
9974 int j;
9975
9976 for (j = i; j < txb->nr_frags; j++)
9977 remaining_bytes += txb->fragments[j]->len - hdr_len;
9978
9979 printk(KERN_INFO "Trying to reallocate for %d bytes\n",
9980 remaining_bytes);
9981 skb = alloc_skb(remaining_bytes, GFP_ATOMIC);
9982 if (skb != NULL) {
James Ketrenosa613bff2005-08-24 21:43:11 -05009983 tfd->u.data.chunk_len[i] = cpu_to_le16(remaining_bytes);
James Ketrenos43f66a62005-03-25 12:31:53 -06009984 for (j = i; j < txb->nr_frags; j++) {
9985 int size = txb->fragments[j]->len - hdr_len;
James Ketrenosafbf30a2005-08-25 00:05:33 -05009986
James Ketrenos43f66a62005-03-25 12:31:53 -06009987 printk(KERN_INFO "Adding frag %d %d...\n",
Jeff Garzik0edd5b42005-09-07 00:48:31 -04009988 j, size);
James Ketrenos43f66a62005-03-25 12:31:53 -06009989 memcpy(skb_put(skb, size),
Jeff Garzik0edd5b42005-09-07 00:48:31 -04009990 txb->fragments[j]->data + hdr_len, size);
James Ketrenos43f66a62005-03-25 12:31:53 -06009991 }
9992 dev_kfree_skb_any(txb->fragments[i]);
9993 txb->fragments[i] = skb;
Jeff Garzik0edd5b42005-09-07 00:48:31 -04009994 tfd->u.data.chunk_ptr[i] =
James Ketrenosa613bff2005-08-24 21:43:11 -05009995 cpu_to_le32(pci_map_single
9996 (priv->pci_dev, skb->data,
9997 tfd->u.data.chunk_len[i],
9998 PCI_DMA_TODEVICE));
9999
10000 tfd->u.data.num_chunks =
10001 cpu_to_le32(le32_to_cpu(tfd->u.data.num_chunks) +
10002 1);
Jeff Garzikbf794512005-07-31 13:07:26 -040010003 }
James Ketrenos43f66a62005-03-25 12:31:53 -060010004 }
10005
10006 /* kick DMA */
10007 q->first_empty = ipw_queue_inc_wrap(q->first_empty, q->n_bd);
10008 ipw_write32(priv, q->reg_w, q->first_empty);
10009
James Ketrenos227d2dc2005-07-28 16:25:55 -050010010 return NETDEV_TX_OK;
James Ketrenos43f66a62005-03-25 12:31:53 -060010011
Jeff Garzik0edd5b42005-09-07 00:48:31 -040010012 drop:
James Ketrenos43f66a62005-03-25 12:31:53 -060010013 IPW_DEBUG_DROP("Silently dropping Tx packet.\n");
10014 ieee80211_txb_free(txb);
James Ketrenos227d2dc2005-07-28 16:25:55 -050010015 return NETDEV_TX_OK;
10016}
10017
10018static int ipw_net_is_queue_full(struct net_device *dev, int pri)
10019{
10020 struct ipw_priv *priv = ieee80211_priv(dev);
10021#ifdef CONFIG_IPW_QOS
10022 int tx_id = ipw_get_tx_queue_number(priv, pri);
10023 struct clx2_tx_queue *txq = &priv->txq[tx_id];
10024#else
10025 struct clx2_tx_queue *txq = &priv->txq[0];
10026#endif /* CONFIG_IPW_QOS */
10027
10028 if (ipw_queue_space(&txq->q) < txq->q.high_mark)
10029 return 1;
10030
10031 return 0;
James Ketrenos43f66a62005-03-25 12:31:53 -060010032}
10033
10034static int ipw_net_hard_start_xmit(struct ieee80211_txb *txb,
James Ketrenosc8d42d12005-09-21 12:23:43 -050010035 struct net_device *dev, int pri)
James Ketrenos43f66a62005-03-25 12:31:53 -060010036{
10037 struct ipw_priv *priv = ieee80211_priv(dev);
10038 unsigned long flags;
James Ketrenos227d2dc2005-07-28 16:25:55 -050010039 int ret;
James Ketrenos43f66a62005-03-25 12:31:53 -060010040
10041 IPW_DEBUG_TX("dev->xmit(%d bytes)\n", txb->payload_size);
James Ketrenos43f66a62005-03-25 12:31:53 -060010042 spin_lock_irqsave(&priv->lock, flags);
10043
10044 if (!(priv->status & STATUS_ASSOCIATED)) {
10045 IPW_DEBUG_INFO("Tx attempt while not associated.\n");
10046 priv->ieee->stats.tx_carrier_errors++;
10047 netif_stop_queue(dev);
10048 goto fail_unlock;
10049 }
10050
James Ketrenos227d2dc2005-07-28 16:25:55 -050010051 ret = ipw_tx_skb(priv, txb, pri);
10052 if (ret == NETDEV_TX_OK)
10053 __ipw_led_activity_on(priv);
James Ketrenosc848d0a2005-08-24 21:56:24 -050010054 spin_unlock_irqrestore(&priv->lock, flags);
James Ketrenos43f66a62005-03-25 12:31:53 -060010055
James Ketrenos227d2dc2005-07-28 16:25:55 -050010056 return ret;
James Ketrenos43f66a62005-03-25 12:31:53 -060010057
Jeff Garzik0edd5b42005-09-07 00:48:31 -040010058 fail_unlock:
James Ketrenos43f66a62005-03-25 12:31:53 -060010059 spin_unlock_irqrestore(&priv->lock, flags);
10060 return 1;
10061}
10062
10063static struct net_device_stats *ipw_net_get_stats(struct net_device *dev)
10064{
10065 struct ipw_priv *priv = ieee80211_priv(dev);
Jeff Garzikbf794512005-07-31 13:07:26 -040010066
James Ketrenos43f66a62005-03-25 12:31:53 -060010067 priv->ieee->stats.tx_packets = priv->tx_packets;
10068 priv->ieee->stats.rx_packets = priv->rx_packets;
10069 return &priv->ieee->stats;
10070}
10071
10072static void ipw_net_set_multicast_list(struct net_device *dev)
10073{
10074
10075}
10076
10077static int ipw_net_set_mac_address(struct net_device *dev, void *p)
10078{
10079 struct ipw_priv *priv = ieee80211_priv(dev);
10080 struct sockaddr *addr = p;
10081 if (!is_valid_ether_addr(addr->sa_data))
10082 return -EADDRNOTAVAIL;
James Ketrenosc848d0a2005-08-24 21:56:24 -050010083 down(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -060010084 priv->config |= CFG_CUSTOM_MAC;
10085 memcpy(priv->mac_addr, addr->sa_data, ETH_ALEN);
10086 printk(KERN_INFO "%s: Setting MAC to " MAC_FMT "\n",
10087 priv->net_dev->name, MAC_ARG(priv->mac_addr));
James Ketrenosa613bff2005-08-24 21:43:11 -050010088 queue_work(priv->workqueue, &priv->adapter_restart);
James Ketrenosc848d0a2005-08-24 21:56:24 -050010089 up(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -060010090 return 0;
10091}
10092
Jeff Garzikbf794512005-07-31 13:07:26 -040010093static void ipw_ethtool_get_drvinfo(struct net_device *dev,
James Ketrenos43f66a62005-03-25 12:31:53 -060010094 struct ethtool_drvinfo *info)
10095{
10096 struct ipw_priv *p = ieee80211_priv(dev);
10097 char vers[64];
10098 char date[32];
10099 u32 len;
10100
10101 strcpy(info->driver, DRV_NAME);
10102 strcpy(info->version, DRV_VERSION);
10103
10104 len = sizeof(vers);
10105 ipw_get_ordinal(p, IPW_ORD_STAT_FW_VERSION, vers, &len);
10106 len = sizeof(date);
10107 ipw_get_ordinal(p, IPW_ORD_STAT_FW_DATE, date, &len);
10108
Jeff Garzik0edd5b42005-09-07 00:48:31 -040010109 snprintf(info->fw_version, sizeof(info->fw_version), "%s (%s)",
James Ketrenos43f66a62005-03-25 12:31:53 -060010110 vers, date);
10111 strcpy(info->bus_info, pci_name(p->pci_dev));
James Ketrenosb095c382005-08-24 22:04:42 -050010112 info->eedump_len = IPW_EEPROM_IMAGE_SIZE;
James Ketrenos43f66a62005-03-25 12:31:53 -060010113}
10114
10115static u32 ipw_ethtool_get_link(struct net_device *dev)
10116{
10117 struct ipw_priv *priv = ieee80211_priv(dev);
10118 return (priv->status & STATUS_ASSOCIATED) != 0;
10119}
10120
10121static int ipw_ethtool_get_eeprom_len(struct net_device *dev)
10122{
James Ketrenosb095c382005-08-24 22:04:42 -050010123 return IPW_EEPROM_IMAGE_SIZE;
James Ketrenos43f66a62005-03-25 12:31:53 -060010124}
10125
10126static int ipw_ethtool_get_eeprom(struct net_device *dev,
Jeff Garzik0edd5b42005-09-07 00:48:31 -040010127 struct ethtool_eeprom *eeprom, u8 * bytes)
James Ketrenos43f66a62005-03-25 12:31:53 -060010128{
10129 struct ipw_priv *p = ieee80211_priv(dev);
10130
James Ketrenosb095c382005-08-24 22:04:42 -050010131 if (eeprom->offset + eeprom->len > IPW_EEPROM_IMAGE_SIZE)
James Ketrenos43f66a62005-03-25 12:31:53 -060010132 return -EINVAL;
James Ketrenosc848d0a2005-08-24 21:56:24 -050010133 down(&p->sem);
James Ketrenosafbf30a2005-08-25 00:05:33 -050010134 memcpy(bytes, &p->eeprom[eeprom->offset], eeprom->len);
James Ketrenosc848d0a2005-08-24 21:56:24 -050010135 up(&p->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -060010136 return 0;
10137}
10138
10139static int ipw_ethtool_set_eeprom(struct net_device *dev,
Jeff Garzik0edd5b42005-09-07 00:48:31 -040010140 struct ethtool_eeprom *eeprom, u8 * bytes)
James Ketrenos43f66a62005-03-25 12:31:53 -060010141{
10142 struct ipw_priv *p = ieee80211_priv(dev);
10143 int i;
10144
James Ketrenosb095c382005-08-24 22:04:42 -050010145 if (eeprom->offset + eeprom->len > IPW_EEPROM_IMAGE_SIZE)
James Ketrenos43f66a62005-03-25 12:31:53 -060010146 return -EINVAL;
James Ketrenosc848d0a2005-08-24 21:56:24 -050010147 down(&p->sem);
James Ketrenosafbf30a2005-08-25 00:05:33 -050010148 memcpy(&p->eeprom[eeprom->offset], bytes, eeprom->len);
Jeff Garzikbf794512005-07-31 13:07:26 -040010149 for (i = IPW_EEPROM_DATA;
James Ketrenosb095c382005-08-24 22:04:42 -050010150 i < IPW_EEPROM_DATA + IPW_EEPROM_IMAGE_SIZE; i++)
James Ketrenos43f66a62005-03-25 12:31:53 -060010151 ipw_write8(p, i, p->eeprom[i]);
James Ketrenosc848d0a2005-08-24 21:56:24 -050010152 up(&p->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -060010153 return 0;
10154}
10155
10156static struct ethtool_ops ipw_ethtool_ops = {
James Ketrenosea2b26e2005-08-24 21:25:16 -050010157 .get_link = ipw_ethtool_get_link,
10158 .get_drvinfo = ipw_ethtool_get_drvinfo,
10159 .get_eeprom_len = ipw_ethtool_get_eeprom_len,
10160 .get_eeprom = ipw_ethtool_get_eeprom,
10161 .set_eeprom = ipw_ethtool_set_eeprom,
James Ketrenos43f66a62005-03-25 12:31:53 -060010162};
10163
10164static irqreturn_t ipw_isr(int irq, void *data, struct pt_regs *regs)
10165{
10166 struct ipw_priv *priv = data;
10167 u32 inta, inta_mask;
Jeff Garzikbf794512005-07-31 13:07:26 -040010168
James Ketrenos43f66a62005-03-25 12:31:53 -060010169 if (!priv)
10170 return IRQ_NONE;
10171
10172 spin_lock(&priv->lock);
10173
10174 if (!(priv->status & STATUS_INT_ENABLED)) {
10175 /* Shared IRQ */
10176 goto none;
10177 }
10178
James Ketrenosb095c382005-08-24 22:04:42 -050010179 inta = ipw_read32(priv, IPW_INTA_RW);
10180 inta_mask = ipw_read32(priv, IPW_INTA_MASK_R);
Jeff Garzikbf794512005-07-31 13:07:26 -040010181
James Ketrenos43f66a62005-03-25 12:31:53 -060010182 if (inta == 0xFFFFFFFF) {
10183 /* Hardware disappeared */
10184 IPW_WARNING("IRQ INTA == 0xFFFFFFFF\n");
10185 goto none;
10186 }
10187
James Ketrenosb095c382005-08-24 22:04:42 -050010188 if (!(inta & (IPW_INTA_MASK_ALL & inta_mask))) {
James Ketrenos43f66a62005-03-25 12:31:53 -060010189 /* Shared interrupt */
10190 goto none;
10191 }
10192
10193 /* tell the device to stop sending interrupts */
10194 ipw_disable_interrupts(priv);
Jeff Garzikbf794512005-07-31 13:07:26 -040010195
James Ketrenos43f66a62005-03-25 12:31:53 -060010196 /* ack current interrupts */
James Ketrenosb095c382005-08-24 22:04:42 -050010197 inta &= (IPW_INTA_MASK_ALL & inta_mask);
10198 ipw_write32(priv, IPW_INTA_RW, inta);
Jeff Garzikbf794512005-07-31 13:07:26 -040010199
James Ketrenos43f66a62005-03-25 12:31:53 -060010200 /* Cache INTA value for our tasklet */
10201 priv->isr_inta = inta;
10202
10203 tasklet_schedule(&priv->irq_tasklet);
10204
Jeff Garzik0edd5b42005-09-07 00:48:31 -040010205 spin_unlock(&priv->lock);
James Ketrenos43f66a62005-03-25 12:31:53 -060010206
10207 return IRQ_HANDLED;
Jeff Garzik0edd5b42005-09-07 00:48:31 -040010208 none:
James Ketrenos43f66a62005-03-25 12:31:53 -060010209 spin_unlock(&priv->lock);
10210 return IRQ_NONE;
10211}
10212
10213static void ipw_rf_kill(void *adapter)
10214{
10215 struct ipw_priv *priv = adapter;
10216 unsigned long flags;
Jeff Garzikbf794512005-07-31 13:07:26 -040010217
James Ketrenos43f66a62005-03-25 12:31:53 -060010218 spin_lock_irqsave(&priv->lock, flags);
10219
10220 if (rf_kill_active(priv)) {
10221 IPW_DEBUG_RF_KILL("RF Kill active, rescheduling GPIO check\n");
10222 if (priv->workqueue)
10223 queue_delayed_work(priv->workqueue,
10224 &priv->rf_kill, 2 * HZ);
10225 goto exit_unlock;
10226 }
10227
10228 /* RF Kill is now disabled, so bring the device back up */
10229
10230 if (!(priv->status & STATUS_RF_KILL_MASK)) {
10231 IPW_DEBUG_RF_KILL("HW RF Kill no longer active, restarting "
10232 "device\n");
10233
10234 /* we can not do an adapter restart while inside an irq lock */
10235 queue_work(priv->workqueue, &priv->adapter_restart);
Jeff Garzikbf794512005-07-31 13:07:26 -040010236 } else
James Ketrenos43f66a62005-03-25 12:31:53 -060010237 IPW_DEBUG_RF_KILL("HW RF Kill deactivated. SW RF Kill still "
10238 "enabled\n");
10239
Jeff Garzik0edd5b42005-09-07 00:48:31 -040010240 exit_unlock:
James Ketrenos43f66a62005-03-25 12:31:53 -060010241 spin_unlock_irqrestore(&priv->lock, flags);
10242}
10243
James Ketrenosc848d0a2005-08-24 21:56:24 -050010244static void ipw_bg_rf_kill(void *data)
10245{
10246 struct ipw_priv *priv = data;
10247 down(&priv->sem);
10248 ipw_rf_kill(data);
10249 up(&priv->sem);
10250}
10251
James Ketrenosa613bff2005-08-24 21:43:11 -050010252void ipw_link_up(struct ipw_priv *priv)
10253{
James Ketrenosafbf30a2005-08-25 00:05:33 -050010254 priv->last_seq_num = -1;
10255 priv->last_frag_num = -1;
10256 priv->last_packet_time = 0;
10257
James Ketrenosa613bff2005-08-24 21:43:11 -050010258 netif_carrier_on(priv->net_dev);
10259 if (netif_queue_stopped(priv->net_dev)) {
10260 IPW_DEBUG_NOTIF("waking queue\n");
10261 netif_wake_queue(priv->net_dev);
10262 } else {
10263 IPW_DEBUG_NOTIF("starting queue\n");
10264 netif_start_queue(priv->net_dev);
10265 }
10266
James Ketrenosc848d0a2005-08-24 21:56:24 -050010267 cancel_delayed_work(&priv->request_scan);
James Ketrenosa613bff2005-08-24 21:43:11 -050010268 ipw_reset_stats(priv);
10269 /* Ensure the rate is updated immediately */
10270 priv->last_rate = ipw_get_current_rate(priv);
10271 ipw_gather_stats(priv);
10272 ipw_led_link_up(priv);
10273 notify_wx_assoc_event(priv);
10274
10275 if (priv->config & CFG_BACKGROUND_SCAN)
10276 queue_delayed_work(priv->workqueue, &priv->request_scan, HZ);
10277}
10278
James Ketrenosc848d0a2005-08-24 21:56:24 -050010279static void ipw_bg_link_up(void *data)
10280{
10281 struct ipw_priv *priv = data;
10282 down(&priv->sem);
10283 ipw_link_up(data);
10284 up(&priv->sem);
10285}
10286
James Ketrenosa613bff2005-08-24 21:43:11 -050010287void ipw_link_down(struct ipw_priv *priv)
10288{
10289 ipw_led_link_down(priv);
10290 netif_carrier_off(priv->net_dev);
10291 netif_stop_queue(priv->net_dev);
10292 notify_wx_assoc_event(priv);
10293
10294 /* Cancel any queued work ... */
10295 cancel_delayed_work(&priv->request_scan);
10296 cancel_delayed_work(&priv->adhoc_check);
10297 cancel_delayed_work(&priv->gather_stats);
10298
10299 ipw_reset_stats(priv);
10300
James Ketrenosafbf30a2005-08-25 00:05:33 -050010301 if (!(priv->status & STATUS_EXIT_PENDING)) {
10302 /* Queue up another scan... */
10303 queue_work(priv->workqueue, &priv->request_scan);
10304 }
James Ketrenosa613bff2005-08-24 21:43:11 -050010305}
10306
James Ketrenosc848d0a2005-08-24 21:56:24 -050010307static void ipw_bg_link_down(void *data)
10308{
10309 struct ipw_priv *priv = data;
10310 down(&priv->sem);
10311 ipw_link_down(data);
10312 up(&priv->sem);
10313}
10314
James Ketrenos43f66a62005-03-25 12:31:53 -060010315static int ipw_setup_deferred_work(struct ipw_priv *priv)
10316{
10317 int ret = 0;
10318
James Ketrenos43f66a62005-03-25 12:31:53 -060010319 priv->workqueue = create_workqueue(DRV_NAME);
James Ketrenos43f66a62005-03-25 12:31:53 -060010320 init_waitqueue_head(&priv->wait_command_queue);
James Ketrenosafbf30a2005-08-25 00:05:33 -050010321 init_waitqueue_head(&priv->wait_state);
James Ketrenos43f66a62005-03-25 12:31:53 -060010322
James Ketrenosc848d0a2005-08-24 21:56:24 -050010323 INIT_WORK(&priv->adhoc_check, ipw_bg_adhoc_check, priv);
10324 INIT_WORK(&priv->associate, ipw_bg_associate, priv);
10325 INIT_WORK(&priv->disassociate, ipw_bg_disassociate, priv);
Zhu Yid8bad6d2005-07-13 12:25:38 -050010326 INIT_WORK(&priv->system_config, ipw_system_config, priv);
James Ketrenosc848d0a2005-08-24 21:56:24 -050010327 INIT_WORK(&priv->rx_replenish, ipw_bg_rx_queue_replenish, priv);
10328 INIT_WORK(&priv->adapter_restart, ipw_bg_adapter_restart, priv);
10329 INIT_WORK(&priv->rf_kill, ipw_bg_rf_kill, priv);
10330 INIT_WORK(&priv->up, (void (*)(void *))ipw_bg_up, priv);
10331 INIT_WORK(&priv->down, (void (*)(void *))ipw_bg_down, priv);
Jeff Garzikbf794512005-07-31 13:07:26 -040010332 INIT_WORK(&priv->request_scan,
James Ketrenosb095c382005-08-24 22:04:42 -050010333 (void (*)(void *))ipw_request_scan, priv);
Jeff Garzikbf794512005-07-31 13:07:26 -040010334 INIT_WORK(&priv->gather_stats,
James Ketrenosc848d0a2005-08-24 21:56:24 -050010335 (void (*)(void *))ipw_bg_gather_stats, priv);
10336 INIT_WORK(&priv->abort_scan, (void (*)(void *))ipw_bg_abort_scan, priv);
10337 INIT_WORK(&priv->roam, ipw_bg_roam, priv);
10338 INIT_WORK(&priv->scan_check, ipw_bg_scan_check, priv);
10339 INIT_WORK(&priv->link_up, (void (*)(void *))ipw_bg_link_up, priv);
10340 INIT_WORK(&priv->link_down, (void (*)(void *))ipw_bg_link_down, priv);
10341 INIT_WORK(&priv->led_link_on, (void (*)(void *))ipw_bg_led_link_on,
James Ketrenosa613bff2005-08-24 21:43:11 -050010342 priv);
James Ketrenosc848d0a2005-08-24 21:56:24 -050010343 INIT_WORK(&priv->led_link_off, (void (*)(void *))ipw_bg_led_link_off,
James Ketrenosa613bff2005-08-24 21:43:11 -050010344 priv);
James Ketrenosc848d0a2005-08-24 21:56:24 -050010345 INIT_WORK(&priv->led_act_off, (void (*)(void *))ipw_bg_led_activity_off,
10346 priv);
10347 INIT_WORK(&priv->merge_networks,
10348 (void (*)(void *))ipw_merge_adhoc_network, priv);
James Ketrenos43f66a62005-03-25 12:31:53 -060010349
James Ketrenosb095c382005-08-24 22:04:42 -050010350#ifdef CONFIG_IPW_QOS
10351 INIT_WORK(&priv->qos_activate, (void (*)(void *))ipw_bg_qos_activate,
10352 priv);
10353#endif /* CONFIG_IPW_QOS */
10354
James Ketrenos43f66a62005-03-25 12:31:53 -060010355 tasklet_init(&priv->irq_tasklet, (void (*)(unsigned long))
10356 ipw_irq_tasklet, (unsigned long)priv);
10357
10358 return ret;
10359}
10360
James Ketrenos43f66a62005-03-25 12:31:53 -060010361static void shim__set_security(struct net_device *dev,
10362 struct ieee80211_security *sec)
10363{
10364 struct ipw_priv *priv = ieee80211_priv(dev);
10365 int i;
Jeff Garzikbf794512005-07-31 13:07:26 -040010366 for (i = 0; i < 4; i++) {
James Ketrenos43f66a62005-03-25 12:31:53 -060010367 if (sec->flags & (1 << i)) {
James Ketrenosafbf30a2005-08-25 00:05:33 -050010368 priv->ieee->sec.encode_alg[i] = sec->encode_alg[i];
James Ketrenosb095c382005-08-24 22:04:42 -050010369 priv->ieee->sec.key_sizes[i] = sec->key_sizes[i];
James Ketrenos43f66a62005-03-25 12:31:53 -060010370 if (sec->key_sizes[i] == 0)
James Ketrenosb095c382005-08-24 22:04:42 -050010371 priv->ieee->sec.flags &= ~(1 << i);
10372 else {
10373 memcpy(priv->ieee->sec.keys[i], sec->keys[i],
James Ketrenos43f66a62005-03-25 12:31:53 -060010374 sec->key_sizes[i]);
James Ketrenosb095c382005-08-24 22:04:42 -050010375 priv->ieee->sec.flags |= (1 << i);
10376 }
James Ketrenos43f66a62005-03-25 12:31:53 -060010377 priv->status |= STATUS_SECURITY_UPDATED;
James Ketrenosb095c382005-08-24 22:04:42 -050010378 } else if (sec->level != SEC_LEVEL_1)
10379 priv->ieee->sec.flags &= ~(1 << i);
James Ketrenos43f66a62005-03-25 12:31:53 -060010380 }
10381
James Ketrenosb095c382005-08-24 22:04:42 -050010382 if (sec->flags & SEC_ACTIVE_KEY) {
James Ketrenos43f66a62005-03-25 12:31:53 -060010383 if (sec->active_key <= 3) {
James Ketrenosb095c382005-08-24 22:04:42 -050010384 priv->ieee->sec.active_key = sec->active_key;
10385 priv->ieee->sec.flags |= SEC_ACTIVE_KEY;
Jeff Garzikbf794512005-07-31 13:07:26 -040010386 } else
James Ketrenosb095c382005-08-24 22:04:42 -050010387 priv->ieee->sec.flags &= ~SEC_ACTIVE_KEY;
James Ketrenos43f66a62005-03-25 12:31:53 -060010388 priv->status |= STATUS_SECURITY_UPDATED;
James Ketrenosb095c382005-08-24 22:04:42 -050010389 } else
10390 priv->ieee->sec.flags &= ~SEC_ACTIVE_KEY;
James Ketrenos43f66a62005-03-25 12:31:53 -060010391
10392 if ((sec->flags & SEC_AUTH_MODE) &&
James Ketrenosb095c382005-08-24 22:04:42 -050010393 (priv->ieee->sec.auth_mode != sec->auth_mode)) {
10394 priv->ieee->sec.auth_mode = sec->auth_mode;
10395 priv->ieee->sec.flags |= SEC_AUTH_MODE;
James Ketrenos43f66a62005-03-25 12:31:53 -060010396 if (sec->auth_mode == WLAN_AUTH_SHARED_KEY)
10397 priv->capability |= CAP_SHARED_KEY;
10398 else
10399 priv->capability &= ~CAP_SHARED_KEY;
10400 priv->status |= STATUS_SECURITY_UPDATED;
10401 }
Jeff Garzikbf794512005-07-31 13:07:26 -040010402
James Ketrenosb095c382005-08-24 22:04:42 -050010403 if (sec->flags & SEC_ENABLED && priv->ieee->sec.enabled != sec->enabled) {
10404 priv->ieee->sec.flags |= SEC_ENABLED;
10405 priv->ieee->sec.enabled = sec->enabled;
James Ketrenos43f66a62005-03-25 12:31:53 -060010406 priv->status |= STATUS_SECURITY_UPDATED;
Jeff Garzikbf794512005-07-31 13:07:26 -040010407 if (sec->enabled)
James Ketrenos43f66a62005-03-25 12:31:53 -060010408 priv->capability |= CAP_PRIVACY_ON;
10409 else
10410 priv->capability &= ~CAP_PRIVACY_ON;
10411 }
James Ketrenosafbf30a2005-08-25 00:05:33 -050010412
10413 if (sec->flags & SEC_ENCRYPT)
10414 priv->ieee->sec.encrypt = sec->encrypt;
Jeff Garzikbf794512005-07-31 13:07:26 -040010415
James Ketrenosb095c382005-08-24 22:04:42 -050010416 if (sec->flags & SEC_LEVEL && priv->ieee->sec.level != sec->level) {
10417 priv->ieee->sec.level = sec->level;
10418 priv->ieee->sec.flags |= SEC_LEVEL;
James Ketrenos43f66a62005-03-25 12:31:53 -060010419 priv->status |= STATUS_SECURITY_UPDATED;
Zhu Yid8bad6d2005-07-13 12:25:38 -050010420 }
James Ketrenosb095c382005-08-24 22:04:42 -050010421
Zhu Yi1fbfea52005-08-05 17:22:56 +080010422 if (!priv->ieee->host_encrypt && (sec->flags & SEC_ENCRYPT))
10423 ipw_set_hwcrypto_keys(priv);
10424
Jeff Garzikbf794512005-07-31 13:07:26 -040010425 /* To match current functionality of ipw2100 (which works well w/
10426 * various supplicants, we don't force a disassociate if the
James Ketrenos43f66a62005-03-25 12:31:53 -060010427 * privacy capability changes ... */
10428#if 0
10429 if ((priv->status & (STATUS_ASSOCIATED | STATUS_ASSOCIATING)) &&
Jeff Garzikbf794512005-07-31 13:07:26 -040010430 (((priv->assoc_request.capability &
James Ketrenos43f66a62005-03-25 12:31:53 -060010431 WLAN_CAPABILITY_PRIVACY) && !sec->enabled) ||
Jeff Garzikbf794512005-07-31 13:07:26 -040010432 (!(priv->assoc_request.capability &
Jeff Garzik0edd5b42005-09-07 00:48:31 -040010433 WLAN_CAPABILITY_PRIVACY) && sec->enabled))) {
James Ketrenos43f66a62005-03-25 12:31:53 -060010434 IPW_DEBUG_ASSOC("Disassociating due to capability "
10435 "change.\n");
10436 ipw_disassociate(priv);
10437 }
10438#endif
10439}
10440
Jeff Garzikbf794512005-07-31 13:07:26 -040010441static int init_supported_rates(struct ipw_priv *priv,
James Ketrenos43f66a62005-03-25 12:31:53 -060010442 struct ipw_supported_rates *rates)
10443{
10444 /* TODO: Mask out rates based on priv->rates_mask */
10445
10446 memset(rates, 0, sizeof(*rates));
Jeff Garzik0edd5b42005-09-07 00:48:31 -040010447 /* configure supported rates */
James Ketrenos43f66a62005-03-25 12:31:53 -060010448 switch (priv->ieee->freq_band) {
10449 case IEEE80211_52GHZ_BAND:
10450 rates->ieee_mode = IPW_A_MODE;
10451 rates->purpose = IPW_RATE_CAPABILITIES;
10452 ipw_add_ofdm_scan_rates(rates, IEEE80211_CCK_MODULATION,
10453 IEEE80211_OFDM_DEFAULT_RATES_MASK);
10454 break;
10455
Jeff Garzik0edd5b42005-09-07 00:48:31 -040010456 default: /* Mixed or 2.4Ghz */
James Ketrenos43f66a62005-03-25 12:31:53 -060010457 rates->ieee_mode = IPW_G_MODE;
10458 rates->purpose = IPW_RATE_CAPABILITIES;
10459 ipw_add_cck_scan_rates(rates, IEEE80211_CCK_MODULATION,
10460 IEEE80211_CCK_DEFAULT_RATES_MASK);
10461 if (priv->ieee->modulation & IEEE80211_OFDM_MODULATION) {
10462 ipw_add_ofdm_scan_rates(rates, IEEE80211_CCK_MODULATION,
10463 IEEE80211_OFDM_DEFAULT_RATES_MASK);
10464 }
10465 break;
10466 }
10467
10468 return 0;
10469}
10470
Jeff Garzikbf794512005-07-31 13:07:26 -040010471static int ipw_config(struct ipw_priv *priv)
James Ketrenos43f66a62005-03-25 12:31:53 -060010472{
James Ketrenos43f66a62005-03-25 12:31:53 -060010473 /* This is only called from ipw_up, which resets/reloads the firmware
10474 so, we don't need to first disable the card before we configure
10475 it */
Zhu Yi6de9f7f2005-08-11 14:39:33 +080010476 if (ipw_set_tx_power(priv))
James Ketrenos43f66a62005-03-25 12:31:53 -060010477 goto error;
10478
10479 /* initialize adapter address */
10480 if (ipw_send_adapter_address(priv, priv->net_dev->dev_addr))
10481 goto error;
10482
10483 /* set basic system config settings */
10484 init_sys_config(&priv->sys_config);
James Ketrenosc848d0a2005-08-24 21:56:24 -050010485 if (priv->ieee->iw_mode == IW_MODE_ADHOC)
10486 priv->sys_config.answer_broadcast_ssid_probe = 1;
10487 else
10488 priv->sys_config.answer_broadcast_ssid_probe = 0;
10489
James Ketrenos43f66a62005-03-25 12:31:53 -060010490 if (ipw_send_system_config(priv, &priv->sys_config))
10491 goto error;
10492
Jeff Garzik0edd5b42005-09-07 00:48:31 -040010493 init_supported_rates(priv, &priv->rates);
10494 if (ipw_send_supported_rates(priv, &priv->rates))
James Ketrenos43f66a62005-03-25 12:31:53 -060010495 goto error;
10496
10497 /* Set request-to-send threshold */
10498 if (priv->rts_threshold) {
10499 if (ipw_send_rts_threshold(priv, priv->rts_threshold))
10500 goto error;
10501 }
James Ketrenosb095c382005-08-24 22:04:42 -050010502#ifdef CONFIG_IPW_QOS
10503 IPW_DEBUG_QOS("QoS: call ipw_qos_activate\n");
10504 ipw_qos_activate(priv, NULL);
10505#endif /* CONFIG_IPW_QOS */
James Ketrenos43f66a62005-03-25 12:31:53 -060010506
10507 if (ipw_set_random_seed(priv))
10508 goto error;
Jeff Garzikbf794512005-07-31 13:07:26 -040010509
James Ketrenos43f66a62005-03-25 12:31:53 -060010510 /* final state transition to the RUN state */
10511 if (ipw_send_host_complete(priv))
10512 goto error;
10513
James Ketrenose6666192005-08-12 09:17:04 -050010514 priv->status |= STATUS_INIT;
10515
10516 ipw_led_init(priv);
10517 ipw_led_radio_on(priv);
10518 priv->notif_missed_beacons = 0;
10519
10520 /* Set hardware WEP key if it is configured. */
10521 if ((priv->capability & CAP_PRIVACY_ON) &&
10522 (priv->ieee->sec.level == SEC_LEVEL_1) &&
10523 !(priv->ieee->host_encrypt || priv->ieee->host_decrypt))
10524 ipw_set_hwcrypto_keys(priv);
James Ketrenos43f66a62005-03-25 12:31:53 -060010525
10526 return 0;
Jeff Garzikbf794512005-07-31 13:07:26 -040010527
Jeff Garzik0edd5b42005-09-07 00:48:31 -040010528 error:
James Ketrenos43f66a62005-03-25 12:31:53 -060010529 return -EIO;
10530}
10531
James Ketrenos4f36f802005-08-03 20:36:56 -050010532/*
10533 * NOTE:
10534 *
10535 * These tables have been tested in conjunction with the
10536 * Intel PRO/Wireless 2200BG and 2915ABG Network Connection Adapters.
10537 *
10538 * Altering this values, using it on other hardware, or in geographies
10539 * not intended for resale of the above mentioned Intel adapters has
10540 * not been tested.
10541 *
10542 */
10543static const struct ieee80211_geo ipw_geos[] = {
10544 { /* Restricted */
10545 "---",
10546 .bg_channels = 11,
10547 .bg = {{2412, 1}, {2417, 2}, {2422, 3},
10548 {2427, 4}, {2432, 5}, {2437, 6},
10549 {2442, 7}, {2447, 8}, {2452, 9},
10550 {2457, 10}, {2462, 11}},
10551 },
10552
10553 { /* Custom US/Canada */
10554 "ZZF",
10555 .bg_channels = 11,
10556 .bg = {{2412, 1}, {2417, 2}, {2422, 3},
10557 {2427, 4}, {2432, 5}, {2437, 6},
10558 {2442, 7}, {2447, 8}, {2452, 9},
10559 {2457, 10}, {2462, 11}},
10560 .a_channels = 8,
10561 .a = {{5180, 36},
10562 {5200, 40},
10563 {5220, 44},
10564 {5240, 48},
10565 {5260, 52, IEEE80211_CH_PASSIVE_ONLY},
10566 {5280, 56, IEEE80211_CH_PASSIVE_ONLY},
10567 {5300, 60, IEEE80211_CH_PASSIVE_ONLY},
10568 {5320, 64, IEEE80211_CH_PASSIVE_ONLY}},
10569 },
10570
10571 { /* Rest of World */
10572 "ZZD",
10573 .bg_channels = 13,
10574 .bg = {{2412, 1}, {2417, 2}, {2422, 3},
10575 {2427, 4}, {2432, 5}, {2437, 6},
10576 {2442, 7}, {2447, 8}, {2452, 9},
10577 {2457, 10}, {2462, 11}, {2467, 12},
10578 {2472, 13}},
10579 },
10580
10581 { /* Custom USA & Europe & High */
10582 "ZZA",
10583 .bg_channels = 11,
10584 .bg = {{2412, 1}, {2417, 2}, {2422, 3},
10585 {2427, 4}, {2432, 5}, {2437, 6},
10586 {2442, 7}, {2447, 8}, {2452, 9},
10587 {2457, 10}, {2462, 11}},
10588 .a_channels = 13,
10589 .a = {{5180, 36},
10590 {5200, 40},
10591 {5220, 44},
10592 {5240, 48},
10593 {5260, 52, IEEE80211_CH_PASSIVE_ONLY},
10594 {5280, 56, IEEE80211_CH_PASSIVE_ONLY},
10595 {5300, 60, IEEE80211_CH_PASSIVE_ONLY},
10596 {5320, 64, IEEE80211_CH_PASSIVE_ONLY},
10597 {5745, 149},
10598 {5765, 153},
10599 {5785, 157},
10600 {5805, 161},
10601 {5825, 165}},
10602 },
10603
10604 { /* Custom NA & Europe */
10605 "ZZB",
10606 .bg_channels = 11,
10607 .bg = {{2412, 1}, {2417, 2}, {2422, 3},
10608 {2427, 4}, {2432, 5}, {2437, 6},
10609 {2442, 7}, {2447, 8}, {2452, 9},
10610 {2457, 10}, {2462, 11}},
10611 .a_channels = 13,
10612 .a = {{5180, 36},
10613 {5200, 40},
10614 {5220, 44},
10615 {5240, 48},
10616 {5260, 52, IEEE80211_CH_PASSIVE_ONLY},
10617 {5280, 56, IEEE80211_CH_PASSIVE_ONLY},
10618 {5300, 60, IEEE80211_CH_PASSIVE_ONLY},
10619 {5320, 64, IEEE80211_CH_PASSIVE_ONLY},
10620 {5745, 149, IEEE80211_CH_PASSIVE_ONLY},
10621 {5765, 153, IEEE80211_CH_PASSIVE_ONLY},
10622 {5785, 157, IEEE80211_CH_PASSIVE_ONLY},
10623 {5805, 161, IEEE80211_CH_PASSIVE_ONLY},
10624 {5825, 165, IEEE80211_CH_PASSIVE_ONLY}},
10625 },
10626
10627 { /* Custom Japan */
10628 "ZZC",
10629 .bg_channels = 11,
10630 .bg = {{2412, 1}, {2417, 2}, {2422, 3},
10631 {2427, 4}, {2432, 5}, {2437, 6},
10632 {2442, 7}, {2447, 8}, {2452, 9},
10633 {2457, 10}, {2462, 11}},
10634 .a_channels = 4,
10635 .a = {{5170, 34}, {5190, 38},
10636 {5210, 42}, {5230, 46}},
10637 },
10638
10639 { /* Custom */
10640 "ZZM",
10641 .bg_channels = 11,
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 },
10647
10648 { /* Europe */
10649 "ZZE",
10650 .bg_channels = 13,
10651 .bg = {{2412, 1}, {2417, 2}, {2422, 3},
10652 {2427, 4}, {2432, 5}, {2437, 6},
10653 {2442, 7}, {2447, 8}, {2452, 9},
10654 {2457, 10}, {2462, 11}, {2467, 12},
10655 {2472, 13}},
10656 .a_channels = 19,
10657 .a = {{5180, 36},
10658 {5200, 40},
10659 {5220, 44},
10660 {5240, 48},
10661 {5260, 52, IEEE80211_CH_PASSIVE_ONLY},
10662 {5280, 56, IEEE80211_CH_PASSIVE_ONLY},
10663 {5300, 60, IEEE80211_CH_PASSIVE_ONLY},
10664 {5320, 64, IEEE80211_CH_PASSIVE_ONLY},
10665 {5500, 100, IEEE80211_CH_PASSIVE_ONLY},
10666 {5520, 104, IEEE80211_CH_PASSIVE_ONLY},
10667 {5540, 108, IEEE80211_CH_PASSIVE_ONLY},
10668 {5560, 112, IEEE80211_CH_PASSIVE_ONLY},
10669 {5580, 116, IEEE80211_CH_PASSIVE_ONLY},
10670 {5600, 120, IEEE80211_CH_PASSIVE_ONLY},
10671 {5620, 124, IEEE80211_CH_PASSIVE_ONLY},
10672 {5640, 128, IEEE80211_CH_PASSIVE_ONLY},
10673 {5660, 132, IEEE80211_CH_PASSIVE_ONLY},
10674 {5680, 136, IEEE80211_CH_PASSIVE_ONLY},
10675 {5700, 140, IEEE80211_CH_PASSIVE_ONLY}},
10676 },
10677
10678 { /* Custom Japan */
10679 "ZZJ",
10680 .bg_channels = 14,
10681 .bg = {{2412, 1}, {2417, 2}, {2422, 3},
10682 {2427, 4}, {2432, 5}, {2437, 6},
10683 {2442, 7}, {2447, 8}, {2452, 9},
10684 {2457, 10}, {2462, 11}, {2467, 12},
10685 {2472, 13}, {2484, 14, IEEE80211_CH_B_ONLY}},
10686 .a_channels = 4,
10687 .a = {{5170, 34}, {5190, 38},
10688 {5210, 42}, {5230, 46}},
10689 },
10690
10691 { /* High Band */
10692 "ZZH",
10693 .bg_channels = 13,
10694 .bg = {{2412, 1}, {2417, 2}, {2422, 3},
10695 {2427, 4}, {2432, 5}, {2437, 6},
10696 {2442, 7}, {2447, 8}, {2452, 9},
10697 {2457, 10}, {2462, 11},
10698 {2467, 12, IEEE80211_CH_PASSIVE_ONLY},
10699 {2472, 13, IEEE80211_CH_PASSIVE_ONLY}},
10700 .a_channels = 4,
10701 .a = {{5745, 149}, {5765, 153},
10702 {5785, 157}, {5805, 161}},
10703 },
10704
10705 { /* Custom Europe */
10706 "ZZG",
10707 .bg_channels = 13,
10708 .bg = {{2412, 1}, {2417, 2}, {2422, 3},
10709 {2427, 4}, {2432, 5}, {2437, 6},
10710 {2442, 7}, {2447, 8}, {2452, 9},
10711 {2457, 10}, {2462, 11},
10712 {2467, 12}, {2472, 13}},
10713 .a_channels = 4,
10714 .a = {{5180, 36}, {5200, 40},
10715 {5220, 44}, {5240, 48}},
10716 },
10717
10718 { /* Europe */
10719 "ZZK",
10720 .bg_channels = 13,
10721 .bg = {{2412, 1}, {2417, 2}, {2422, 3},
10722 {2427, 4}, {2432, 5}, {2437, 6},
10723 {2442, 7}, {2447, 8}, {2452, 9},
10724 {2457, 10}, {2462, 11},
10725 {2467, 12, IEEE80211_CH_PASSIVE_ONLY},
10726 {2472, 13, IEEE80211_CH_PASSIVE_ONLY}},
10727 .a_channels = 24,
10728 .a = {{5180, 36, IEEE80211_CH_PASSIVE_ONLY},
10729 {5200, 40, IEEE80211_CH_PASSIVE_ONLY},
10730 {5220, 44, IEEE80211_CH_PASSIVE_ONLY},
10731 {5240, 48, IEEE80211_CH_PASSIVE_ONLY},
10732 {5260, 52, IEEE80211_CH_PASSIVE_ONLY},
10733 {5280, 56, IEEE80211_CH_PASSIVE_ONLY},
10734 {5300, 60, IEEE80211_CH_PASSIVE_ONLY},
10735 {5320, 64, IEEE80211_CH_PASSIVE_ONLY},
10736 {5500, 100, IEEE80211_CH_PASSIVE_ONLY},
10737 {5520, 104, IEEE80211_CH_PASSIVE_ONLY},
10738 {5540, 108, IEEE80211_CH_PASSIVE_ONLY},
10739 {5560, 112, IEEE80211_CH_PASSIVE_ONLY},
10740 {5580, 116, IEEE80211_CH_PASSIVE_ONLY},
10741 {5600, 120, IEEE80211_CH_PASSIVE_ONLY},
10742 {5620, 124, IEEE80211_CH_PASSIVE_ONLY},
10743 {5640, 128, IEEE80211_CH_PASSIVE_ONLY},
10744 {5660, 132, IEEE80211_CH_PASSIVE_ONLY},
10745 {5680, 136, IEEE80211_CH_PASSIVE_ONLY},
10746 {5700, 140, IEEE80211_CH_PASSIVE_ONLY},
10747 {5745, 149, IEEE80211_CH_PASSIVE_ONLY},
10748 {5765, 153, IEEE80211_CH_PASSIVE_ONLY},
10749 {5785, 157, IEEE80211_CH_PASSIVE_ONLY},
10750 {5805, 161, IEEE80211_CH_PASSIVE_ONLY},
10751 {5825, 165, IEEE80211_CH_PASSIVE_ONLY}},
10752 },
10753
10754 { /* Europe */
10755 "ZZL",
10756 .bg_channels = 11,
10757 .bg = {{2412, 1}, {2417, 2}, {2422, 3},
10758 {2427, 4}, {2432, 5}, {2437, 6},
10759 {2442, 7}, {2447, 8}, {2452, 9},
10760 {2457, 10}, {2462, 11}},
10761 .a_channels = 13,
10762 .a = {{5180, 36, IEEE80211_CH_PASSIVE_ONLY},
10763 {5200, 40, IEEE80211_CH_PASSIVE_ONLY},
10764 {5220, 44, IEEE80211_CH_PASSIVE_ONLY},
10765 {5240, 48, IEEE80211_CH_PASSIVE_ONLY},
10766 {5260, 52, IEEE80211_CH_PASSIVE_ONLY},
10767 {5280, 56, IEEE80211_CH_PASSIVE_ONLY},
10768 {5300, 60, IEEE80211_CH_PASSIVE_ONLY},
10769 {5320, 64, IEEE80211_CH_PASSIVE_ONLY},
10770 {5745, 149, IEEE80211_CH_PASSIVE_ONLY},
10771 {5765, 153, IEEE80211_CH_PASSIVE_ONLY},
10772 {5785, 157, IEEE80211_CH_PASSIVE_ONLY},
10773 {5805, 161, IEEE80211_CH_PASSIVE_ONLY},
10774 {5825, 165, IEEE80211_CH_PASSIVE_ONLY}},
10775 }
James Ketrenosafbf30a2005-08-25 00:05:33 -050010776};
10777
James Ketrenos43f66a62005-03-25 12:31:53 -060010778#define MAX_HW_RESTARTS 5
10779static int ipw_up(struct ipw_priv *priv)
10780{
James Ketrenos4f36f802005-08-03 20:36:56 -050010781 int rc, i, j;
James Ketrenos43f66a62005-03-25 12:31:53 -060010782
10783 if (priv->status & STATUS_EXIT_PENDING)
10784 return -EIO;
10785
James Ketrenosf6c5cb72005-08-25 00:39:09 -050010786 if (cmdlog && !priv->cmdlog) {
10787 priv->cmdlog = kmalloc(sizeof(*priv->cmdlog) * cmdlog,
10788 GFP_KERNEL);
10789 if (priv->cmdlog == NULL) {
10790 IPW_ERROR("Error allocating %d command log entries.\n",
10791 cmdlog);
10792 } else {
10793 memset(priv->cmdlog, 0, sizeof(*priv->cmdlog) * cmdlog);
10794 priv->cmdlog_len = cmdlog;
10795 }
10796 }
10797
Jeff Garzik0edd5b42005-09-07 00:48:31 -040010798 for (i = 0; i < MAX_HW_RESTARTS; i++) {
Jeff Garzikbf794512005-07-31 13:07:26 -040010799 /* Load the microcode, firmware, and eeprom.
James Ketrenos43f66a62005-03-25 12:31:53 -060010800 * Also start the clocks. */
10801 rc = ipw_load(priv);
10802 if (rc) {
Jeff Garzik0edd5b42005-09-07 00:48:31 -040010803 IPW_ERROR("Unable to load firmware: 0x%08X\n", rc);
James Ketrenos43f66a62005-03-25 12:31:53 -060010804 return rc;
10805 }
10806
10807 ipw_init_ordinals(priv);
10808 if (!(priv->config & CFG_CUSTOM_MAC))
10809 eeprom_parse_mac(priv, priv->mac_addr);
10810 memcpy(priv->net_dev->dev_addr, priv->mac_addr, ETH_ALEN);
10811
James Ketrenos4f36f802005-08-03 20:36:56 -050010812 for (j = 0; j < ARRAY_SIZE(ipw_geos); j++) {
10813 if (!memcmp(&priv->eeprom[EEPROM_COUNTRY_CODE],
10814 ipw_geos[j].name, 3))
10815 break;
10816 }
10817 if (j == ARRAY_SIZE(ipw_geos))
10818 j = 0;
10819 if (ieee80211_set_geo(priv->ieee, &ipw_geos[j])) {
10820 IPW_WARNING("Could not set geography.");
10821 return 0;
10822 }
10823
10824 IPW_DEBUG_INFO("Geography %03d [%s] detected.\n",
10825 j, priv->ieee->geo.name);
James Ketrenosafbf30a2005-08-25 00:05:33 -050010826
James Ketrenosb095c382005-08-24 22:04:42 -050010827 if (priv->status & STATUS_RF_KILL_SW) {
10828 IPW_WARNING("Radio disabled by module parameter.\n");
10829 return 0;
10830 } else if (rf_kill_active(priv)) {
10831 IPW_WARNING("Radio Frequency Kill Switch is On:\n"
10832 "Kill switch must be turned off for "
10833 "wireless networking to work.\n");
10834 queue_delayed_work(priv->workqueue, &priv->rf_kill,
10835 2 * HZ);
James Ketrenos43f66a62005-03-25 12:31:53 -060010836 return 0;
James Ketrenosc848d0a2005-08-24 21:56:24 -050010837 }
James Ketrenos43f66a62005-03-25 12:31:53 -060010838
10839 rc = ipw_config(priv);
10840 if (!rc) {
10841 IPW_DEBUG_INFO("Configured device on count %i\n", i);
James Ketrenosafbf30a2005-08-25 00:05:33 -050010842
James Ketrenose6666192005-08-12 09:17:04 -050010843 /* If configure to try and auto-associate, kick
10844 * off a scan. */
10845 queue_work(priv->workqueue, &priv->request_scan);
James Ketrenosafbf30a2005-08-25 00:05:33 -050010846
James Ketrenos43f66a62005-03-25 12:31:53 -060010847 return 0;
James Ketrenos43f66a62005-03-25 12:31:53 -060010848 }
Jeff Garzikbf794512005-07-31 13:07:26 -040010849
James Ketrenosc848d0a2005-08-24 21:56:24 -050010850 IPW_DEBUG_INFO("Device configuration failed: 0x%08X\n", rc);
James Ketrenos43f66a62005-03-25 12:31:53 -060010851 IPW_DEBUG_INFO("Failed to config device on retry %d of %d\n",
10852 i, MAX_HW_RESTARTS);
10853
10854 /* We had an error bringing up the hardware, so take it
10855 * all the way back down so we can try again */
10856 ipw_down(priv);
10857 }
10858
Jeff Garzikbf794512005-07-31 13:07:26 -040010859 /* tried to restart and config the device for as long as our
James Ketrenos43f66a62005-03-25 12:31:53 -060010860 * patience could withstand */
Jeff Garzik0edd5b42005-09-07 00:48:31 -040010861 IPW_ERROR("Unable to initialize device after %d attempts.\n", i);
James Ketrenosc848d0a2005-08-24 21:56:24 -050010862
James Ketrenos43f66a62005-03-25 12:31:53 -060010863 return -EIO;
10864}
10865
James Ketrenosc848d0a2005-08-24 21:56:24 -050010866static void ipw_bg_up(void *data)
10867{
10868 struct ipw_priv *priv = data;
10869 down(&priv->sem);
10870 ipw_up(data);
10871 up(&priv->sem);
10872}
10873
James Ketrenosb095c382005-08-24 22:04:42 -050010874static void ipw_deinit(struct ipw_priv *priv)
James Ketrenos43f66a62005-03-25 12:31:53 -060010875{
James Ketrenosb095c382005-08-24 22:04:42 -050010876 int i;
10877
10878 if (priv->status & STATUS_SCANNING) {
10879 IPW_DEBUG_INFO("Aborting scan during shutdown.\n");
10880 ipw_abort_scan(priv);
10881 }
10882
10883 if (priv->status & STATUS_ASSOCIATED) {
10884 IPW_DEBUG_INFO("Disassociating during shutdown.\n");
10885 ipw_disassociate(priv);
10886 }
10887
10888 ipw_led_shutdown(priv);
10889
10890 /* Wait up to 1s for status to change to not scanning and not
10891 * associated (disassociation can take a while for a ful 802.11
10892 * exchange */
10893 for (i = 1000; i && (priv->status &
10894 (STATUS_DISASSOCIATING |
10895 STATUS_ASSOCIATED | STATUS_SCANNING)); i--)
10896 udelay(10);
10897
10898 if (priv->status & (STATUS_DISASSOCIATING |
10899 STATUS_ASSOCIATED | STATUS_SCANNING))
10900 IPW_DEBUG_INFO("Still associated or scanning...\n");
10901 else
10902 IPW_DEBUG_INFO("Took %dms to de-init\n", 1000 - i);
10903
James Ketrenosc848d0a2005-08-24 21:56:24 -050010904 /* Attempt to disable the card */
James Ketrenos43f66a62005-03-25 12:31:53 -060010905 ipw_send_card_disable(priv, 0);
James Ketrenosb095c382005-08-24 22:04:42 -050010906
10907 priv->status &= ~STATUS_INIT;
10908}
10909
10910static void ipw_down(struct ipw_priv *priv)
10911{
10912 int exit_pending = priv->status & STATUS_EXIT_PENDING;
10913
10914 priv->status |= STATUS_EXIT_PENDING;
10915
10916 if (ipw_is_init(priv))
10917 ipw_deinit(priv);
10918
10919 /* Wipe out the EXIT_PENDING status bit if we are not actually
10920 * exiting the module */
10921 if (!exit_pending)
10922 priv->status &= ~STATUS_EXIT_PENDING;
James Ketrenos43f66a62005-03-25 12:31:53 -060010923
10924 /* tell the device to stop sending interrupts */
10925 ipw_disable_interrupts(priv);
10926
10927 /* Clear all bits but the RF Kill */
James Ketrenosb095c382005-08-24 22:04:42 -050010928 priv->status &= STATUS_RF_KILL_MASK | STATUS_EXIT_PENDING;
James Ketrenos43f66a62005-03-25 12:31:53 -060010929 netif_carrier_off(priv->net_dev);
10930 netif_stop_queue(priv->net_dev);
10931
10932 ipw_stop_nic(priv);
James Ketrenosa613bff2005-08-24 21:43:11 -050010933
10934 ipw_led_radio_off(priv);
James Ketrenos43f66a62005-03-25 12:31:53 -060010935}
10936
James Ketrenosc848d0a2005-08-24 21:56:24 -050010937static void ipw_bg_down(void *data)
10938{
10939 struct ipw_priv *priv = data;
10940 down(&priv->sem);
10941 ipw_down(data);
10942 up(&priv->sem);
10943}
10944
James Ketrenosafbf30a2005-08-25 00:05:33 -050010945#if WIRELESS_EXT < 18
James Ketrenosea2b26e2005-08-24 21:25:16 -050010946static int ipw_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
10947{
James Ketrenosea2b26e2005-08-24 21:25:16 -050010948 struct iwreq *wrq = (struct iwreq *)rq;
10949 int ret = -1;
10950 switch (cmd) {
10951 case IPW_IOCTL_WPA_SUPPLICANT:
10952 ret = ipw_wpa_supplicant(dev, &wrq->u.data);
10953 return ret;
10954
10955 default:
10956 return -EOPNOTSUPP;
10957 }
10958
James Ketrenosea2b26e2005-08-24 21:25:16 -050010959 return -EOPNOTSUPP;
10960}
James Ketrenosafbf30a2005-08-25 00:05:33 -050010961#endif
James Ketrenosea2b26e2005-08-24 21:25:16 -050010962
James Ketrenos43f66a62005-03-25 12:31:53 -060010963/* Called by register_netdev() */
10964static int ipw_net_init(struct net_device *dev)
10965{
10966 struct ipw_priv *priv = ieee80211_priv(dev);
James Ketrenosc848d0a2005-08-24 21:56:24 -050010967 down(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -060010968
James Ketrenosc848d0a2005-08-24 21:56:24 -050010969 if (ipw_up(priv)) {
10970 up(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -060010971 return -EIO;
James Ketrenosc848d0a2005-08-24 21:56:24 -050010972 }
James Ketrenos43f66a62005-03-25 12:31:53 -060010973
James Ketrenosc848d0a2005-08-24 21:56:24 -050010974 up(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -060010975 return 0;
10976}
10977
10978/* PCI driver stuff */
10979static struct pci_device_id card_ids[] = {
10980 {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2701, 0, 0, 0},
10981 {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2702, 0, 0, 0},
10982 {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2711, 0, 0, 0},
10983 {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2712, 0, 0, 0},
10984 {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2721, 0, 0, 0},
10985 {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2722, 0, 0, 0},
10986 {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2731, 0, 0, 0},
10987 {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2732, 0, 0, 0},
10988 {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2741, 0, 0, 0},
10989 {PCI_VENDOR_ID_INTEL, 0x1043, 0x103c, 0x2741, 0, 0, 0},
10990 {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2742, 0, 0, 0},
10991 {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2751, 0, 0, 0},
10992 {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2752, 0, 0, 0},
10993 {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2753, 0, 0, 0},
10994 {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2754, 0, 0, 0},
10995 {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2761, 0, 0, 0},
10996 {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2762, 0, 0, 0},
10997 {PCI_VENDOR_ID_INTEL, 0x104f, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
Jeff Garzik0edd5b42005-09-07 00:48:31 -040010998 {PCI_VENDOR_ID_INTEL, 0x4220, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, /* BG */
James Ketrenosa613bff2005-08-24 21:43:11 -050010999 {PCI_VENDOR_ID_INTEL, 0x4221, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, /* BG */
Jeff Garzik0edd5b42005-09-07 00:48:31 -040011000 {PCI_VENDOR_ID_INTEL, 0x4223, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, /* ABG */
11001 {PCI_VENDOR_ID_INTEL, 0x4224, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, /* ABG */
Jeff Garzikbf794512005-07-31 13:07:26 -040011002
James Ketrenos43f66a62005-03-25 12:31:53 -060011003 /* required last entry */
11004 {0,}
11005};
11006
11007MODULE_DEVICE_TABLE(pci, card_ids);
11008
11009static struct attribute *ipw_sysfs_entries[] = {
11010 &dev_attr_rf_kill.attr,
11011 &dev_attr_direct_dword.attr,
11012 &dev_attr_indirect_byte.attr,
11013 &dev_attr_indirect_dword.attr,
11014 &dev_attr_mem_gpio_reg.attr,
11015 &dev_attr_command_event_reg.attr,
11016 &dev_attr_nic_type.attr,
11017 &dev_attr_status.attr,
11018 &dev_attr_cfg.attr,
James Ketrenosb39860c2005-08-12 09:36:32 -050011019 &dev_attr_error.attr,
11020 &dev_attr_event_log.attr,
James Ketrenosf6c5cb72005-08-25 00:39:09 -050011021 &dev_attr_cmd_log.attr,
James Ketrenos43f66a62005-03-25 12:31:53 -060011022 &dev_attr_eeprom_delay.attr,
11023 &dev_attr_ucode_version.attr,
11024 &dev_attr_rtc.attr,
James Ketrenosa613bff2005-08-24 21:43:11 -050011025 &dev_attr_scan_age.attr,
11026 &dev_attr_led.attr,
James Ketrenosb095c382005-08-24 22:04:42 -050011027 &dev_attr_speed_scan.attr,
11028 &dev_attr_net_stats.attr,
James Ketrenos43f66a62005-03-25 12:31:53 -060011029 NULL
11030};
11031
11032static struct attribute_group ipw_attribute_group = {
11033 .name = NULL, /* put in device directory */
Jeff Garzik0edd5b42005-09-07 00:48:31 -040011034 .attrs = ipw_sysfs_entries,
James Ketrenos43f66a62005-03-25 12:31:53 -060011035};
11036
Jeff Garzik0edd5b42005-09-07 00:48:31 -040011037static int ipw_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
James Ketrenos43f66a62005-03-25 12:31:53 -060011038{
11039 int err = 0;
11040 struct net_device *net_dev;
11041 void __iomem *base;
11042 u32 length, val;
11043 struct ipw_priv *priv;
James Ketrenosafbf30a2005-08-25 00:05:33 -050011044 int i;
James Ketrenos43f66a62005-03-25 12:31:53 -060011045
11046 net_dev = alloc_ieee80211(sizeof(struct ipw_priv));
11047 if (net_dev == NULL) {
11048 err = -ENOMEM;
11049 goto out;
11050 }
11051
11052 priv = ieee80211_priv(net_dev);
11053 priv->ieee = netdev_priv(net_dev);
James Ketrenosa613bff2005-08-24 21:43:11 -050011054
James Ketrenos43f66a62005-03-25 12:31:53 -060011055 priv->net_dev = net_dev;
11056 priv->pci_dev = pdev;
11057#ifdef CONFIG_IPW_DEBUG
11058 ipw_debug_level = debug;
11059#endif
11060 spin_lock_init(&priv->lock);
James Ketrenosafbf30a2005-08-25 00:05:33 -050011061 for (i = 0; i < IPW_IBSS_MAC_HASH_SIZE; i++)
11062 INIT_LIST_HEAD(&priv->ibss_mac_hash[i]);
James Ketrenos43f66a62005-03-25 12:31:53 -060011063
James Ketrenosc848d0a2005-08-24 21:56:24 -050011064 init_MUTEX(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -060011065 if (pci_enable_device(pdev)) {
11066 err = -ENODEV;
11067 goto out_free_ieee80211;
11068 }
11069
11070 pci_set_master(pdev);
11071
Tobias Klauser0e08b442005-06-20 14:28:41 -070011072 err = pci_set_dma_mask(pdev, DMA_32BIT_MASK);
Jeff Garzikbf794512005-07-31 13:07:26 -040011073 if (!err)
Tobias Klauser0e08b442005-06-20 14:28:41 -070011074 err = pci_set_consistent_dma_mask(pdev, DMA_32BIT_MASK);
James Ketrenos43f66a62005-03-25 12:31:53 -060011075 if (err) {
11076 printk(KERN_WARNING DRV_NAME ": No suitable DMA available.\n");
11077 goto out_pci_disable_device;
11078 }
11079
11080 pci_set_drvdata(pdev, priv);
11081
11082 err = pci_request_regions(pdev, DRV_NAME);
Jeff Garzikbf794512005-07-31 13:07:26 -040011083 if (err)
James Ketrenos43f66a62005-03-25 12:31:53 -060011084 goto out_pci_disable_device;
11085
Jeff Garzikbf794512005-07-31 13:07:26 -040011086 /* We disable the RETRY_TIMEOUT register (0x41) to keep
James Ketrenos43f66a62005-03-25 12:31:53 -060011087 * PCI Tx retries from interfering with C3 CPU state */
Jeff Garzikbf794512005-07-31 13:07:26 -040011088 pci_read_config_dword(pdev, 0x40, &val);
11089 if ((val & 0x0000ff00) != 0)
James Ketrenos43f66a62005-03-25 12:31:53 -060011090 pci_write_config_dword(pdev, 0x40, val & 0xffff00ff);
Jeff Garzikbf794512005-07-31 13:07:26 -040011091
James Ketrenos43f66a62005-03-25 12:31:53 -060011092 length = pci_resource_len(pdev, 0);
11093 priv->hw_len = length;
Jeff Garzikbf794512005-07-31 13:07:26 -040011094
James Ketrenos43f66a62005-03-25 12:31:53 -060011095 base = ioremap_nocache(pci_resource_start(pdev, 0), length);
11096 if (!base) {
11097 err = -ENODEV;
11098 goto out_pci_release_regions;
11099 }
11100
11101 priv->hw_base = base;
11102 IPW_DEBUG_INFO("pci_resource_len = 0x%08x\n", length);
11103 IPW_DEBUG_INFO("pci_resource_base = %p\n", base);
11104
11105 err = ipw_setup_deferred_work(priv);
11106 if (err) {
11107 IPW_ERROR("Unable to setup deferred work\n");
11108 goto out_iounmap;
11109 }
11110
James Ketrenosb095c382005-08-24 22:04:42 -050011111 ipw_sw_reset(priv, 1);
James Ketrenos43f66a62005-03-25 12:31:53 -060011112
Jeff Garzik0edd5b42005-09-07 00:48:31 -040011113 err = request_irq(pdev->irq, ipw_isr, SA_SHIRQ, DRV_NAME, priv);
James Ketrenos43f66a62005-03-25 12:31:53 -060011114 if (err) {
11115 IPW_ERROR("Error allocating IRQ %d\n", pdev->irq);
11116 goto out_destroy_workqueue;
11117 }
11118
11119 SET_MODULE_OWNER(net_dev);
11120 SET_NETDEV_DEV(net_dev, &pdev->dev);
11121
James Ketrenosa613bff2005-08-24 21:43:11 -050011122 ipw_wx_data.spy_data = &priv->ieee->spy_data;
11123 ipw_wx_data.ieee80211 = priv->ieee;
11124
James Ketrenosc848d0a2005-08-24 21:56:24 -050011125 down(&priv->sem);
11126
James Ketrenos43f66a62005-03-25 12:31:53 -060011127 priv->ieee->hard_start_xmit = ipw_net_hard_start_xmit;
11128 priv->ieee->set_security = shim__set_security;
James Ketrenos227d2dc2005-07-28 16:25:55 -050011129 priv->ieee->is_queue_full = ipw_net_is_queue_full;
James Ketrenos43f66a62005-03-25 12:31:53 -060011130
James Ketrenosb095c382005-08-24 22:04:42 -050011131#ifdef CONFIG_IPW_QOS
James Ketrenos2b184d52005-08-03 20:33:14 -050011132 priv->ieee->handle_management = ipw_handle_management;
James Ketrenosb095c382005-08-24 22:04:42 -050011133#endif /* CONFIG_IPW_QOS */
11134
James Ketrenosc848d0a2005-08-24 21:56:24 -050011135 priv->ieee->perfect_rssi = -20;
11136 priv->ieee->worst_rssi = -85;
11137
James Ketrenos43f66a62005-03-25 12:31:53 -060011138 net_dev->open = ipw_net_open;
11139 net_dev->stop = ipw_net_stop;
11140 net_dev->init = ipw_net_init;
James Ketrenosafbf30a2005-08-25 00:05:33 -050011141#if WIRELESS_EXT < 18
James Ketrenosea2b26e2005-08-24 21:25:16 -050011142 net_dev->do_ioctl = ipw_ioctl;
James Ketrenosafbf30a2005-08-25 00:05:33 -050011143#endif
James Ketrenos43f66a62005-03-25 12:31:53 -060011144 net_dev->get_stats = ipw_net_get_stats;
11145 net_dev->set_multicast_list = ipw_net_set_multicast_list;
11146 net_dev->set_mac_address = ipw_net_set_mac_address;
11147 net_dev->get_wireless_stats = ipw_get_wireless_stats;
James Ketrenosa613bff2005-08-24 21:43:11 -050011148 net_dev->wireless_data = &ipw_wx_data;
James Ketrenos43f66a62005-03-25 12:31:53 -060011149 net_dev->wireless_handlers = &ipw_wx_handler_def;
11150 net_dev->ethtool_ops = &ipw_ethtool_ops;
11151 net_dev->irq = pdev->irq;
Jeff Garzik0edd5b42005-09-07 00:48:31 -040011152 net_dev->base_addr = (unsigned long)priv->hw_base;
James Ketrenos43f66a62005-03-25 12:31:53 -060011153 net_dev->mem_start = pci_resource_start(pdev, 0);
11154 net_dev->mem_end = net_dev->mem_start + pci_resource_len(pdev, 0) - 1;
11155
11156 err = sysfs_create_group(&pdev->dev.kobj, &ipw_attribute_group);
11157 if (err) {
11158 IPW_ERROR("failed to create sysfs device attributes\n");
James Ketrenosc848d0a2005-08-24 21:56:24 -050011159 up(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -060011160 goto out_release_irq;
11161 }
11162
James Ketrenosc848d0a2005-08-24 21:56:24 -050011163 up(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -060011164 err = register_netdev(net_dev);
11165 if (err) {
11166 IPW_ERROR("failed to register network device\n");
James Ketrenosa613bff2005-08-24 21:43:11 -050011167 goto out_remove_sysfs;
James Ketrenos43f66a62005-03-25 12:31:53 -060011168 }
James Ketrenos43f66a62005-03-25 12:31:53 -060011169 return 0;
11170
James Ketrenosa613bff2005-08-24 21:43:11 -050011171 out_remove_sysfs:
James Ketrenos43f66a62005-03-25 12:31:53 -060011172 sysfs_remove_group(&pdev->dev.kobj, &ipw_attribute_group);
Jeff Garzik0edd5b42005-09-07 00:48:31 -040011173 out_release_irq:
James Ketrenos43f66a62005-03-25 12:31:53 -060011174 free_irq(pdev->irq, priv);
Jeff Garzik0edd5b42005-09-07 00:48:31 -040011175 out_destroy_workqueue:
James Ketrenos43f66a62005-03-25 12:31:53 -060011176 destroy_workqueue(priv->workqueue);
11177 priv->workqueue = NULL;
Jeff Garzik0edd5b42005-09-07 00:48:31 -040011178 out_iounmap:
James Ketrenos43f66a62005-03-25 12:31:53 -060011179 iounmap(priv->hw_base);
Jeff Garzik0edd5b42005-09-07 00:48:31 -040011180 out_pci_release_regions:
James Ketrenos43f66a62005-03-25 12:31:53 -060011181 pci_release_regions(pdev);
Jeff Garzik0edd5b42005-09-07 00:48:31 -040011182 out_pci_disable_device:
James Ketrenos43f66a62005-03-25 12:31:53 -060011183 pci_disable_device(pdev);
11184 pci_set_drvdata(pdev, NULL);
Jeff Garzik0edd5b42005-09-07 00:48:31 -040011185 out_free_ieee80211:
James Ketrenos43f66a62005-03-25 12:31:53 -060011186 free_ieee80211(priv->net_dev);
Jeff Garzik0edd5b42005-09-07 00:48:31 -040011187 out:
James Ketrenos43f66a62005-03-25 12:31:53 -060011188 return err;
11189}
11190
11191static void ipw_pci_remove(struct pci_dev *pdev)
11192{
11193 struct ipw_priv *priv = pci_get_drvdata(pdev);
James Ketrenosafbf30a2005-08-25 00:05:33 -050011194 struct list_head *p, *q;
11195 int i;
James Ketrenosb095c382005-08-24 22:04:42 -050011196
James Ketrenos43f66a62005-03-25 12:31:53 -060011197 if (!priv)
11198 return;
11199
James Ketrenosb095c382005-08-24 22:04:42 -050011200 down(&priv->sem);
James Ketrenosafbf30a2005-08-25 00:05:33 -050011201
11202 priv->status |= STATUS_EXIT_PENDING;
James Ketrenos43f66a62005-03-25 12:31:53 -060011203 ipw_down(priv);
James Ketrenosb095c382005-08-24 22:04:42 -050011204 sysfs_remove_group(&pdev->dev.kobj, &ipw_attribute_group);
James Ketrenosafbf30a2005-08-25 00:05:33 -050011205
James Ketrenosb095c382005-08-24 22:04:42 -050011206 up(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -060011207
11208 unregister_netdev(priv->net_dev);
11209
11210 if (priv->rxq) {
11211 ipw_rx_queue_free(priv, priv->rxq);
11212 priv->rxq = NULL;
11213 }
11214 ipw_tx_queue_free(priv);
11215
James Ketrenosf6c5cb72005-08-25 00:39:09 -050011216 if (priv->cmdlog) {
11217 kfree(priv->cmdlog);
11218 priv->cmdlog = NULL;
11219 }
James Ketrenos43f66a62005-03-25 12:31:53 -060011220 /* ipw_down will ensure that there is no more pending work
11221 * in the workqueue's, so we can safely remove them now. */
James Ketrenosa613bff2005-08-24 21:43:11 -050011222 cancel_delayed_work(&priv->adhoc_check);
11223 cancel_delayed_work(&priv->gather_stats);
11224 cancel_delayed_work(&priv->request_scan);
11225 cancel_delayed_work(&priv->rf_kill);
11226 cancel_delayed_work(&priv->scan_check);
11227 destroy_workqueue(priv->workqueue);
11228 priv->workqueue = NULL;
James Ketrenos43f66a62005-03-25 12:31:53 -060011229
James Ketrenosafbf30a2005-08-25 00:05:33 -050011230 /* Free MAC hash list for ADHOC */
11231 for (i = 0; i < IPW_IBSS_MAC_HASH_SIZE; i++) {
11232 list_for_each_safe(p, q, &priv->ibss_mac_hash[i]) {
11233 kfree(list_entry(p, struct ipw_ibss_seq, list));
11234 list_del(p);
11235 }
11236 }
11237
James Ketrenosb39860c2005-08-12 09:36:32 -050011238 if (priv->error) {
11239 ipw_free_error_log(priv->error);
11240 priv->error = NULL;
11241 }
11242
James Ketrenos43f66a62005-03-25 12:31:53 -060011243 free_irq(pdev->irq, priv);
11244 iounmap(priv->hw_base);
11245 pci_release_regions(pdev);
11246 pci_disable_device(pdev);
11247 pci_set_drvdata(pdev, NULL);
11248 free_ieee80211(priv->net_dev);
James Ketrenosafbf30a2005-08-25 00:05:33 -050011249 free_firmware();
James Ketrenos43f66a62005-03-25 12:31:53 -060011250}
11251
James Ketrenos43f66a62005-03-25 12:31:53 -060011252#ifdef CONFIG_PM
Pavel Machek583a4e82005-09-03 15:56:58 -070011253static int ipw_pci_suspend(struct pci_dev *pdev, pm_message_t state)
James Ketrenos43f66a62005-03-25 12:31:53 -060011254{
11255 struct ipw_priv *priv = pci_get_drvdata(pdev);
11256 struct net_device *dev = priv->net_dev;
11257
11258 printk(KERN_INFO "%s: Going into suspend...\n", dev->name);
11259
Jeff Garzik0edd5b42005-09-07 00:48:31 -040011260 /* Take down the device; powers it off, etc. */
James Ketrenos43f66a62005-03-25 12:31:53 -060011261 ipw_down(priv);
11262
11263 /* Remove the PRESENT state of the device */
11264 netif_device_detach(dev);
11265
James Ketrenos43f66a62005-03-25 12:31:53 -060011266 pci_save_state(pdev);
James Ketrenos43f66a62005-03-25 12:31:53 -060011267 pci_disable_device(pdev);
Pavel Machek583a4e82005-09-03 15:56:58 -070011268 pci_set_power_state(pdev, pci_choose_state(pdev, state));
Jeff Garzikbf794512005-07-31 13:07:26 -040011269
James Ketrenos43f66a62005-03-25 12:31:53 -060011270 return 0;
11271}
11272
11273static int ipw_pci_resume(struct pci_dev *pdev)
11274{
11275 struct ipw_priv *priv = pci_get_drvdata(pdev);
11276 struct net_device *dev = priv->net_dev;
11277 u32 val;
Jeff Garzikbf794512005-07-31 13:07:26 -040011278
James Ketrenos43f66a62005-03-25 12:31:53 -060011279 printk(KERN_INFO "%s: Coming out of suspend...\n", dev->name);
11280
James Ketrenosea2b26e2005-08-24 21:25:16 -050011281 pci_set_power_state(pdev, PCI_D0);
James Ketrenos43f66a62005-03-25 12:31:53 -060011282 pci_enable_device(pdev);
James Ketrenos43f66a62005-03-25 12:31:53 -060011283 pci_restore_state(pdev);
James Ketrenosea2b26e2005-08-24 21:25:16 -050011284
James Ketrenos43f66a62005-03-25 12:31:53 -060011285 /*
11286 * Suspend/Resume resets the PCI configuration space, so we have to
11287 * re-disable the RETRY_TIMEOUT register (0x41) to keep PCI Tx retries
11288 * from interfering with C3 CPU state. pci_restore_state won't help
11289 * here since it only restores the first 64 bytes pci config header.
11290 */
Jeff Garzikbf794512005-07-31 13:07:26 -040011291 pci_read_config_dword(pdev, 0x40, &val);
11292 if ((val & 0x0000ff00) != 0)
James Ketrenos43f66a62005-03-25 12:31:53 -060011293 pci_write_config_dword(pdev, 0x40, val & 0xffff00ff);
11294
11295 /* Set the device back into the PRESENT state; this will also wake
11296 * the queue of needed */
11297 netif_device_attach(dev);
11298
11299 /* Bring the device back up */
11300 queue_work(priv->workqueue, &priv->up);
Jeff Garzikbf794512005-07-31 13:07:26 -040011301
James Ketrenos43f66a62005-03-25 12:31:53 -060011302 return 0;
11303}
11304#endif
11305
11306/* driver initialization stuff */
11307static struct pci_driver ipw_driver = {
11308 .name = DRV_NAME,
11309 .id_table = card_ids,
11310 .probe = ipw_pci_probe,
11311 .remove = __devexit_p(ipw_pci_remove),
11312#ifdef CONFIG_PM
11313 .suspend = ipw_pci_suspend,
11314 .resume = ipw_pci_resume,
11315#endif
11316};
11317
11318static int __init ipw_init(void)
11319{
11320 int ret;
11321
11322 printk(KERN_INFO DRV_NAME ": " DRV_DESCRIPTION ", " DRV_VERSION "\n");
11323 printk(KERN_INFO DRV_NAME ": " DRV_COPYRIGHT "\n");
11324
11325 ret = pci_module_init(&ipw_driver);
11326 if (ret) {
11327 IPW_ERROR("Unable to initialize PCI module\n");
11328 return ret;
11329 }
11330
Jeff Garzik0edd5b42005-09-07 00:48:31 -040011331 ret = driver_create_file(&ipw_driver.driver, &driver_attr_debug_level);
James Ketrenos43f66a62005-03-25 12:31:53 -060011332 if (ret) {
11333 IPW_ERROR("Unable to create driver sysfs file\n");
11334 pci_unregister_driver(&ipw_driver);
11335 return ret;
11336 }
11337
11338 return ret;
11339}
11340
11341static void __exit ipw_exit(void)
11342{
11343 driver_remove_file(&ipw_driver.driver, &driver_attr_debug_level);
11344 pci_unregister_driver(&ipw_driver);
11345}
11346
11347module_param(disable, int, 0444);
11348MODULE_PARM_DESC(disable, "manually disable the radio (default 0 [radio on])");
11349
11350module_param(associate, int, 0444);
11351MODULE_PARM_DESC(associate, "auto associate when scanning (default on)");
11352
11353module_param(auto_create, int, 0444);
11354MODULE_PARM_DESC(auto_create, "auto create adhoc network (default on)");
11355
James Ketrenosa613bff2005-08-24 21:43:11 -050011356module_param(led, int, 0444);
James Ketrenosc848d0a2005-08-24 21:56:24 -050011357MODULE_PARM_DESC(led, "enable led control on some systems (default 0 off)\n");
James Ketrenosa613bff2005-08-24 21:43:11 -050011358
James Ketrenos43f66a62005-03-25 12:31:53 -060011359module_param(debug, int, 0444);
11360MODULE_PARM_DESC(debug, "debug output mask");
11361
11362module_param(channel, int, 0444);
Jeff Garzikbf794512005-07-31 13:07:26 -040011363MODULE_PARM_DESC(channel, "channel to limit associate to (default 0 [ANY])");
James Ketrenos43f66a62005-03-25 12:31:53 -060011364
James Ketrenosb095c382005-08-24 22:04:42 -050011365#ifdef CONFIG_IPW_QOS
11366module_param(qos_enable, int, 0444);
11367MODULE_PARM_DESC(qos_enable, "enable all QoS functionalitis");
11368
11369module_param(qos_burst_enable, int, 0444);
11370MODULE_PARM_DESC(qos_burst_enable, "enable QoS burst mode");
11371
11372module_param(qos_no_ack_mask, int, 0444);
11373MODULE_PARM_DESC(qos_no_ack_mask, "mask Tx_Queue to no ack");
11374
11375module_param(burst_duration_CCK, int, 0444);
11376MODULE_PARM_DESC(burst_duration_CCK, "set CCK burst value");
11377
11378module_param(burst_duration_OFDM, int, 0444);
11379MODULE_PARM_DESC(burst_duration_OFDM, "set OFDM burst value");
11380#endif /* CONFIG_IPW_QOS */
11381
11382#ifdef CONFIG_IPW2200_MONITOR
James Ketrenos43f66a62005-03-25 12:31:53 -060011383module_param(mode, int, 0444);
11384MODULE_PARM_DESC(mode, "network mode (0=BSS,1=IBSS,2=Monitor)");
11385#else
11386module_param(mode, int, 0444);
11387MODULE_PARM_DESC(mode, "network mode (0=BSS,1=IBSS)");
11388#endif
11389
James Ketrenosb095c382005-08-24 22:04:42 -050011390module_param(hwcrypto, int, 0444);
11391MODULE_PARM_DESC(hwcrypto, "enable hardware crypto (default on)");
11392
James Ketrenosf6c5cb72005-08-25 00:39:09 -050011393module_param(cmdlog, int, 0444);
11394MODULE_PARM_DESC(cmdlog,
11395 "allocate a ring buffer for logging firmware commands");
11396
James Ketrenos43f66a62005-03-25 12:31:53 -060011397module_exit(ipw_exit);
11398module_init(ipw_init);