blob: 0bf1931ac5f3404ecd9cf4a43b6d97068eceeae3 [file] [log] [blame]
James Ketrenos43f66a62005-03-25 12:31:53 -06001/******************************************************************************
Jeff Garzikbf794512005-07-31 13:07:26 -04002
James Ketrenos43f66a62005-03-25 12:31:53 -06003 Copyright(c) 2003 - 2004 Intel Corporation. All rights reserved.
4
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 Ketrenosc848d0a2005-08-24 21:56:24 -050035#define IPW2200_VERSION "1.0.3"
James Ketrenos43f66a62005-03-25 12:31:53 -060036#define DRV_DESCRIPTION "Intel(R) PRO/Wireless 2200/2915 Network Driver"
37#define DRV_COPYRIGHT "Copyright(c) 2003-2004 Intel Corporation"
38#define DRV_VERSION IPW2200_VERSION
39
40MODULE_DESCRIPTION(DRV_DESCRIPTION);
41MODULE_VERSION(DRV_VERSION);
42MODULE_AUTHOR(DRV_COPYRIGHT);
43MODULE_LICENSE("GPL");
44
45static int debug = 0;
46static int channel = 0;
James Ketrenos43f66a62005-03-25 12:31:53 -060047static int mode = 0;
48
49static u32 ipw_debug_level;
50static int associate = 1;
51static int auto_create = 1;
James Ketrenosa613bff2005-08-24 21:43:11 -050052static int led = 0;
James Ketrenos43f66a62005-03-25 12:31:53 -060053static int disable = 0;
54static const char ipw_modes[] = {
55 'a', 'b', 'g', '?'
56};
57
58static void ipw_rx(struct ipw_priv *priv);
Jeff Garzikbf794512005-07-31 13:07:26 -040059static int ipw_queue_tx_reclaim(struct ipw_priv *priv,
James Ketrenos43f66a62005-03-25 12:31:53 -060060 struct clx2_tx_queue *txq, int qindex);
61static int ipw_queue_reset(struct ipw_priv *priv);
62
63static int ipw_queue_tx_hcmd(struct ipw_priv *priv, int hcmd, void *buf,
64 int len, int sync);
65
66static void ipw_tx_queue_free(struct ipw_priv *);
67
68static struct ipw_rx_queue *ipw_rx_queue_alloc(struct ipw_priv *);
69static void ipw_rx_queue_free(struct ipw_priv *, struct ipw_rx_queue *);
70static void ipw_rx_queue_replenish(void *);
James Ketrenos43f66a62005-03-25 12:31:53 -060071static int ipw_up(struct ipw_priv *);
James Ketrenosc848d0a2005-08-24 21:56:24 -050072static void ipw_bg_up(void *);
James Ketrenos43f66a62005-03-25 12:31:53 -060073static void ipw_down(struct ipw_priv *);
James Ketrenosc848d0a2005-08-24 21:56:24 -050074static void ipw_bg_down(void *);
James Ketrenos43f66a62005-03-25 12:31:53 -060075static int ipw_config(struct ipw_priv *);
Jeff Garzik0edd5b42005-09-07 00:48:31 -040076static int init_supported_rates(struct ipw_priv *priv,
77 struct ipw_supported_rates *prates);
James Ketrenos43f66a62005-03-25 12:31:53 -060078
79static u8 band_b_active_channel[MAX_B_CHANNELS] = {
80 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 0
81};
82static u8 band_a_active_channel[MAX_A_CHANNELS] = {
83 36, 40, 44, 48, 149, 153, 157, 161, 165, 52, 56, 60, 64, 0
84};
85
86static int is_valid_channel(int mode_mask, int channel)
87{
88 int i;
89
90 if (!channel)
91 return 0;
92
93 if (mode_mask & IEEE_A)
94 for (i = 0; i < MAX_A_CHANNELS; i++)
95 if (band_a_active_channel[i] == channel)
96 return IEEE_A;
97
98 if (mode_mask & (IEEE_B | IEEE_G))
99 for (i = 0; i < MAX_B_CHANNELS; i++)
100 if (band_b_active_channel[i] == channel)
101 return mode_mask & (IEEE_B | IEEE_G);
102
103 return 0;
104}
105
Jeff Garzikbf794512005-07-31 13:07:26 -0400106static char *snprint_line(char *buf, size_t count,
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400107 const u8 * data, u32 len, u32 ofs)
James Ketrenos43f66a62005-03-25 12:31:53 -0600108{
109 int out, i, j, l;
110 char c;
Jeff Garzikbf794512005-07-31 13:07:26 -0400111
James Ketrenos43f66a62005-03-25 12:31:53 -0600112 out = snprintf(buf, count, "%08X", ofs);
113
114 for (l = 0, i = 0; i < 2; i++) {
115 out += snprintf(buf + out, count - out, " ");
Jeff Garzikbf794512005-07-31 13:07:26 -0400116 for (j = 0; j < 8 && l < len; j++, l++)
117 out += snprintf(buf + out, count - out, "%02X ",
James Ketrenos43f66a62005-03-25 12:31:53 -0600118 data[(i * 8 + j)]);
119 for (; j < 8; j++)
120 out += snprintf(buf + out, count - out, " ");
121 }
Jeff Garzikbf794512005-07-31 13:07:26 -0400122
James Ketrenos43f66a62005-03-25 12:31:53 -0600123 out += snprintf(buf + out, count - out, " ");
124 for (l = 0, i = 0; i < 2; i++) {
125 out += snprintf(buf + out, count - out, " ");
126 for (j = 0; j < 8 && l < len; j++, l++) {
127 c = data[(i * 8 + j)];
128 if (!isascii(c) || !isprint(c))
129 c = '.';
Jeff Garzikbf794512005-07-31 13:07:26 -0400130
James Ketrenos43f66a62005-03-25 12:31:53 -0600131 out += snprintf(buf + out, count - out, "%c", c);
132 }
133
134 for (; j < 8; j++)
135 out += snprintf(buf + out, count - out, " ");
136 }
Jeff Garzikbf794512005-07-31 13:07:26 -0400137
James Ketrenos43f66a62005-03-25 12:31:53 -0600138 return buf;
139}
140
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400141static void printk_buf(int level, const u8 * data, u32 len)
James Ketrenos43f66a62005-03-25 12:31:53 -0600142{
143 char line[81];
144 u32 ofs = 0;
145 if (!(ipw_debug_level & level))
146 return;
147
148 while (len) {
149 printk(KERN_DEBUG "%s\n",
Jeff Garzikbf794512005-07-31 13:07:26 -0400150 snprint_line(line, sizeof(line), &data[ofs],
James Ketrenos43f66a62005-03-25 12:31:53 -0600151 min(len, 16U), ofs));
152 ofs += 16;
153 len -= min(len, 16U);
154 }
155}
156
157static u32 _ipw_read_reg32(struct ipw_priv *priv, u32 reg);
158#define ipw_read_reg32(a, b) _ipw_read_reg32(a, b)
159
160static u8 _ipw_read_reg8(struct ipw_priv *ipw, u32 reg);
161#define ipw_read_reg8(a, b) _ipw_read_reg8(a, b)
162
163static void _ipw_write_reg8(struct ipw_priv *priv, u32 reg, u8 value);
164static inline void ipw_write_reg8(struct ipw_priv *a, u32 b, u8 c)
165{
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400166 IPW_DEBUG_IO("%s %d: write_indirect8(0x%08X, 0x%08X)\n", __FILE__,
167 __LINE__, (u32) (b), (u32) (c));
James Ketrenos43f66a62005-03-25 12:31:53 -0600168 _ipw_write_reg8(a, b, c);
169}
170
171static void _ipw_write_reg16(struct ipw_priv *priv, u32 reg, u16 value);
172static inline void ipw_write_reg16(struct ipw_priv *a, u32 b, u16 c)
173{
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400174 IPW_DEBUG_IO("%s %d: write_indirect16(0x%08X, 0x%08X)\n", __FILE__,
175 __LINE__, (u32) (b), (u32) (c));
James Ketrenos43f66a62005-03-25 12:31:53 -0600176 _ipw_write_reg16(a, b, c);
177}
178
179static void _ipw_write_reg32(struct ipw_priv *priv, u32 reg, u32 value);
180static inline void ipw_write_reg32(struct ipw_priv *a, u32 b, u32 c)
181{
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400182 IPW_DEBUG_IO("%s %d: write_indirect32(0x%08X, 0x%08X)\n", __FILE__,
183 __LINE__, (u32) (b), (u32) (c));
James Ketrenos43f66a62005-03-25 12:31:53 -0600184 _ipw_write_reg32(a, b, c);
185}
186
187#define _ipw_write8(ipw, ofs, val) writeb((val), (ipw)->hw_base + (ofs))
188#define ipw_write8(ipw, ofs, val) \
189 IPW_DEBUG_IO("%s %d: write_direct8(0x%08X, 0x%08X)\n", __FILE__, __LINE__, (u32)(ofs), (u32)(val)); \
190 _ipw_write8(ipw, ofs, val)
191
192#define _ipw_write16(ipw, ofs, val) writew((val), (ipw)->hw_base + (ofs))
193#define ipw_write16(ipw, ofs, val) \
194 IPW_DEBUG_IO("%s %d: write_direct16(0x%08X, 0x%08X)\n", __FILE__, __LINE__, (u32)(ofs), (u32)(val)); \
195 _ipw_write16(ipw, ofs, val)
196
197#define _ipw_write32(ipw, ofs, val) writel((val), (ipw)->hw_base + (ofs))
198#define ipw_write32(ipw, ofs, val) \
199 IPW_DEBUG_IO("%s %d: write_direct32(0x%08X, 0x%08X)\n", __FILE__, __LINE__, (u32)(ofs), (u32)(val)); \
200 _ipw_write32(ipw, ofs, val)
201
202#define _ipw_read8(ipw, ofs) readb((ipw)->hw_base + (ofs))
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400203static inline u8 __ipw_read8(char *f, u32 l, struct ipw_priv *ipw, u32 ofs)
204{
205 IPW_DEBUG_IO("%s %d: read_direct8(0x%08X)\n", f, l, (u32) (ofs));
James Ketrenos43f66a62005-03-25 12:31:53 -0600206 return _ipw_read8(ipw, ofs);
207}
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400208
James Ketrenos43f66a62005-03-25 12:31:53 -0600209#define ipw_read8(ipw, ofs) __ipw_read8(__FILE__, __LINE__, ipw, ofs)
210
211#define _ipw_read16(ipw, ofs) readw((ipw)->hw_base + (ofs))
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400212static inline u16 __ipw_read16(char *f, u32 l, struct ipw_priv *ipw, u32 ofs)
213{
214 IPW_DEBUG_IO("%s %d: read_direct16(0x%08X)\n", f, l, (u32) (ofs));
James Ketrenos43f66a62005-03-25 12:31:53 -0600215 return _ipw_read16(ipw, ofs);
216}
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400217
James Ketrenos43f66a62005-03-25 12:31:53 -0600218#define ipw_read16(ipw, ofs) __ipw_read16(__FILE__, __LINE__, ipw, ofs)
219
220#define _ipw_read32(ipw, ofs) readl((ipw)->hw_base + (ofs))
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400221static inline u32 __ipw_read32(char *f, u32 l, struct ipw_priv *ipw, u32 ofs)
222{
223 IPW_DEBUG_IO("%s %d: read_direct32(0x%08X)\n", f, l, (u32) (ofs));
James Ketrenos43f66a62005-03-25 12:31:53 -0600224 return _ipw_read32(ipw, ofs);
225}
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400226
James Ketrenos43f66a62005-03-25 12:31:53 -0600227#define ipw_read32(ipw, ofs) __ipw_read32(__FILE__, __LINE__, ipw, ofs)
228
229static void _ipw_read_indirect(struct ipw_priv *, u32, u8 *, int);
230#define ipw_read_indirect(a, b, c, d) \
231 IPW_DEBUG_IO("%s %d: read_inddirect(0x%08X) %d bytes\n", __FILE__, __LINE__, (u32)(b), d); \
232 _ipw_read_indirect(a, b, c, d)
233
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400234static void _ipw_write_indirect(struct ipw_priv *priv, u32 addr, u8 * data,
235 int num);
James Ketrenos43f66a62005-03-25 12:31:53 -0600236#define ipw_write_indirect(a, b, c, d) \
237 IPW_DEBUG_IO("%s %d: write_indirect(0x%08X) %d bytes\n", __FILE__, __LINE__, (u32)(b), d); \
238 _ipw_write_indirect(a, b, c, d)
239
240/* indirect write s */
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400241static void _ipw_write_reg32(struct ipw_priv *priv, u32 reg, u32 value)
James Ketrenos43f66a62005-03-25 12:31:53 -0600242{
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400243 IPW_DEBUG_IO(" %p : reg = 0x%8X : value = 0x%8X\n", priv, reg, value);
James Ketrenos43f66a62005-03-25 12:31:53 -0600244 _ipw_write32(priv, CX2_INDIRECT_ADDR, reg);
245 _ipw_write32(priv, CX2_INDIRECT_DATA, value);
246}
247
James Ketrenos43f66a62005-03-25 12:31:53 -0600248static void _ipw_write_reg8(struct ipw_priv *priv, u32 reg, u8 value)
249{
250 IPW_DEBUG_IO(" reg = 0x%8X : value = 0x%8X\n", reg, value);
251 _ipw_write32(priv, CX2_INDIRECT_ADDR, reg & CX2_INDIRECT_ADDR_MASK);
252 _ipw_write8(priv, CX2_INDIRECT_DATA, value);
Jeff Garzikbf794512005-07-31 13:07:26 -0400253 IPW_DEBUG_IO(" reg = 0x%8lX : value = 0x%8X\n",
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400254 (unsigned long)(priv->hw_base + CX2_INDIRECT_DATA), value);
James Ketrenos43f66a62005-03-25 12:31:53 -0600255}
256
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400257static void _ipw_write_reg16(struct ipw_priv *priv, u32 reg, u16 value)
James Ketrenos43f66a62005-03-25 12:31:53 -0600258{
259 IPW_DEBUG_IO(" reg = 0x%8X : value = 0x%8X\n", reg, value);
260 _ipw_write32(priv, CX2_INDIRECT_ADDR, reg & CX2_INDIRECT_ADDR_MASK);
261 _ipw_write16(priv, CX2_INDIRECT_DATA, value);
262}
263
264/* indirect read s */
265
266static u8 _ipw_read_reg8(struct ipw_priv *priv, u32 reg)
267{
268 u32 word;
269 _ipw_write32(priv, CX2_INDIRECT_ADDR, reg & CX2_INDIRECT_ADDR_MASK);
270 IPW_DEBUG_IO(" reg = 0x%8X : \n", reg);
271 word = _ipw_read32(priv, CX2_INDIRECT_DATA);
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400272 return (word >> ((reg & 0x3) * 8)) & 0xff;
James Ketrenos43f66a62005-03-25 12:31:53 -0600273}
274
275static u32 _ipw_read_reg32(struct ipw_priv *priv, u32 reg)
276{
277 u32 value;
278
279 IPW_DEBUG_IO("%p : reg = 0x%08x\n", priv, reg);
280
281 _ipw_write32(priv, CX2_INDIRECT_ADDR, reg);
282 value = _ipw_read32(priv, CX2_INDIRECT_DATA);
283 IPW_DEBUG_IO(" reg = 0x%4X : value = 0x%4x \n", reg, value);
284 return value;
285}
286
287/* iterative/auto-increment 32 bit reads and writes */
288static void _ipw_read_indirect(struct ipw_priv *priv, u32 addr, u8 * buf,
289 int num)
290{
291 u32 aligned_addr = addr & CX2_INDIRECT_ADDR_MASK;
292 u32 dif_len = addr - aligned_addr;
James Ketrenos43f66a62005-03-25 12:31:53 -0600293 u32 i;
Jeff Garzikbf794512005-07-31 13:07:26 -0400294
James Ketrenos43f66a62005-03-25 12:31:53 -0600295 IPW_DEBUG_IO("addr = %i, buf = %p, num = %i\n", addr, buf, num);
296
James Ketrenosea2b26e2005-08-24 21:25:16 -0500297 if (num <= 0) {
298 return;
299 }
300
James Ketrenos43f66a62005-03-25 12:31:53 -0600301 /* Read the first nibble byte by byte */
302 if (unlikely(dif_len)) {
James Ketrenos43f66a62005-03-25 12:31:53 -0600303 _ipw_write32(priv, CX2_INDIRECT_ADDR, aligned_addr);
James Ketrenosea2b26e2005-08-24 21:25:16 -0500304 /* Start reading at aligned_addr + dif_len */
305 for (i = dif_len; ((i < 4) && (num > 0)); i++, num--)
306 *buf++ = _ipw_read8(priv, CX2_INDIRECT_DATA + i);
James Ketrenos43f66a62005-03-25 12:31:53 -0600307 aligned_addr += 4;
308 }
309
James Ketrenos43f66a62005-03-25 12:31:53 -0600310 _ipw_write32(priv, CX2_AUTOINC_ADDR, aligned_addr);
James Ketrenosea2b26e2005-08-24 21:25:16 -0500311 for (; num >= 4; buf += 4, aligned_addr += 4, num -= 4)
312 *(u32 *) buf = _ipw_read32(priv, CX2_AUTOINC_DATA);
Jeff Garzikbf794512005-07-31 13:07:26 -0400313
James Ketrenos43f66a62005-03-25 12:31:53 -0600314 /* Copy the last nibble */
James Ketrenosea2b26e2005-08-24 21:25:16 -0500315 if (unlikely(num)) {
316 _ipw_write32(priv, CX2_INDIRECT_ADDR, aligned_addr);
317 for (i = 0; num > 0; i++, num--)
318 *buf++ = ipw_read8(priv, CX2_INDIRECT_DATA + i);
319 }
James Ketrenos43f66a62005-03-25 12:31:53 -0600320}
321
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400322static void _ipw_write_indirect(struct ipw_priv *priv, u32 addr, u8 * buf,
James Ketrenos43f66a62005-03-25 12:31:53 -0600323 int num)
324{
325 u32 aligned_addr = addr & CX2_INDIRECT_ADDR_MASK;
326 u32 dif_len = addr - aligned_addr;
James Ketrenos43f66a62005-03-25 12:31:53 -0600327 u32 i;
Jeff Garzikbf794512005-07-31 13:07:26 -0400328
James Ketrenos43f66a62005-03-25 12:31:53 -0600329 IPW_DEBUG_IO("addr = %i, buf = %p, num = %i\n", addr, buf, num);
Jeff Garzikbf794512005-07-31 13:07:26 -0400330
James Ketrenosea2b26e2005-08-24 21:25:16 -0500331 if (num <= 0) {
332 return;
333 }
334
James Ketrenos43f66a62005-03-25 12:31:53 -0600335 /* Write the first nibble byte by byte */
336 if (unlikely(dif_len)) {
James Ketrenos43f66a62005-03-25 12:31:53 -0600337 _ipw_write32(priv, CX2_INDIRECT_ADDR, aligned_addr);
James Ketrenosea2b26e2005-08-24 21:25:16 -0500338 /* Start reading at aligned_addr + dif_len */
339 for (i = dif_len; ((i < 4) && (num > 0)); i++, num--, buf++)
James Ketrenos43f66a62005-03-25 12:31:53 -0600340 _ipw_write8(priv, CX2_INDIRECT_DATA + i, *buf);
James Ketrenos43f66a62005-03-25 12:31:53 -0600341 aligned_addr += 4;
342 }
Jeff Garzikbf794512005-07-31 13:07:26 -0400343
James Ketrenos43f66a62005-03-25 12:31:53 -0600344 _ipw_write32(priv, CX2_AUTOINC_ADDR, aligned_addr);
James Ketrenosea2b26e2005-08-24 21:25:16 -0500345 for (; num >= 4; buf += 4, aligned_addr += 4, num -= 4)
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400346 _ipw_write32(priv, CX2_AUTOINC_DATA, *(u32 *) buf);
Jeff Garzikbf794512005-07-31 13:07:26 -0400347
James Ketrenos43f66a62005-03-25 12:31:53 -0600348 /* Copy the last nibble */
James Ketrenosea2b26e2005-08-24 21:25:16 -0500349 if (unlikely(num)) {
350 _ipw_write32(priv, CX2_INDIRECT_ADDR, aligned_addr);
351 for (i = 0; num > 0; i++, num--, buf++)
352 _ipw_write8(priv, CX2_INDIRECT_DATA + i, *buf);
353 }
James Ketrenos43f66a62005-03-25 12:31:53 -0600354}
355
Jeff Garzikbf794512005-07-31 13:07:26 -0400356static void ipw_write_direct(struct ipw_priv *priv, u32 addr, void *buf,
James Ketrenos43f66a62005-03-25 12:31:53 -0600357 int num)
358{
359 memcpy_toio((priv->hw_base + addr), buf, num);
360}
361
362static inline void ipw_set_bit(struct ipw_priv *priv, u32 reg, u32 mask)
363{
364 ipw_write32(priv, reg, ipw_read32(priv, reg) | mask);
365}
366
367static inline void ipw_clear_bit(struct ipw_priv *priv, u32 reg, u32 mask)
368{
369 ipw_write32(priv, reg, ipw_read32(priv, reg) & ~mask);
370}
371
372static inline void ipw_enable_interrupts(struct ipw_priv *priv)
373{
374 if (priv->status & STATUS_INT_ENABLED)
375 return;
376 priv->status |= STATUS_INT_ENABLED;
377 ipw_write32(priv, CX2_INTA_MASK_R, CX2_INTA_MASK_ALL);
378}
379
380static inline void ipw_disable_interrupts(struct ipw_priv *priv)
381{
382 if (!(priv->status & STATUS_INT_ENABLED))
383 return;
384 priv->status &= ~STATUS_INT_ENABLED;
385 ipw_write32(priv, CX2_INTA_MASK_R, ~CX2_INTA_MASK_ALL);
386}
387
388static char *ipw_error_desc(u32 val)
389{
390 switch (val) {
Jeff Garzikbf794512005-07-31 13:07:26 -0400391 case IPW_FW_ERROR_OK:
James Ketrenos43f66a62005-03-25 12:31:53 -0600392 return "ERROR_OK";
Jeff Garzikbf794512005-07-31 13:07:26 -0400393 case IPW_FW_ERROR_FAIL:
James Ketrenos43f66a62005-03-25 12:31:53 -0600394 return "ERROR_FAIL";
Jeff Garzikbf794512005-07-31 13:07:26 -0400395 case IPW_FW_ERROR_MEMORY_UNDERFLOW:
James Ketrenos43f66a62005-03-25 12:31:53 -0600396 return "MEMORY_UNDERFLOW";
Jeff Garzikbf794512005-07-31 13:07:26 -0400397 case IPW_FW_ERROR_MEMORY_OVERFLOW:
James Ketrenos43f66a62005-03-25 12:31:53 -0600398 return "MEMORY_OVERFLOW";
Jeff Garzikbf794512005-07-31 13:07:26 -0400399 case IPW_FW_ERROR_BAD_PARAM:
James Ketrenos43f66a62005-03-25 12:31:53 -0600400 return "ERROR_BAD_PARAM";
Jeff Garzikbf794512005-07-31 13:07:26 -0400401 case IPW_FW_ERROR_BAD_CHECKSUM:
James Ketrenos43f66a62005-03-25 12:31:53 -0600402 return "ERROR_BAD_CHECKSUM";
Jeff Garzikbf794512005-07-31 13:07:26 -0400403 case IPW_FW_ERROR_NMI_INTERRUPT:
James Ketrenos43f66a62005-03-25 12:31:53 -0600404 return "ERROR_NMI_INTERRUPT";
Jeff Garzikbf794512005-07-31 13:07:26 -0400405 case IPW_FW_ERROR_BAD_DATABASE:
James Ketrenos43f66a62005-03-25 12:31:53 -0600406 return "ERROR_BAD_DATABASE";
Jeff Garzikbf794512005-07-31 13:07:26 -0400407 case IPW_FW_ERROR_ALLOC_FAIL:
James Ketrenos43f66a62005-03-25 12:31:53 -0600408 return "ERROR_ALLOC_FAIL";
Jeff Garzikbf794512005-07-31 13:07:26 -0400409 case IPW_FW_ERROR_DMA_UNDERRUN:
James Ketrenos43f66a62005-03-25 12:31:53 -0600410 return "ERROR_DMA_UNDERRUN";
Jeff Garzikbf794512005-07-31 13:07:26 -0400411 case IPW_FW_ERROR_DMA_STATUS:
James Ketrenos43f66a62005-03-25 12:31:53 -0600412 return "ERROR_DMA_STATUS";
Jeff Garzikbf794512005-07-31 13:07:26 -0400413 case IPW_FW_ERROR_DINOSTATUS_ERROR:
James Ketrenos43f66a62005-03-25 12:31:53 -0600414 return "ERROR_DINOSTATUS_ERROR";
Jeff Garzikbf794512005-07-31 13:07:26 -0400415 case IPW_FW_ERROR_EEPROMSTATUS_ERROR:
James Ketrenos43f66a62005-03-25 12:31:53 -0600416 return "ERROR_EEPROMSTATUS_ERROR";
Jeff Garzikbf794512005-07-31 13:07:26 -0400417 case IPW_FW_ERROR_SYSASSERT:
James Ketrenos43f66a62005-03-25 12:31:53 -0600418 return "ERROR_SYSASSERT";
Jeff Garzikbf794512005-07-31 13:07:26 -0400419 case IPW_FW_ERROR_FATAL_ERROR:
James Ketrenos43f66a62005-03-25 12:31:53 -0600420 return "ERROR_FATALSTATUS_ERROR";
Jeff Garzikbf794512005-07-31 13:07:26 -0400421 default:
James Ketrenos43f66a62005-03-25 12:31:53 -0600422 return "UNKNOWNSTATUS_ERROR";
423 }
424}
425
426static void ipw_dump_nic_error_log(struct ipw_priv *priv)
427{
428 u32 desc, time, blink1, blink2, ilink1, ilink2, idata, i, count, base;
429
430 base = ipw_read32(priv, IPWSTATUS_ERROR_LOG);
431 count = ipw_read_reg32(priv, base);
Jeff Garzikbf794512005-07-31 13:07:26 -0400432
James Ketrenos43f66a62005-03-25 12:31:53 -0600433 if (ERROR_START_OFFSET <= count * ERROR_ELEM_SIZE) {
434 IPW_ERROR("Start IPW Error Log Dump:\n");
435 IPW_ERROR("Status: 0x%08X, Config: %08X\n",
436 priv->status, priv->config);
437 }
438
Jeff Garzikbf794512005-07-31 13:07:26 -0400439 for (i = ERROR_START_OFFSET;
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400440 i <= count * ERROR_ELEM_SIZE; i += ERROR_ELEM_SIZE) {
441 desc = ipw_read_reg32(priv, base + i);
442 time = ipw_read_reg32(priv, base + i + 1 * sizeof(u32));
443 blink1 = ipw_read_reg32(priv, base + i + 2 * sizeof(u32));
444 blink2 = ipw_read_reg32(priv, base + i + 3 * sizeof(u32));
445 ilink1 = ipw_read_reg32(priv, base + i + 4 * sizeof(u32));
446 ilink2 = ipw_read_reg32(priv, base + i + 5 * sizeof(u32));
447 idata = ipw_read_reg32(priv, base + i + 6 * sizeof(u32));
James Ketrenos43f66a62005-03-25 12:31:53 -0600448
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400449 IPW_ERROR("%s %i 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x\n",
450 ipw_error_desc(desc), time, blink1, blink2,
451 ilink1, ilink2, idata);
James Ketrenos43f66a62005-03-25 12:31:53 -0600452 }
453}
454
455static void ipw_dump_nic_event_log(struct ipw_priv *priv)
456{
457 u32 ev, time, data, i, count, base;
458
459 base = ipw_read32(priv, IPW_EVENT_LOG);
460 count = ipw_read_reg32(priv, base);
Jeff Garzikbf794512005-07-31 13:07:26 -0400461
James Ketrenos43f66a62005-03-25 12:31:53 -0600462 if (EVENT_START_OFFSET <= count * EVENT_ELEM_SIZE)
463 IPW_ERROR("Start IPW Event Log Dump:\n");
464
Jeff Garzikbf794512005-07-31 13:07:26 -0400465 for (i = EVENT_START_OFFSET;
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400466 i <= count * EVENT_ELEM_SIZE; i += EVENT_ELEM_SIZE) {
James Ketrenos43f66a62005-03-25 12:31:53 -0600467 ev = ipw_read_reg32(priv, base + i);
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400468 time = ipw_read_reg32(priv, base + i + 1 * sizeof(u32));
469 data = ipw_read_reg32(priv, base + i + 2 * sizeof(u32));
James Ketrenos43f66a62005-03-25 12:31:53 -0600470
471#ifdef CONFIG_IPW_DEBUG
472 IPW_ERROR("%i\t0x%08x\t%i\n", time, data, ev);
473#endif
474 }
475}
476
James Ketrenosc848d0a2005-08-24 21:56:24 -0500477static inline int ipw_is_init(struct ipw_priv *priv)
478{
479 return (priv->status & STATUS_INIT) ? 1 : 0;
480}
481
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400482static int ipw_get_ordinal(struct ipw_priv *priv, u32 ord, void *val, u32 * len)
James Ketrenos43f66a62005-03-25 12:31:53 -0600483{
484 u32 addr, field_info, field_len, field_count, total_len;
485
486 IPW_DEBUG_ORD("ordinal = %i\n", ord);
487
488 if (!priv || !val || !len) {
489 IPW_DEBUG_ORD("Invalid argument\n");
490 return -EINVAL;
491 }
Jeff Garzikbf794512005-07-31 13:07:26 -0400492
James Ketrenos43f66a62005-03-25 12:31:53 -0600493 /* verify device ordinal tables have been initialized */
494 if (!priv->table0_addr || !priv->table1_addr || !priv->table2_addr) {
495 IPW_DEBUG_ORD("Access ordinals before initialization\n");
496 return -EINVAL;
497 }
498
499 switch (IPW_ORD_TABLE_ID_MASK & ord) {
500 case IPW_ORD_TABLE_0_MASK:
501 /*
502 * TABLE 0: Direct access to a table of 32 bit values
503 *
Jeff Garzikbf794512005-07-31 13:07:26 -0400504 * This is a very simple table with the data directly
James Ketrenos43f66a62005-03-25 12:31:53 -0600505 * read from the table
506 */
507
508 /* remove the table id from the ordinal */
509 ord &= IPW_ORD_TABLE_VALUE_MASK;
510
511 /* boundary check */
512 if (ord > priv->table0_len) {
513 IPW_DEBUG_ORD("ordinal value (%i) longer then "
514 "max (%i)\n", ord, priv->table0_len);
515 return -EINVAL;
516 }
517
518 /* verify we have enough room to store the value */
519 if (*len < sizeof(u32)) {
520 IPW_DEBUG_ORD("ordinal buffer length too small, "
Jiri Bencaaa4d302005-06-07 14:58:41 +0200521 "need %zd\n", sizeof(u32));
James Ketrenos43f66a62005-03-25 12:31:53 -0600522 return -EINVAL;
523 }
524
525 IPW_DEBUG_ORD("Reading TABLE0[%i] from offset 0x%08x\n",
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400526 ord, priv->table0_addr + (ord << 2));
James Ketrenos43f66a62005-03-25 12:31:53 -0600527
528 *len = sizeof(u32);
529 ord <<= 2;
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400530 *((u32 *) val) = ipw_read32(priv, priv->table0_addr + ord);
James Ketrenos43f66a62005-03-25 12:31:53 -0600531 break;
532
533 case IPW_ORD_TABLE_1_MASK:
534 /*
535 * TABLE 1: Indirect access to a table of 32 bit values
Jeff Garzikbf794512005-07-31 13:07:26 -0400536 *
537 * This is a fairly large table of u32 values each
James Ketrenos43f66a62005-03-25 12:31:53 -0600538 * representing starting addr for the data (which is
539 * also a u32)
540 */
541
542 /* remove the table id from the ordinal */
543 ord &= IPW_ORD_TABLE_VALUE_MASK;
Jeff Garzikbf794512005-07-31 13:07:26 -0400544
James Ketrenos43f66a62005-03-25 12:31:53 -0600545 /* boundary check */
546 if (ord > priv->table1_len) {
547 IPW_DEBUG_ORD("ordinal value too long\n");
548 return -EINVAL;
549 }
550
551 /* verify we have enough room to store the value */
552 if (*len < sizeof(u32)) {
553 IPW_DEBUG_ORD("ordinal buffer length too small, "
Jiri Bencaaa4d302005-06-07 14:58:41 +0200554 "need %zd\n", sizeof(u32));
James Ketrenos43f66a62005-03-25 12:31:53 -0600555 return -EINVAL;
556 }
557
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400558 *((u32 *) val) =
559 ipw_read_reg32(priv, (priv->table1_addr + (ord << 2)));
James Ketrenos43f66a62005-03-25 12:31:53 -0600560 *len = sizeof(u32);
561 break;
562
563 case IPW_ORD_TABLE_2_MASK:
564 /*
565 * TABLE 2: Indirect access to a table of variable sized values
566 *
567 * This table consist of six values, each containing
568 * - dword containing the starting offset of the data
569 * - dword containing the lengh in the first 16bits
570 * and the count in the second 16bits
571 */
572
573 /* remove the table id from the ordinal */
574 ord &= IPW_ORD_TABLE_VALUE_MASK;
575
576 /* boundary check */
577 if (ord > priv->table2_len) {
578 IPW_DEBUG_ORD("ordinal value too long\n");
579 return -EINVAL;
580 }
581
582 /* get the address of statistic */
583 addr = ipw_read_reg32(priv, priv->table2_addr + (ord << 3));
Jeff Garzikbf794512005-07-31 13:07:26 -0400584
585 /* get the second DW of statistics ;
James Ketrenos43f66a62005-03-25 12:31:53 -0600586 * two 16-bit words - first is length, second is count */
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400587 field_info =
588 ipw_read_reg32(priv,
589 priv->table2_addr + (ord << 3) +
590 sizeof(u32));
Jeff Garzikbf794512005-07-31 13:07:26 -0400591
James Ketrenos43f66a62005-03-25 12:31:53 -0600592 /* get each entry length */
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400593 field_len = *((u16 *) & field_info);
Jeff Garzikbf794512005-07-31 13:07:26 -0400594
James Ketrenos43f66a62005-03-25 12:31:53 -0600595 /* get number of entries */
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400596 field_count = *(((u16 *) & field_info) + 1);
Jeff Garzikbf794512005-07-31 13:07:26 -0400597
James Ketrenos43f66a62005-03-25 12:31:53 -0600598 /* abort if not enought memory */
599 total_len = field_len * field_count;
600 if (total_len > *len) {
601 *len = total_len;
602 return -EINVAL;
603 }
Jeff Garzikbf794512005-07-31 13:07:26 -0400604
James Ketrenos43f66a62005-03-25 12:31:53 -0600605 *len = total_len;
606 if (!total_len)
607 return 0;
608
609 IPW_DEBUG_ORD("addr = 0x%08x, total_len = %i, "
Jeff Garzikbf794512005-07-31 13:07:26 -0400610 "field_info = 0x%08x\n",
James Ketrenos43f66a62005-03-25 12:31:53 -0600611 addr, total_len, field_info);
612 ipw_read_indirect(priv, addr, val, total_len);
613 break;
614
615 default:
616 IPW_DEBUG_ORD("Invalid ordinal!\n");
617 return -EINVAL;
618
619 }
620
James Ketrenos43f66a62005-03-25 12:31:53 -0600621 return 0;
622}
623
624static void ipw_init_ordinals(struct ipw_priv *priv)
625{
626 priv->table0_addr = IPW_ORDINALS_TABLE_LOWER;
Jeff Garzikbf794512005-07-31 13:07:26 -0400627 priv->table0_len = ipw_read32(priv, priv->table0_addr);
James Ketrenos43f66a62005-03-25 12:31:53 -0600628
629 IPW_DEBUG_ORD("table 0 offset at 0x%08x, len = %i\n",
630 priv->table0_addr, priv->table0_len);
631
632 priv->table1_addr = ipw_read32(priv, IPW_ORDINALS_TABLE_1);
633 priv->table1_len = ipw_read_reg32(priv, priv->table1_addr);
634
635 IPW_DEBUG_ORD("table 1 offset at 0x%08x, len = %i\n",
636 priv->table1_addr, priv->table1_len);
637
638 priv->table2_addr = ipw_read32(priv, IPW_ORDINALS_TABLE_2);
639 priv->table2_len = ipw_read_reg32(priv, priv->table2_addr);
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400640 priv->table2_len &= 0x0000ffff; /* use first two bytes */
James Ketrenos43f66a62005-03-25 12:31:53 -0600641
642 IPW_DEBUG_ORD("table 2 offset at 0x%08x, len = %i\n",
643 priv->table2_addr, priv->table2_len);
644
645}
646
James Ketrenosa613bff2005-08-24 21:43:11 -0500647u32 ipw_register_toggle(u32 reg)
648{
649 reg &= ~CX2_START_STANDBY;
650 if (reg & CX2_GATE_ODMA)
651 reg &= ~CX2_GATE_ODMA;
652 if (reg & CX2_GATE_IDMA)
653 reg &= ~CX2_GATE_IDMA;
654 if (reg & CX2_GATE_ADMA)
655 reg &= ~CX2_GATE_ADMA;
656 return reg;
657}
658
659/*
660 * LED behavior:
661 * - On radio ON, turn on any LEDs that require to be on during start
662 * - On initialization, start unassociated blink
663 * - On association, disable unassociated blink
664 * - On disassociation, start unassociated blink
665 * - On radio OFF, turn off any LEDs started during radio on
666 *
667 */
668#define LD_TIME_LINK_ON 300
669#define LD_TIME_LINK_OFF 2700
670#define LD_TIME_ACT_ON 250
671
672void ipw_led_link_on(struct ipw_priv *priv)
673{
674 unsigned long flags;
675 u32 led;
676
677 /* If configured to not use LEDs, or nic_type is 1,
678 * then we don't toggle a LINK led */
679 if (priv->config & CFG_NO_LED || priv->nic_type == EEPROM_NIC_TYPE_1)
680 return;
681
682 spin_lock_irqsave(&priv->lock, flags);
683
684 if (!(priv->status & STATUS_RF_KILL_MASK) &&
685 !(priv->status & STATUS_LED_LINK_ON)) {
686 IPW_DEBUG_LED("Link LED On\n");
687 led = ipw_read_reg32(priv, CX2_EVENT_REG);
688 led |= priv->led_association_on;
689
690 led = ipw_register_toggle(led);
691
692 IPW_DEBUG_LED("Reg: 0x%08X\n", led);
693 ipw_write_reg32(priv, CX2_EVENT_REG, led);
694
695 priv->status |= STATUS_LED_LINK_ON;
696
697 /* If we aren't associated, schedule turning the LED off */
698 if (!(priv->status & STATUS_ASSOCIATED))
699 queue_delayed_work(priv->workqueue,
700 &priv->led_link_off,
701 LD_TIME_LINK_ON);
702 }
703
704 spin_unlock_irqrestore(&priv->lock, flags);
705}
706
James Ketrenosc848d0a2005-08-24 21:56:24 -0500707static void ipw_bg_led_link_on(void *data)
708{
709 struct ipw_priv *priv = data;
710 down(&priv->sem);
711 ipw_led_link_on(data);
712 up(&priv->sem);
713}
714
James Ketrenosa613bff2005-08-24 21:43:11 -0500715void ipw_led_link_off(struct ipw_priv *priv)
716{
717 unsigned long flags;
718 u32 led;
719
720 /* If configured not to use LEDs, or nic type is 1,
721 * then we don't goggle the LINK led. */
722 if (priv->config & CFG_NO_LED || priv->nic_type == EEPROM_NIC_TYPE_1)
723 return;
724
725 spin_lock_irqsave(&priv->lock, flags);
726
727 if (priv->status & STATUS_LED_LINK_ON) {
728 led = ipw_read_reg32(priv, CX2_EVENT_REG);
729 led &= priv->led_association_off;
730 led = ipw_register_toggle(led);
731
732 IPW_DEBUG_LED("Reg: 0x%08X\n", led);
733 ipw_write_reg32(priv, CX2_EVENT_REG, led);
734
735 IPW_DEBUG_LED("Link LED Off\n");
736
737 priv->status &= ~STATUS_LED_LINK_ON;
738
739 /* If we aren't associated and the radio is on, schedule
740 * turning the LED on (blink while unassociated) */
741 if (!(priv->status & STATUS_RF_KILL_MASK) &&
742 !(priv->status & STATUS_ASSOCIATED))
743 queue_delayed_work(priv->workqueue, &priv->led_link_on,
744 LD_TIME_LINK_OFF);
745
746 }
747
748 spin_unlock_irqrestore(&priv->lock, flags);
749}
750
James Ketrenosc848d0a2005-08-24 21:56:24 -0500751static void ipw_bg_led_link_off(void *data)
752{
753 struct ipw_priv *priv = data;
754 down(&priv->sem);
755 ipw_led_link_off(data);
756 up(&priv->sem);
757}
758
James Ketrenosa613bff2005-08-24 21:43:11 -0500759void ipw_led_activity_on(struct ipw_priv *priv)
760{
761 unsigned long flags;
762 u32 led;
763
764 if (priv->config & CFG_NO_LED)
765 return;
766
767 spin_lock_irqsave(&priv->lock, flags);
768
769 if (priv->status & STATUS_RF_KILL_MASK) {
770 spin_unlock_irqrestore(&priv->lock, flags);
771 return;
772 }
773
774 if (!(priv->status & STATUS_LED_ACT_ON)) {
775 led = ipw_read_reg32(priv, CX2_EVENT_REG);
776 led |= priv->led_activity_on;
777
778 led = ipw_register_toggle(led);
779
780 IPW_DEBUG_LED("Reg: 0x%08X\n", led);
781 ipw_write_reg32(priv, CX2_EVENT_REG, led);
782
783 IPW_DEBUG_LED("Activity LED On\n");
784
785 priv->status |= STATUS_LED_ACT_ON;
786
James Ketrenosc848d0a2005-08-24 21:56:24 -0500787 cancel_delayed_work(&priv->led_act_off);
James Ketrenosa613bff2005-08-24 21:43:11 -0500788 queue_delayed_work(priv->workqueue, &priv->led_act_off,
789 LD_TIME_ACT_ON);
790 } else {
791 /* Reschedule LED off for full time period */
792 cancel_delayed_work(&priv->led_act_off);
793 queue_delayed_work(priv->workqueue, &priv->led_act_off,
794 LD_TIME_ACT_ON);
795 }
796
797 spin_unlock_irqrestore(&priv->lock, flags);
798}
799
800void ipw_led_activity_off(struct ipw_priv *priv)
801{
802 unsigned long flags;
803 u32 led;
804
805 if (priv->config & CFG_NO_LED)
806 return;
807
808 spin_lock_irqsave(&priv->lock, flags);
809
810 if (priv->status & STATUS_LED_ACT_ON) {
811 led = ipw_read_reg32(priv, CX2_EVENT_REG);
812 led &= priv->led_activity_off;
813
814 led = ipw_register_toggle(led);
815
816 IPW_DEBUG_LED("Reg: 0x%08X\n", led);
817 ipw_write_reg32(priv, CX2_EVENT_REG, led);
818
819 IPW_DEBUG_LED("Activity LED Off\n");
820
821 priv->status &= ~STATUS_LED_ACT_ON;
822 }
823
824 spin_unlock_irqrestore(&priv->lock, flags);
825}
826
James Ketrenosc848d0a2005-08-24 21:56:24 -0500827static void ipw_bg_led_activity_off(void *data)
828{
829 struct ipw_priv *priv = data;
830 down(&priv->sem);
831 ipw_led_activity_off(data);
832 up(&priv->sem);
833}
834
James Ketrenosa613bff2005-08-24 21:43:11 -0500835void ipw_led_band_on(struct ipw_priv *priv)
836{
837 unsigned long flags;
838 u32 led;
839
840 /* Only nic type 1 supports mode LEDs */
James Ketrenosc848d0a2005-08-24 21:56:24 -0500841 if (priv->config & CFG_NO_LED ||
842 priv->nic_type != EEPROM_NIC_TYPE_1 || !priv->assoc_network)
James Ketrenosa613bff2005-08-24 21:43:11 -0500843 return;
844
845 spin_lock_irqsave(&priv->lock, flags);
846
847 led = ipw_read_reg32(priv, CX2_EVENT_REG);
848 if (priv->assoc_network->mode == IEEE_A) {
849 led |= priv->led_ofdm_on;
850 led &= priv->led_association_off;
851 IPW_DEBUG_LED("Mode LED On: 802.11a\n");
852 } else if (priv->assoc_network->mode == IEEE_G) {
853 led |= priv->led_ofdm_on;
854 led |= priv->led_association_on;
855 IPW_DEBUG_LED("Mode LED On: 802.11g\n");
856 } else {
857 led &= priv->led_ofdm_off;
858 led |= priv->led_association_on;
859 IPW_DEBUG_LED("Mode LED On: 802.11b\n");
860 }
861
862 led = ipw_register_toggle(led);
863
864 IPW_DEBUG_LED("Reg: 0x%08X\n", led);
865 ipw_write_reg32(priv, CX2_EVENT_REG, led);
866
867 spin_unlock_irqrestore(&priv->lock, flags);
868}
869
870void ipw_led_band_off(struct ipw_priv *priv)
871{
872 unsigned long flags;
873 u32 led;
874
875 /* Only nic type 1 supports mode LEDs */
876 if (priv->config & CFG_NO_LED || priv->nic_type != EEPROM_NIC_TYPE_1)
877 return;
878
879 spin_lock_irqsave(&priv->lock, flags);
880
881 led = ipw_read_reg32(priv, CX2_EVENT_REG);
882 led &= priv->led_ofdm_off;
883 led &= priv->led_association_off;
884
885 led = ipw_register_toggle(led);
886
887 IPW_DEBUG_LED("Reg: 0x%08X\n", led);
888 ipw_write_reg32(priv, CX2_EVENT_REG, led);
889
890 spin_unlock_irqrestore(&priv->lock, flags);
891}
892
893void ipw_led_radio_on(struct ipw_priv *priv)
894{
895 ipw_led_link_on(priv);
896}
897
898void ipw_led_radio_off(struct ipw_priv *priv)
899{
900 ipw_led_activity_off(priv);
901 ipw_led_link_off(priv);
902}
903
904void ipw_led_link_up(struct ipw_priv *priv)
905{
906 /* Set the Link Led on for all nic types */
907 ipw_led_link_on(priv);
908}
909
910void ipw_led_link_down(struct ipw_priv *priv)
911{
912 ipw_led_activity_off(priv);
913 ipw_led_link_off(priv);
914
915 if (priv->status & STATUS_RF_KILL_MASK)
916 ipw_led_radio_off(priv);
917}
918
919void ipw_led_init(struct ipw_priv *priv)
920{
921 priv->nic_type = priv->eeprom[EEPROM_NIC_TYPE];
922
923 /* Set the default PINs for the link and activity leds */
924 priv->led_activity_on = CX2_ACTIVITY_LED;
925 priv->led_activity_off = ~(CX2_ACTIVITY_LED);
926
927 priv->led_association_on = CX2_ASSOCIATED_LED;
928 priv->led_association_off = ~(CX2_ASSOCIATED_LED);
929
930 /* Set the default PINs for the OFDM leds */
931 priv->led_ofdm_on = CX2_OFDM_LED;
932 priv->led_ofdm_off = ~(CX2_OFDM_LED);
933
934 switch (priv->nic_type) {
935 case EEPROM_NIC_TYPE_1:
936 /* In this NIC type, the LEDs are reversed.... */
937 priv->led_activity_on = CX2_ASSOCIATED_LED;
938 priv->led_activity_off = ~(CX2_ASSOCIATED_LED);
939 priv->led_association_on = CX2_ACTIVITY_LED;
940 priv->led_association_off = ~(CX2_ACTIVITY_LED);
941
942 if (!(priv->config & CFG_NO_LED))
943 ipw_led_band_on(priv);
944
945 /* And we don't blink link LEDs for this nic, so
946 * just return here */
947 return;
948
949 case EEPROM_NIC_TYPE_3:
950 case EEPROM_NIC_TYPE_2:
951 case EEPROM_NIC_TYPE_4:
952 case EEPROM_NIC_TYPE_0:
953 break;
954
955 default:
956 IPW_DEBUG_INFO("Unknown NIC type from EEPROM: %d\n",
957 priv->nic_type);
958 priv->nic_type = EEPROM_NIC_TYPE_0;
959 break;
960 }
961
962 if (!(priv->config & CFG_NO_LED)) {
963 if (priv->status & STATUS_ASSOCIATED)
964 ipw_led_link_on(priv);
965 else
966 ipw_led_link_off(priv);
967 }
968}
969
970void ipw_led_shutdown(struct ipw_priv *priv)
971{
972 cancel_delayed_work(&priv->led_link_on);
973 cancel_delayed_work(&priv->led_link_off);
974 cancel_delayed_work(&priv->led_act_off);
975 ipw_led_activity_off(priv);
976 ipw_led_link_off(priv);
977 ipw_led_band_off(priv);
978}
979
James Ketrenos43f66a62005-03-25 12:31:53 -0600980/*
981 * The following adds a new attribute to the sysfs representation
982 * of this device driver (i.e. a new file in /sys/bus/pci/drivers/ipw/)
983 * used for controling the debug level.
Jeff Garzikbf794512005-07-31 13:07:26 -0400984 *
James Ketrenos43f66a62005-03-25 12:31:53 -0600985 * See the level definitions in ipw for details.
986 */
987static ssize_t show_debug_level(struct device_driver *d, char *buf)
988{
989 return sprintf(buf, "0x%08X\n", ipw_debug_level);
990}
James Ketrenosa613bff2005-08-24 21:43:11 -0500991
992static ssize_t store_debug_level(struct device_driver *d, const char *buf,
993 size_t count)
James Ketrenos43f66a62005-03-25 12:31:53 -0600994{
995 char *p = (char *)buf;
996 u32 val;
997
998 if (p[1] == 'x' || p[1] == 'X' || p[0] == 'x' || p[0] == 'X') {
999 p++;
1000 if (p[0] == 'x' || p[0] == 'X')
1001 p++;
1002 val = simple_strtoul(p, &p, 16);
1003 } else
1004 val = simple_strtoul(p, &p, 10);
Jeff Garzikbf794512005-07-31 13:07:26 -04001005 if (p == buf)
1006 printk(KERN_INFO DRV_NAME
James Ketrenos43f66a62005-03-25 12:31:53 -06001007 ": %s is not in hex or decimal form.\n", buf);
1008 else
1009 ipw_debug_level = val;
1010
1011 return strnlen(buf, count);
1012}
1013
Jeff Garzikbf794512005-07-31 13:07:26 -04001014static DRIVER_ATTR(debug_level, S_IWUSR | S_IRUGO,
James Ketrenos43f66a62005-03-25 12:31:53 -06001015 show_debug_level, store_debug_level);
1016
James Ketrenosa613bff2005-08-24 21:43:11 -05001017static ssize_t show_scan_age(struct device *d, struct device_attribute *attr,
1018 char *buf)
1019{
1020 struct ipw_priv *priv = dev_get_drvdata(d);
1021 return sprintf(buf, "%d\n", priv->ieee->scan_age);
1022}
1023
1024static ssize_t store_scan_age(struct device *d, struct device_attribute *attr,
1025 const char *buf, size_t count)
1026{
1027 struct ipw_priv *priv = dev_get_drvdata(d);
James Ketrenosc848d0a2005-08-24 21:56:24 -05001028#ifdef CONFIG_IPW_DEBUG
James Ketrenosa613bff2005-08-24 21:43:11 -05001029 struct net_device *dev = priv->net_dev;
James Ketrenosc848d0a2005-08-24 21:56:24 -05001030#endif
James Ketrenosa613bff2005-08-24 21:43:11 -05001031 char buffer[] = "00000000";
1032 unsigned long len =
1033 (sizeof(buffer) - 1) > count ? count : sizeof(buffer) - 1;
1034 unsigned long val;
1035 char *p = buffer;
1036
1037 IPW_DEBUG_INFO("enter\n");
1038
1039 strncpy(buffer, buf, len);
1040 buffer[len] = 0;
1041
1042 if (p[1] == 'x' || p[1] == 'X' || p[0] == 'x' || p[0] == 'X') {
1043 p++;
1044 if (p[0] == 'x' || p[0] == 'X')
1045 p++;
1046 val = simple_strtoul(p, &p, 16);
1047 } else
1048 val = simple_strtoul(p, &p, 10);
1049 if (p == buffer) {
1050 IPW_DEBUG_INFO("%s: user supplied invalid value.\n", dev->name);
1051 } else {
1052 priv->ieee->scan_age = val;
1053 IPW_DEBUG_INFO("set scan_age = %u\n", priv->ieee->scan_age);
1054 }
1055
1056 IPW_DEBUG_INFO("exit\n");
1057 return len;
1058}
1059
1060static DEVICE_ATTR(scan_age, S_IWUSR | S_IRUGO, show_scan_age, store_scan_age);
1061
1062static ssize_t show_led(struct device *d, struct device_attribute *attr,
1063 char *buf)
1064{
1065 struct ipw_priv *priv = dev_get_drvdata(d);
1066 return sprintf(buf, "%d\n", (priv->config & CFG_NO_LED) ? 0 : 1);
1067}
1068
1069static ssize_t store_led(struct device *d, struct device_attribute *attr,
1070 const char *buf, size_t count)
1071{
1072 struct ipw_priv *priv = dev_get_drvdata(d);
1073
1074 IPW_DEBUG_INFO("enter\n");
1075
1076 if (count == 0)
1077 return 0;
1078
1079 if (*buf == 0) {
1080 IPW_DEBUG_LED("Disabling LED control.\n");
1081 priv->config |= CFG_NO_LED;
1082 ipw_led_shutdown(priv);
1083 } else {
1084 IPW_DEBUG_LED("Enabling LED control.\n");
1085 priv->config &= ~CFG_NO_LED;
1086 ipw_led_init(priv);
1087 }
1088
1089 IPW_DEBUG_INFO("exit\n");
1090 return count;
1091}
1092
1093static DEVICE_ATTR(led, S_IWUSR | S_IRUGO, show_led, store_led);
1094
Andrew Mortonad3fee52005-06-20 14:30:36 -07001095static ssize_t show_status(struct device *d,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001096 struct device_attribute *attr, char *buf)
James Ketrenos43f66a62005-03-25 12:31:53 -06001097{
Andrew Mortonad3fee52005-06-20 14:30:36 -07001098 struct ipw_priv *p = d->driver_data;
James Ketrenos43f66a62005-03-25 12:31:53 -06001099 return sprintf(buf, "0x%08x\n", (int)p->status);
1100}
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001101
James Ketrenos43f66a62005-03-25 12:31:53 -06001102static DEVICE_ATTR(status, S_IRUGO, show_status, NULL);
1103
Andrew Mortonad3fee52005-06-20 14:30:36 -07001104static ssize_t show_cfg(struct device *d, struct device_attribute *attr,
1105 char *buf)
James Ketrenos43f66a62005-03-25 12:31:53 -06001106{
Andrew Mortonad3fee52005-06-20 14:30:36 -07001107 struct ipw_priv *p = d->driver_data;
James Ketrenos43f66a62005-03-25 12:31:53 -06001108 return sprintf(buf, "0x%08x\n", (int)p->config);
1109}
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001110
James Ketrenos43f66a62005-03-25 12:31:53 -06001111static DEVICE_ATTR(cfg, S_IRUGO, show_cfg, NULL);
1112
Andrew Mortonad3fee52005-06-20 14:30:36 -07001113static ssize_t show_nic_type(struct device *d,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001114 struct device_attribute *attr, char *buf)
James Ketrenos43f66a62005-03-25 12:31:53 -06001115{
James Ketrenosa613bff2005-08-24 21:43:11 -05001116 struct ipw_priv *priv = d->driver_data;
1117 return sprintf(buf, "TYPE: %d\n", priv->nic_type);
James Ketrenos43f66a62005-03-25 12:31:53 -06001118}
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001119
James Ketrenos43f66a62005-03-25 12:31:53 -06001120static DEVICE_ATTR(nic_type, S_IRUGO, show_nic_type, NULL);
1121
Andrew Mortonad3fee52005-06-20 14:30:36 -07001122static ssize_t dump_error_log(struct device *d,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001123 struct device_attribute *attr, const char *buf,
1124 size_t count)
James Ketrenos43f66a62005-03-25 12:31:53 -06001125{
1126 char *p = (char *)buf;
1127
Jeff Garzikbf794512005-07-31 13:07:26 -04001128 if (p[0] == '1')
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001129 ipw_dump_nic_error_log((struct ipw_priv *)d->driver_data);
James Ketrenos43f66a62005-03-25 12:31:53 -06001130
1131 return strnlen(buf, count);
1132}
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001133
James Ketrenos43f66a62005-03-25 12:31:53 -06001134static DEVICE_ATTR(dump_errors, S_IWUSR, NULL, dump_error_log);
1135
Andrew Mortonad3fee52005-06-20 14:30:36 -07001136static ssize_t dump_event_log(struct device *d,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001137 struct device_attribute *attr, const char *buf,
1138 size_t count)
James Ketrenos43f66a62005-03-25 12:31:53 -06001139{
1140 char *p = (char *)buf;
1141
Jeff Garzikbf794512005-07-31 13:07:26 -04001142 if (p[0] == '1')
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001143 ipw_dump_nic_event_log((struct ipw_priv *)d->driver_data);
James Ketrenos43f66a62005-03-25 12:31:53 -06001144
1145 return strnlen(buf, count);
1146}
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001147
James Ketrenos43f66a62005-03-25 12:31:53 -06001148static DEVICE_ATTR(dump_events, S_IWUSR, NULL, dump_event_log);
1149
Andrew Mortonad3fee52005-06-20 14:30:36 -07001150static ssize_t show_ucode_version(struct device *d,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001151 struct device_attribute *attr, char *buf)
James Ketrenos43f66a62005-03-25 12:31:53 -06001152{
1153 u32 len = sizeof(u32), tmp = 0;
Andrew Mortonad3fee52005-06-20 14:30:36 -07001154 struct ipw_priv *p = d->driver_data;
James Ketrenos43f66a62005-03-25 12:31:53 -06001155
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001156 if (ipw_get_ordinal(p, IPW_ORD_STAT_UCODE_VERSION, &tmp, &len))
James Ketrenos43f66a62005-03-25 12:31:53 -06001157 return 0;
1158
1159 return sprintf(buf, "0x%08x\n", tmp);
1160}
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001161
1162static DEVICE_ATTR(ucode_version, S_IWUSR | S_IRUGO, show_ucode_version, NULL);
James Ketrenos43f66a62005-03-25 12:31:53 -06001163
Andrew Mortonad3fee52005-06-20 14:30:36 -07001164static ssize_t show_rtc(struct device *d, struct device_attribute *attr,
1165 char *buf)
James Ketrenos43f66a62005-03-25 12:31:53 -06001166{
1167 u32 len = sizeof(u32), tmp = 0;
Andrew Mortonad3fee52005-06-20 14:30:36 -07001168 struct ipw_priv *p = d->driver_data;
James Ketrenos43f66a62005-03-25 12:31:53 -06001169
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001170 if (ipw_get_ordinal(p, IPW_ORD_STAT_RTC, &tmp, &len))
James Ketrenos43f66a62005-03-25 12:31:53 -06001171 return 0;
1172
1173 return sprintf(buf, "0x%08x\n", tmp);
1174}
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001175
1176static DEVICE_ATTR(rtc, S_IWUSR | S_IRUGO, show_rtc, NULL);
James Ketrenos43f66a62005-03-25 12:31:53 -06001177
1178/*
1179 * Add a device attribute to view/control the delay between eeprom
1180 * operations.
1181 */
Andrew Mortonad3fee52005-06-20 14:30:36 -07001182static ssize_t show_eeprom_delay(struct device *d,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001183 struct device_attribute *attr, char *buf)
James Ketrenos43f66a62005-03-25 12:31:53 -06001184{
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001185 int n = ((struct ipw_priv *)d->driver_data)->eeprom_delay;
James Ketrenos43f66a62005-03-25 12:31:53 -06001186 return sprintf(buf, "%i\n", n);
1187}
Andrew Mortonad3fee52005-06-20 14:30:36 -07001188static ssize_t store_eeprom_delay(struct device *d,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001189 struct device_attribute *attr,
1190 const char *buf, size_t count)
James Ketrenos43f66a62005-03-25 12:31:53 -06001191{
Andrew Mortonad3fee52005-06-20 14:30:36 -07001192 struct ipw_priv *p = d->driver_data;
James Ketrenos43f66a62005-03-25 12:31:53 -06001193 sscanf(buf, "%i", &p->eeprom_delay);
1194 return strnlen(buf, count);
1195}
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001196
1197static DEVICE_ATTR(eeprom_delay, S_IWUSR | S_IRUGO,
1198 show_eeprom_delay, store_eeprom_delay);
James Ketrenos43f66a62005-03-25 12:31:53 -06001199
Andrew Mortonad3fee52005-06-20 14:30:36 -07001200static ssize_t show_command_event_reg(struct device *d,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001201 struct device_attribute *attr, char *buf)
James Ketrenos43f66a62005-03-25 12:31:53 -06001202{
1203 u32 reg = 0;
Andrew Mortonad3fee52005-06-20 14:30:36 -07001204 struct ipw_priv *p = d->driver_data;
James Ketrenos43f66a62005-03-25 12:31:53 -06001205
1206 reg = ipw_read_reg32(p, CX2_INTERNAL_CMD_EVENT);
1207 return sprintf(buf, "0x%08x\n", reg);
1208}
Andrew Mortonad3fee52005-06-20 14:30:36 -07001209static ssize_t store_command_event_reg(struct device *d,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001210 struct device_attribute *attr,
1211 const char *buf, size_t count)
James Ketrenos43f66a62005-03-25 12:31:53 -06001212{
1213 u32 reg;
Andrew Mortonad3fee52005-06-20 14:30:36 -07001214 struct ipw_priv *p = d->driver_data;
James Ketrenos43f66a62005-03-25 12:31:53 -06001215
1216 sscanf(buf, "%x", &reg);
1217 ipw_write_reg32(p, CX2_INTERNAL_CMD_EVENT, reg);
1218 return strnlen(buf, count);
1219}
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001220
1221static DEVICE_ATTR(command_event_reg, S_IWUSR | S_IRUGO,
1222 show_command_event_reg, store_command_event_reg);
James Ketrenos43f66a62005-03-25 12:31:53 -06001223
Andrew Mortonad3fee52005-06-20 14:30:36 -07001224static ssize_t show_mem_gpio_reg(struct device *d,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001225 struct device_attribute *attr, char *buf)
James Ketrenos43f66a62005-03-25 12:31:53 -06001226{
1227 u32 reg = 0;
Andrew Mortonad3fee52005-06-20 14:30:36 -07001228 struct ipw_priv *p = d->driver_data;
James Ketrenos43f66a62005-03-25 12:31:53 -06001229
1230 reg = ipw_read_reg32(p, 0x301100);
1231 return sprintf(buf, "0x%08x\n", reg);
1232}
Andrew Mortonad3fee52005-06-20 14:30:36 -07001233static ssize_t store_mem_gpio_reg(struct device *d,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001234 struct device_attribute *attr,
1235 const char *buf, size_t count)
James Ketrenos43f66a62005-03-25 12:31:53 -06001236{
1237 u32 reg;
Andrew Mortonad3fee52005-06-20 14:30:36 -07001238 struct ipw_priv *p = d->driver_data;
James Ketrenos43f66a62005-03-25 12:31:53 -06001239
1240 sscanf(buf, "%x", &reg);
1241 ipw_write_reg32(p, 0x301100, reg);
1242 return strnlen(buf, count);
1243}
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001244
1245static DEVICE_ATTR(mem_gpio_reg, S_IWUSR | S_IRUGO,
1246 show_mem_gpio_reg, store_mem_gpio_reg);
James Ketrenos43f66a62005-03-25 12:31:53 -06001247
Andrew Mortonad3fee52005-06-20 14:30:36 -07001248static ssize_t show_indirect_dword(struct device *d,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001249 struct device_attribute *attr, char *buf)
James Ketrenos43f66a62005-03-25 12:31:53 -06001250{
1251 u32 reg = 0;
Andrew Mortonad3fee52005-06-20 14:30:36 -07001252 struct ipw_priv *priv = d->driver_data;
Jeff Garzikbf794512005-07-31 13:07:26 -04001253 if (priv->status & STATUS_INDIRECT_DWORD)
James Ketrenos43f66a62005-03-25 12:31:53 -06001254 reg = ipw_read_reg32(priv, priv->indirect_dword);
Jeff Garzikbf794512005-07-31 13:07:26 -04001255 else
James Ketrenos43f66a62005-03-25 12:31:53 -06001256 reg = 0;
Jeff Garzikbf794512005-07-31 13:07:26 -04001257
James Ketrenos43f66a62005-03-25 12:31:53 -06001258 return sprintf(buf, "0x%08x\n", reg);
1259}
Andrew Mortonad3fee52005-06-20 14:30:36 -07001260static ssize_t store_indirect_dword(struct device *d,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001261 struct device_attribute *attr,
1262 const char *buf, size_t count)
James Ketrenos43f66a62005-03-25 12:31:53 -06001263{
Andrew Mortonad3fee52005-06-20 14:30:36 -07001264 struct ipw_priv *priv = d->driver_data;
James Ketrenos43f66a62005-03-25 12:31:53 -06001265
1266 sscanf(buf, "%x", &priv->indirect_dword);
1267 priv->status |= STATUS_INDIRECT_DWORD;
1268 return strnlen(buf, count);
1269}
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001270
1271static DEVICE_ATTR(indirect_dword, S_IWUSR | S_IRUGO,
1272 show_indirect_dword, store_indirect_dword);
James Ketrenos43f66a62005-03-25 12:31:53 -06001273
Andrew Mortonad3fee52005-06-20 14:30:36 -07001274static ssize_t show_indirect_byte(struct device *d,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001275 struct device_attribute *attr, char *buf)
James Ketrenos43f66a62005-03-25 12:31:53 -06001276{
1277 u8 reg = 0;
Andrew Mortonad3fee52005-06-20 14:30:36 -07001278 struct ipw_priv *priv = d->driver_data;
Jeff Garzikbf794512005-07-31 13:07:26 -04001279 if (priv->status & STATUS_INDIRECT_BYTE)
James Ketrenos43f66a62005-03-25 12:31:53 -06001280 reg = ipw_read_reg8(priv, priv->indirect_byte);
Jeff Garzikbf794512005-07-31 13:07:26 -04001281 else
James Ketrenos43f66a62005-03-25 12:31:53 -06001282 reg = 0;
1283
1284 return sprintf(buf, "0x%02x\n", reg);
1285}
Andrew Mortonad3fee52005-06-20 14:30:36 -07001286static ssize_t store_indirect_byte(struct device *d,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001287 struct device_attribute *attr,
1288 const char *buf, size_t count)
James Ketrenos43f66a62005-03-25 12:31:53 -06001289{
Andrew Mortonad3fee52005-06-20 14:30:36 -07001290 struct ipw_priv *priv = d->driver_data;
James Ketrenos43f66a62005-03-25 12:31:53 -06001291
1292 sscanf(buf, "%x", &priv->indirect_byte);
1293 priv->status |= STATUS_INDIRECT_BYTE;
1294 return strnlen(buf, count);
1295}
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001296
1297static DEVICE_ATTR(indirect_byte, S_IWUSR | S_IRUGO,
James Ketrenos43f66a62005-03-25 12:31:53 -06001298 show_indirect_byte, store_indirect_byte);
1299
Andrew Mortonad3fee52005-06-20 14:30:36 -07001300static ssize_t show_direct_dword(struct device *d,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001301 struct device_attribute *attr, char *buf)
James Ketrenos43f66a62005-03-25 12:31:53 -06001302{
1303 u32 reg = 0;
Andrew Mortonad3fee52005-06-20 14:30:36 -07001304 struct ipw_priv *priv = d->driver_data;
James Ketrenos43f66a62005-03-25 12:31:53 -06001305
Jeff Garzikbf794512005-07-31 13:07:26 -04001306 if (priv->status & STATUS_DIRECT_DWORD)
James Ketrenos43f66a62005-03-25 12:31:53 -06001307 reg = ipw_read32(priv, priv->direct_dword);
Jeff Garzikbf794512005-07-31 13:07:26 -04001308 else
James Ketrenos43f66a62005-03-25 12:31:53 -06001309 reg = 0;
1310
1311 return sprintf(buf, "0x%08x\n", reg);
1312}
Andrew Mortonad3fee52005-06-20 14:30:36 -07001313static ssize_t store_direct_dword(struct device *d,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001314 struct device_attribute *attr,
1315 const char *buf, size_t count)
James Ketrenos43f66a62005-03-25 12:31:53 -06001316{
Andrew Mortonad3fee52005-06-20 14:30:36 -07001317 struct ipw_priv *priv = d->driver_data;
James Ketrenos43f66a62005-03-25 12:31:53 -06001318
1319 sscanf(buf, "%x", &priv->direct_dword);
1320 priv->status |= STATUS_DIRECT_DWORD;
1321 return strnlen(buf, count);
1322}
James Ketrenos43f66a62005-03-25 12:31:53 -06001323
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001324static DEVICE_ATTR(direct_dword, S_IWUSR | S_IRUGO,
1325 show_direct_dword, store_direct_dword);
James Ketrenos43f66a62005-03-25 12:31:53 -06001326
1327static inline int rf_kill_active(struct ipw_priv *priv)
1328{
1329 if (0 == (ipw_read32(priv, 0x30) & 0x10000))
1330 priv->status |= STATUS_RF_KILL_HW;
1331 else
1332 priv->status &= ~STATUS_RF_KILL_HW;
1333
1334 return (priv->status & STATUS_RF_KILL_HW) ? 1 : 0;
1335}
1336
Andrew Mortonad3fee52005-06-20 14:30:36 -07001337static ssize_t show_rf_kill(struct device *d, struct device_attribute *attr,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001338 char *buf)
James Ketrenos43f66a62005-03-25 12:31:53 -06001339{
1340 /* 0 - RF kill not enabled
Jeff Garzikbf794512005-07-31 13:07:26 -04001341 1 - SW based RF kill active (sysfs)
James Ketrenos43f66a62005-03-25 12:31:53 -06001342 2 - HW based RF kill active
1343 3 - Both HW and SW baed RF kill active */
Andrew Mortonad3fee52005-06-20 14:30:36 -07001344 struct ipw_priv *priv = d->driver_data;
James Ketrenos43f66a62005-03-25 12:31:53 -06001345 int val = ((priv->status & STATUS_RF_KILL_SW) ? 0x1 : 0x0) |
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001346 (rf_kill_active(priv) ? 0x2 : 0x0);
James Ketrenos43f66a62005-03-25 12:31:53 -06001347 return sprintf(buf, "%i\n", val);
1348}
1349
1350static int ipw_radio_kill_sw(struct ipw_priv *priv, int disable_radio)
1351{
Jeff Garzikbf794512005-07-31 13:07:26 -04001352 if ((disable_radio ? 1 : 0) ==
James Ketrenosea2b26e2005-08-24 21:25:16 -05001353 ((priv->status & STATUS_RF_KILL_SW) ? 1 : 0))
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001354 return 0;
James Ketrenos43f66a62005-03-25 12:31:53 -06001355
1356 IPW_DEBUG_RF_KILL("Manual SW RF Kill set to: RADIO %s\n",
1357 disable_radio ? "OFF" : "ON");
1358
1359 if (disable_radio) {
1360 priv->status |= STATUS_RF_KILL_SW;
1361
James Ketrenosa613bff2005-08-24 21:43:11 -05001362 if (priv->workqueue)
James Ketrenos43f66a62005-03-25 12:31:53 -06001363 cancel_delayed_work(&priv->request_scan);
James Ketrenos43f66a62005-03-25 12:31:53 -06001364 wake_up_interruptible(&priv->wait_command_queue);
1365 queue_work(priv->workqueue, &priv->down);
1366 } else {
1367 priv->status &= ~STATUS_RF_KILL_SW;
1368 if (rf_kill_active(priv)) {
1369 IPW_DEBUG_RF_KILL("Can not turn radio back on - "
1370 "disabled by HW switch\n");
1371 /* Make sure the RF_KILL check timer is running */
1372 cancel_delayed_work(&priv->rf_kill);
Jeff Garzikbf794512005-07-31 13:07:26 -04001373 queue_delayed_work(priv->workqueue, &priv->rf_kill,
James Ketrenos43f66a62005-03-25 12:31:53 -06001374 2 * HZ);
Jeff Garzikbf794512005-07-31 13:07:26 -04001375 } else
James Ketrenos43f66a62005-03-25 12:31:53 -06001376 queue_work(priv->workqueue, &priv->up);
1377 }
1378
1379 return 1;
1380}
1381
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001382static ssize_t store_rf_kill(struct device *d, struct device_attribute *attr,
1383 const char *buf, size_t count)
James Ketrenos43f66a62005-03-25 12:31:53 -06001384{
Andrew Mortonad3fee52005-06-20 14:30:36 -07001385 struct ipw_priv *priv = d->driver_data;
Jeff Garzikbf794512005-07-31 13:07:26 -04001386
James Ketrenos43f66a62005-03-25 12:31:53 -06001387 ipw_radio_kill_sw(priv, buf[0] == '1');
1388
1389 return count;
1390}
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001391
1392static DEVICE_ATTR(rf_kill, S_IWUSR | S_IRUGO, show_rf_kill, store_rf_kill);
James Ketrenos43f66a62005-03-25 12:31:53 -06001393
James Ketrenosea2b26e2005-08-24 21:25:16 -05001394static void notify_wx_assoc_event(struct ipw_priv *priv)
1395{
1396 union iwreq_data wrqu;
1397 wrqu.ap_addr.sa_family = ARPHRD_ETHER;
1398 if (priv->status & STATUS_ASSOCIATED)
1399 memcpy(wrqu.ap_addr.sa_data, priv->bssid, ETH_ALEN);
1400 else
1401 memset(wrqu.ap_addr.sa_data, 0, ETH_ALEN);
1402 wireless_send_event(priv->net_dev, SIOCGIWAP, &wrqu, NULL);
1403}
1404
James Ketrenos43f66a62005-03-25 12:31:53 -06001405static void ipw_irq_tasklet(struct ipw_priv *priv)
1406{
1407 u32 inta, inta_mask, handled = 0;
1408 unsigned long flags;
1409 int rc = 0;
1410
1411 spin_lock_irqsave(&priv->lock, flags);
1412
1413 inta = ipw_read32(priv, CX2_INTA_RW);
1414 inta_mask = ipw_read32(priv, CX2_INTA_MASK_R);
1415 inta &= (CX2_INTA_MASK_ALL & inta_mask);
1416
1417 /* Add any cached INTA values that need to be handled */
1418 inta |= priv->isr_inta;
1419
1420 /* handle all the justifications for the interrupt */
1421 if (inta & CX2_INTA_BIT_RX_TRANSFER) {
1422 ipw_rx(priv);
1423 handled |= CX2_INTA_BIT_RX_TRANSFER;
1424 }
1425
1426 if (inta & CX2_INTA_BIT_TX_CMD_QUEUE) {
1427 IPW_DEBUG_HC("Command completed.\n");
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001428 rc = ipw_queue_tx_reclaim(priv, &priv->txq_cmd, -1);
James Ketrenos43f66a62005-03-25 12:31:53 -06001429 priv->status &= ~STATUS_HCMD_ACTIVE;
1430 wake_up_interruptible(&priv->wait_command_queue);
1431 handled |= CX2_INTA_BIT_TX_CMD_QUEUE;
1432 }
1433
1434 if (inta & CX2_INTA_BIT_TX_QUEUE_1) {
1435 IPW_DEBUG_TX("TX_QUEUE_1\n");
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001436 rc = ipw_queue_tx_reclaim(priv, &priv->txq[0], 0);
James Ketrenos43f66a62005-03-25 12:31:53 -06001437 handled |= CX2_INTA_BIT_TX_QUEUE_1;
1438 }
1439
1440 if (inta & CX2_INTA_BIT_TX_QUEUE_2) {
1441 IPW_DEBUG_TX("TX_QUEUE_2\n");
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001442 rc = ipw_queue_tx_reclaim(priv, &priv->txq[1], 1);
James Ketrenos43f66a62005-03-25 12:31:53 -06001443 handled |= CX2_INTA_BIT_TX_QUEUE_2;
1444 }
1445
1446 if (inta & CX2_INTA_BIT_TX_QUEUE_3) {
1447 IPW_DEBUG_TX("TX_QUEUE_3\n");
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001448 rc = ipw_queue_tx_reclaim(priv, &priv->txq[2], 2);
James Ketrenos43f66a62005-03-25 12:31:53 -06001449 handled |= CX2_INTA_BIT_TX_QUEUE_3;
1450 }
1451
1452 if (inta & CX2_INTA_BIT_TX_QUEUE_4) {
1453 IPW_DEBUG_TX("TX_QUEUE_4\n");
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001454 rc = ipw_queue_tx_reclaim(priv, &priv->txq[3], 3);
James Ketrenos43f66a62005-03-25 12:31:53 -06001455 handled |= CX2_INTA_BIT_TX_QUEUE_4;
1456 }
1457
1458 if (inta & CX2_INTA_BIT_STATUS_CHANGE) {
1459 IPW_WARNING("STATUS_CHANGE\n");
1460 handled |= CX2_INTA_BIT_STATUS_CHANGE;
1461 }
1462
1463 if (inta & CX2_INTA_BIT_BEACON_PERIOD_EXPIRED) {
1464 IPW_WARNING("TX_PERIOD_EXPIRED\n");
1465 handled |= CX2_INTA_BIT_BEACON_PERIOD_EXPIRED;
1466 }
1467
1468 if (inta & CX2_INTA_BIT_SLAVE_MODE_HOST_CMD_DONE) {
1469 IPW_WARNING("HOST_CMD_DONE\n");
1470 handled |= CX2_INTA_BIT_SLAVE_MODE_HOST_CMD_DONE;
1471 }
1472
1473 if (inta & CX2_INTA_BIT_FW_INITIALIZATION_DONE) {
1474 IPW_WARNING("FW_INITIALIZATION_DONE\n");
1475 handled |= CX2_INTA_BIT_FW_INITIALIZATION_DONE;
1476 }
1477
1478 if (inta & CX2_INTA_BIT_FW_CARD_DISABLE_PHY_OFF_DONE) {
1479 IPW_WARNING("PHY_OFF_DONE\n");
1480 handled |= CX2_INTA_BIT_FW_CARD_DISABLE_PHY_OFF_DONE;
1481 }
1482
1483 if (inta & CX2_INTA_BIT_RF_KILL_DONE) {
1484 IPW_DEBUG_RF_KILL("RF_KILL_DONE\n");
1485 priv->status |= STATUS_RF_KILL_HW;
1486 wake_up_interruptible(&priv->wait_command_queue);
James Ketrenosea2b26e2005-08-24 21:25:16 -05001487 priv->status &= ~(STATUS_ASSOCIATED | STATUS_ASSOCIATING);
James Ketrenos43f66a62005-03-25 12:31:53 -06001488 cancel_delayed_work(&priv->request_scan);
James Ketrenosa613bff2005-08-24 21:43:11 -05001489 schedule_work(&priv->link_down);
James Ketrenos43f66a62005-03-25 12:31:53 -06001490 queue_delayed_work(priv->workqueue, &priv->rf_kill, 2 * HZ);
1491 handled |= CX2_INTA_BIT_RF_KILL_DONE;
1492 }
Jeff Garzikbf794512005-07-31 13:07:26 -04001493
James Ketrenos43f66a62005-03-25 12:31:53 -06001494 if (inta & CX2_INTA_BIT_FATAL_ERROR) {
1495 IPW_ERROR("Firmware error detected. Restarting.\n");
1496#ifdef CONFIG_IPW_DEBUG
1497 if (ipw_debug_level & IPW_DL_FW_ERRORS) {
1498 ipw_dump_nic_error_log(priv);
1499 ipw_dump_nic_event_log(priv);
1500 }
1501#endif
1502 queue_work(priv->workqueue, &priv->adapter_restart);
1503 handled |= CX2_INTA_BIT_FATAL_ERROR;
1504 }
1505
1506 if (inta & CX2_INTA_BIT_PARITY_ERROR) {
1507 IPW_ERROR("Parity error\n");
1508 handled |= CX2_INTA_BIT_PARITY_ERROR;
1509 }
1510
1511 if (handled != inta) {
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001512 IPW_ERROR("Unhandled INTA bits 0x%08x\n", inta & ~handled);
James Ketrenos43f66a62005-03-25 12:31:53 -06001513 }
1514
1515 /* enable all interrupts */
1516 ipw_enable_interrupts(priv);
1517
1518 spin_unlock_irqrestore(&priv->lock, flags);
1519}
Jeff Garzikbf794512005-07-31 13:07:26 -04001520
James Ketrenos43f66a62005-03-25 12:31:53 -06001521#ifdef CONFIG_IPW_DEBUG
1522#define IPW_CMD(x) case IPW_CMD_ ## x : return #x
1523static char *get_cmd_string(u8 cmd)
1524{
1525 switch (cmd) {
1526 IPW_CMD(HOST_COMPLETE);
Jeff Garzikbf794512005-07-31 13:07:26 -04001527 IPW_CMD(POWER_DOWN);
1528 IPW_CMD(SYSTEM_CONFIG);
1529 IPW_CMD(MULTICAST_ADDRESS);
1530 IPW_CMD(SSID);
1531 IPW_CMD(ADAPTER_ADDRESS);
1532 IPW_CMD(PORT_TYPE);
1533 IPW_CMD(RTS_THRESHOLD);
1534 IPW_CMD(FRAG_THRESHOLD);
1535 IPW_CMD(POWER_MODE);
1536 IPW_CMD(WEP_KEY);
1537 IPW_CMD(TGI_TX_KEY);
1538 IPW_CMD(SCAN_REQUEST);
1539 IPW_CMD(SCAN_REQUEST_EXT);
1540 IPW_CMD(ASSOCIATE);
1541 IPW_CMD(SUPPORTED_RATES);
1542 IPW_CMD(SCAN_ABORT);
1543 IPW_CMD(TX_FLUSH);
1544 IPW_CMD(QOS_PARAMETERS);
1545 IPW_CMD(DINO_CONFIG);
1546 IPW_CMD(RSN_CAPABILITIES);
1547 IPW_CMD(RX_KEY);
1548 IPW_CMD(CARD_DISABLE);
1549 IPW_CMD(SEED_NUMBER);
1550 IPW_CMD(TX_POWER);
1551 IPW_CMD(COUNTRY_INFO);
1552 IPW_CMD(AIRONET_INFO);
1553 IPW_CMD(AP_TX_POWER);
1554 IPW_CMD(CCKM_INFO);
1555 IPW_CMD(CCX_VER_INFO);
1556 IPW_CMD(SET_CALIBRATION);
1557 IPW_CMD(SENSITIVITY_CALIB);
1558 IPW_CMD(RETRY_LIMIT);
1559 IPW_CMD(IPW_PRE_POWER_DOWN);
1560 IPW_CMD(VAP_BEACON_TEMPLATE);
1561 IPW_CMD(VAP_DTIM_PERIOD);
1562 IPW_CMD(EXT_SUPPORTED_RATES);
1563 IPW_CMD(VAP_LOCAL_TX_PWR_CONSTRAINT);
1564 IPW_CMD(VAP_QUIET_INTERVALS);
1565 IPW_CMD(VAP_CHANNEL_SWITCH);
1566 IPW_CMD(VAP_MANDATORY_CHANNELS);
1567 IPW_CMD(VAP_CELL_PWR_LIMIT);
1568 IPW_CMD(VAP_CF_PARAM_SET);
1569 IPW_CMD(VAP_SET_BEACONING_STATE);
1570 IPW_CMD(MEASUREMENT);
1571 IPW_CMD(POWER_CAPABILITY);
1572 IPW_CMD(SUPPORTED_CHANNELS);
1573 IPW_CMD(TPC_REPORT);
1574 IPW_CMD(WME_INFO);
1575 IPW_CMD(PRODUCTION_COMMAND);
1576 default:
James Ketrenos43f66a62005-03-25 12:31:53 -06001577 return "UNKNOWN";
1578 }
1579}
James Ketrenosea2b26e2005-08-24 21:25:16 -05001580#endif
James Ketrenos43f66a62005-03-25 12:31:53 -06001581
1582#define HOST_COMPLETE_TIMEOUT HZ
1583static int ipw_send_cmd(struct ipw_priv *priv, struct host_cmd *cmd)
1584{
1585 int rc = 0;
James Ketrenosa613bff2005-08-24 21:43:11 -05001586 unsigned long flags;
James Ketrenos43f66a62005-03-25 12:31:53 -06001587
James Ketrenosa613bff2005-08-24 21:43:11 -05001588 spin_lock_irqsave(&priv->lock, flags);
James Ketrenos43f66a62005-03-25 12:31:53 -06001589 if (priv->status & STATUS_HCMD_ACTIVE) {
1590 IPW_ERROR("Already sending a command\n");
James Ketrenosa613bff2005-08-24 21:43:11 -05001591 spin_unlock_irqrestore(&priv->lock, flags);
James Ketrenos43f66a62005-03-25 12:31:53 -06001592 return -1;
1593 }
1594
1595 priv->status |= STATUS_HCMD_ACTIVE;
Jeff Garzikbf794512005-07-31 13:07:26 -04001596
1597 IPW_DEBUG_HC("Sending %s command (#%d), %d bytes\n",
James Ketrenos43f66a62005-03-25 12:31:53 -06001598 get_cmd_string(cmd->cmd), cmd->cmd, cmd->len);
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001599 printk_buf(IPW_DL_HOST_COMMAND, (u8 *) cmd->param, cmd->len);
James Ketrenos43f66a62005-03-25 12:31:53 -06001600
1601 rc = ipw_queue_tx_hcmd(priv, cmd->cmd, &cmd->param, cmd->len, 0);
James Ketrenosa613bff2005-08-24 21:43:11 -05001602 if (rc) {
1603 priv->status &= ~STATUS_HCMD_ACTIVE;
1604 spin_unlock_irqrestore(&priv->lock, flags);
James Ketrenos43f66a62005-03-25 12:31:53 -06001605 return rc;
James Ketrenosa613bff2005-08-24 21:43:11 -05001606 }
1607 spin_unlock_irqrestore(&priv->lock, flags);
James Ketrenos43f66a62005-03-25 12:31:53 -06001608
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001609 rc = wait_event_interruptible_timeout(priv->wait_command_queue,
1610 !(priv->
1611 status & STATUS_HCMD_ACTIVE),
1612 HOST_COMPLETE_TIMEOUT);
James Ketrenos43f66a62005-03-25 12:31:53 -06001613 if (rc == 0) {
James Ketrenosa613bff2005-08-24 21:43:11 -05001614 spin_lock_irqsave(&priv->lock, flags);
1615 if (priv->status & STATUS_HCMD_ACTIVE) {
1616 IPW_DEBUG_INFO("Command completion failed out after "
1617 "%dms.\n",
1618 1000 * (HOST_COMPLETE_TIMEOUT / HZ));
1619 priv->status &= ~STATUS_HCMD_ACTIVE;
1620 spin_unlock_irqrestore(&priv->lock, flags);
1621 return -EIO;
1622 }
1623 spin_unlock_irqrestore(&priv->lock, flags);
James Ketrenos43f66a62005-03-25 12:31:53 -06001624 }
James Ketrenosa613bff2005-08-24 21:43:11 -05001625
James Ketrenos43f66a62005-03-25 12:31:53 -06001626 if (priv->status & STATUS_RF_KILL_MASK) {
1627 IPW_DEBUG_INFO("Command aborted due to RF Kill Switch\n");
1628 return -EIO;
1629 }
1630
1631 return 0;
1632}
1633
1634static int ipw_send_host_complete(struct ipw_priv *priv)
1635{
1636 struct host_cmd cmd = {
1637 .cmd = IPW_CMD_HOST_COMPLETE,
1638 .len = 0
1639 };
1640
1641 if (!priv) {
1642 IPW_ERROR("Invalid args\n");
1643 return -1;
1644 }
1645
1646 if (ipw_send_cmd(priv, &cmd)) {
1647 IPW_ERROR("failed to send HOST_COMPLETE command\n");
1648 return -1;
1649 }
Jeff Garzikbf794512005-07-31 13:07:26 -04001650
James Ketrenos43f66a62005-03-25 12:31:53 -06001651 return 0;
1652}
1653
Jeff Garzikbf794512005-07-31 13:07:26 -04001654static int ipw_send_system_config(struct ipw_priv *priv,
James Ketrenos43f66a62005-03-25 12:31:53 -06001655 struct ipw_sys_config *config)
1656{
1657 struct host_cmd cmd = {
1658 .cmd = IPW_CMD_SYSTEM_CONFIG,
1659 .len = sizeof(*config)
1660 };
1661
1662 if (!priv || !config) {
1663 IPW_ERROR("Invalid args\n");
1664 return -1;
1665 }
1666
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001667 memcpy(&cmd.param, config, sizeof(*config));
James Ketrenos43f66a62005-03-25 12:31:53 -06001668 if (ipw_send_cmd(priv, &cmd)) {
1669 IPW_ERROR("failed to send SYSTEM_CONFIG command\n");
1670 return -1;
1671 }
1672
1673 return 0;
1674}
1675
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001676static int ipw_send_ssid(struct ipw_priv *priv, u8 * ssid, int len)
James Ketrenos43f66a62005-03-25 12:31:53 -06001677{
1678 struct host_cmd cmd = {
1679 .cmd = IPW_CMD_SSID,
1680 .len = min(len, IW_ESSID_MAX_SIZE)
1681 };
1682
1683 if (!priv || !ssid) {
1684 IPW_ERROR("Invalid args\n");
1685 return -1;
1686 }
1687
1688 memcpy(&cmd.param, ssid, cmd.len);
1689 if (ipw_send_cmd(priv, &cmd)) {
1690 IPW_ERROR("failed to send SSID command\n");
1691 return -1;
1692 }
Jeff Garzikbf794512005-07-31 13:07:26 -04001693
James Ketrenos43f66a62005-03-25 12:31:53 -06001694 return 0;
1695}
1696
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001697static int ipw_send_adapter_address(struct ipw_priv *priv, u8 * mac)
James Ketrenos43f66a62005-03-25 12:31:53 -06001698{
1699 struct host_cmd cmd = {
1700 .cmd = IPW_CMD_ADAPTER_ADDRESS,
1701 .len = ETH_ALEN
1702 };
1703
1704 if (!priv || !mac) {
1705 IPW_ERROR("Invalid args\n");
1706 return -1;
1707 }
1708
1709 IPW_DEBUG_INFO("%s: Setting MAC to " MAC_FMT "\n",
1710 priv->net_dev->name, MAC_ARG(mac));
1711
1712 memcpy(&cmd.param, mac, ETH_ALEN);
1713
1714 if (ipw_send_cmd(priv, &cmd)) {
1715 IPW_ERROR("failed to send ADAPTER_ADDRESS command\n");
1716 return -1;
1717 }
Jeff Garzikbf794512005-07-31 13:07:26 -04001718
James Ketrenos43f66a62005-03-25 12:31:53 -06001719 return 0;
1720}
1721
James Ketrenosa613bff2005-08-24 21:43:11 -05001722/*
1723 * NOTE: This must be executed from our workqueue as it results in udelay
1724 * being called which may corrupt the keyboard if executed on default
1725 * workqueue
1726 */
James Ketrenos43f66a62005-03-25 12:31:53 -06001727static void ipw_adapter_restart(void *adapter)
1728{
1729 struct ipw_priv *priv = adapter;
1730
1731 if (priv->status & STATUS_RF_KILL_MASK)
1732 return;
1733
1734 ipw_down(priv);
1735 if (ipw_up(priv)) {
1736 IPW_ERROR("Failed to up device\n");
1737 return;
1738 }
1739}
1740
James Ketrenosc848d0a2005-08-24 21:56:24 -05001741static void ipw_bg_adapter_restart(void *data)
1742{
1743 struct ipw_priv *priv = data;
1744 down(&priv->sem);
1745 ipw_adapter_restart(data);
1746 up(&priv->sem);
1747}
1748
James Ketrenos43f66a62005-03-25 12:31:53 -06001749#define IPW_SCAN_CHECK_WATCHDOG (5 * HZ)
1750
1751static void ipw_scan_check(void *data)
1752{
1753 struct ipw_priv *priv = data;
1754 if (priv->status & (STATUS_SCANNING | STATUS_SCAN_ABORTING)) {
1755 IPW_DEBUG_SCAN("Scan completion watchdog resetting "
Jeff Garzikbf794512005-07-31 13:07:26 -04001756 "adapter (%dms).\n",
James Ketrenos43f66a62005-03-25 12:31:53 -06001757 IPW_SCAN_CHECK_WATCHDOG / 100);
James Ketrenosa613bff2005-08-24 21:43:11 -05001758 queue_work(priv->workqueue, &priv->adapter_restart);
James Ketrenos43f66a62005-03-25 12:31:53 -06001759 }
1760}
1761
James Ketrenosc848d0a2005-08-24 21:56:24 -05001762static void ipw_bg_scan_check(void *data)
1763{
1764 struct ipw_priv *priv = data;
1765 down(&priv->sem);
1766 ipw_scan_check(data);
1767 up(&priv->sem);
1768}
1769
James Ketrenos43f66a62005-03-25 12:31:53 -06001770static int ipw_send_scan_request_ext(struct ipw_priv *priv,
1771 struct ipw_scan_request_ext *request)
1772{
1773 struct host_cmd cmd = {
1774 .cmd = IPW_CMD_SCAN_REQUEST_EXT,
1775 .len = sizeof(*request)
1776 };
1777
1778 if (!priv || !request) {
1779 IPW_ERROR("Invalid args\n");
1780 return -1;
1781 }
1782
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001783 memcpy(&cmd.param, request, sizeof(*request));
James Ketrenos43f66a62005-03-25 12:31:53 -06001784 if (ipw_send_cmd(priv, &cmd)) {
1785 IPW_ERROR("failed to send SCAN_REQUEST_EXT command\n");
1786 return -1;
1787 }
Jeff Garzikbf794512005-07-31 13:07:26 -04001788
1789 queue_delayed_work(priv->workqueue, &priv->scan_check,
James Ketrenos43f66a62005-03-25 12:31:53 -06001790 IPW_SCAN_CHECK_WATCHDOG);
1791 return 0;
1792}
1793
1794static int ipw_send_scan_abort(struct ipw_priv *priv)
1795{
1796 struct host_cmd cmd = {
1797 .cmd = IPW_CMD_SCAN_ABORT,
1798 .len = 0
1799 };
1800
1801 if (!priv) {
1802 IPW_ERROR("Invalid args\n");
1803 return -1;
1804 }
1805
1806 if (ipw_send_cmd(priv, &cmd)) {
1807 IPW_ERROR("failed to send SCAN_ABORT command\n");
1808 return -1;
1809 }
Jeff Garzikbf794512005-07-31 13:07:26 -04001810
James Ketrenos43f66a62005-03-25 12:31:53 -06001811 return 0;
1812}
1813
1814static int ipw_set_sensitivity(struct ipw_priv *priv, u16 sens)
1815{
1816 struct host_cmd cmd = {
1817 .cmd = IPW_CMD_SENSITIVITY_CALIB,
1818 .len = sizeof(struct ipw_sensitivity_calib)
1819 };
1820 struct ipw_sensitivity_calib *calib = (struct ipw_sensitivity_calib *)
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001821 &cmd.param;
James Ketrenos43f66a62005-03-25 12:31:53 -06001822 calib->beacon_rssi_raw = sens;
1823 if (ipw_send_cmd(priv, &cmd)) {
1824 IPW_ERROR("failed to send SENSITIVITY CALIB command\n");
1825 return -1;
1826 }
1827
1828 return 0;
1829}
1830
1831static int ipw_send_associate(struct ipw_priv *priv,
1832 struct ipw_associate *associate)
1833{
1834 struct host_cmd cmd = {
1835 .cmd = IPW_CMD_ASSOCIATE,
1836 .len = sizeof(*associate)
1837 };
1838
James Ketrenosa613bff2005-08-24 21:43:11 -05001839 struct ipw_associate tmp_associate;
1840 memcpy(&tmp_associate, associate, sizeof(*associate));
1841 tmp_associate.policy_support =
1842 cpu_to_le16(tmp_associate.policy_support);
1843 tmp_associate.assoc_tsf_msw = cpu_to_le32(tmp_associate.assoc_tsf_msw);
1844 tmp_associate.assoc_tsf_lsw = cpu_to_le32(tmp_associate.assoc_tsf_lsw);
1845 tmp_associate.capability = cpu_to_le16(tmp_associate.capability);
1846 tmp_associate.listen_interval =
1847 cpu_to_le16(tmp_associate.listen_interval);
1848 tmp_associate.beacon_interval =
1849 cpu_to_le16(tmp_associate.beacon_interval);
1850 tmp_associate.atim_window = cpu_to_le16(tmp_associate.atim_window);
1851
James Ketrenos43f66a62005-03-25 12:31:53 -06001852 if (!priv || !associate) {
1853 IPW_ERROR("Invalid args\n");
1854 return -1;
1855 }
1856
James Ketrenosa613bff2005-08-24 21:43:11 -05001857 memcpy(&cmd.param, &tmp_associate, sizeof(*associate));
James Ketrenos43f66a62005-03-25 12:31:53 -06001858 if (ipw_send_cmd(priv, &cmd)) {
1859 IPW_ERROR("failed to send ASSOCIATE command\n");
1860 return -1;
1861 }
Jeff Garzikbf794512005-07-31 13:07:26 -04001862
James Ketrenos43f66a62005-03-25 12:31:53 -06001863 return 0;
1864}
1865
1866static int ipw_send_supported_rates(struct ipw_priv *priv,
1867 struct ipw_supported_rates *rates)
1868{
1869 struct host_cmd cmd = {
1870 .cmd = IPW_CMD_SUPPORTED_RATES,
1871 .len = sizeof(*rates)
1872 };
1873
1874 if (!priv || !rates) {
1875 IPW_ERROR("Invalid args\n");
1876 return -1;
1877 }
1878
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001879 memcpy(&cmd.param, rates, sizeof(*rates));
James Ketrenos43f66a62005-03-25 12:31:53 -06001880 if (ipw_send_cmd(priv, &cmd)) {
1881 IPW_ERROR("failed to send SUPPORTED_RATES command\n");
1882 return -1;
1883 }
Jeff Garzikbf794512005-07-31 13:07:26 -04001884
James Ketrenos43f66a62005-03-25 12:31:53 -06001885 return 0;
1886}
1887
1888static int ipw_set_random_seed(struct ipw_priv *priv)
1889{
1890 struct host_cmd cmd = {
1891 .cmd = IPW_CMD_SEED_NUMBER,
1892 .len = sizeof(u32)
1893 };
1894
1895 if (!priv) {
1896 IPW_ERROR("Invalid args\n");
1897 return -1;
1898 }
1899
1900 get_random_bytes(&cmd.param, sizeof(u32));
1901
1902 if (ipw_send_cmd(priv, &cmd)) {
1903 IPW_ERROR("failed to send SEED_NUMBER command\n");
1904 return -1;
1905 }
Jeff Garzikbf794512005-07-31 13:07:26 -04001906
James Ketrenos43f66a62005-03-25 12:31:53 -06001907 return 0;
1908}
1909
1910#if 0
1911static int ipw_send_card_disable(struct ipw_priv *priv, u32 phy_off)
1912{
1913 struct host_cmd cmd = {
1914 .cmd = IPW_CMD_CARD_DISABLE,
1915 .len = sizeof(u32)
1916 };
1917
1918 if (!priv) {
1919 IPW_ERROR("Invalid args\n");
1920 return -1;
1921 }
1922
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001923 *((u32 *) & cmd.param) = phy_off;
James Ketrenos43f66a62005-03-25 12:31:53 -06001924
1925 if (ipw_send_cmd(priv, &cmd)) {
1926 IPW_ERROR("failed to send CARD_DISABLE command\n");
1927 return -1;
1928 }
Jeff Garzikbf794512005-07-31 13:07:26 -04001929
James Ketrenos43f66a62005-03-25 12:31:53 -06001930 return 0;
1931}
1932#endif
1933
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001934static int ipw_send_tx_power(struct ipw_priv *priv, struct ipw_tx_power *power)
James Ketrenos43f66a62005-03-25 12:31:53 -06001935{
1936 struct host_cmd cmd = {
1937 .cmd = IPW_CMD_TX_POWER,
1938 .len = sizeof(*power)
1939 };
1940
1941 if (!priv || !power) {
1942 IPW_ERROR("Invalid args\n");
1943 return -1;
1944 }
1945
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001946 memcpy(&cmd.param, power, sizeof(*power));
James Ketrenos43f66a62005-03-25 12:31:53 -06001947 if (ipw_send_cmd(priv, &cmd)) {
1948 IPW_ERROR("failed to send TX_POWER command\n");
1949 return -1;
1950 }
Jeff Garzikbf794512005-07-31 13:07:26 -04001951
James Ketrenos43f66a62005-03-25 12:31:53 -06001952 return 0;
1953}
1954
1955static int ipw_send_rts_threshold(struct ipw_priv *priv, u16 rts)
1956{
1957 struct ipw_rts_threshold rts_threshold = {
1958 .rts_threshold = rts,
1959 };
1960 struct host_cmd cmd = {
1961 .cmd = IPW_CMD_RTS_THRESHOLD,
1962 .len = sizeof(rts_threshold)
1963 };
1964
1965 if (!priv) {
1966 IPW_ERROR("Invalid args\n");
1967 return -1;
1968 }
1969
1970 memcpy(&cmd.param, &rts_threshold, sizeof(rts_threshold));
1971 if (ipw_send_cmd(priv, &cmd)) {
1972 IPW_ERROR("failed to send RTS_THRESHOLD command\n");
1973 return -1;
1974 }
1975
1976 return 0;
1977}
1978
1979static int ipw_send_frag_threshold(struct ipw_priv *priv, u16 frag)
1980{
1981 struct ipw_frag_threshold frag_threshold = {
1982 .frag_threshold = frag,
1983 };
1984 struct host_cmd cmd = {
1985 .cmd = IPW_CMD_FRAG_THRESHOLD,
1986 .len = sizeof(frag_threshold)
1987 };
1988
1989 if (!priv) {
1990 IPW_ERROR("Invalid args\n");
1991 return -1;
1992 }
1993
1994 memcpy(&cmd.param, &frag_threshold, sizeof(frag_threshold));
1995 if (ipw_send_cmd(priv, &cmd)) {
1996 IPW_ERROR("failed to send FRAG_THRESHOLD command\n");
1997 return -1;
1998 }
1999
2000 return 0;
2001}
2002
2003static int ipw_send_power_mode(struct ipw_priv *priv, u32 mode)
2004{
2005 struct host_cmd cmd = {
2006 .cmd = IPW_CMD_POWER_MODE,
2007 .len = sizeof(u32)
2008 };
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002009 u32 *param = (u32 *) (&cmd.param);
James Ketrenos43f66a62005-03-25 12:31:53 -06002010
2011 if (!priv) {
2012 IPW_ERROR("Invalid args\n");
2013 return -1;
2014 }
Jeff Garzikbf794512005-07-31 13:07:26 -04002015
James Ketrenos43f66a62005-03-25 12:31:53 -06002016 /* If on battery, set to 3, if AC set to CAM, else user
2017 * level */
2018 switch (mode) {
2019 case IPW_POWER_BATTERY:
2020 *param = IPW_POWER_INDEX_3;
2021 break;
2022 case IPW_POWER_AC:
2023 *param = IPW_POWER_MODE_CAM;
2024 break;
2025 default:
2026 *param = mode;
2027 break;
2028 }
2029
2030 if (ipw_send_cmd(priv, &cmd)) {
2031 IPW_ERROR("failed to send POWER_MODE command\n");
2032 return -1;
2033 }
2034
2035 return 0;
2036}
2037
2038/*
2039 * The IPW device contains a Microwire compatible EEPROM that stores
2040 * various data like the MAC address. Usually the firmware has exclusive
2041 * access to the eeprom, but during device initialization (before the
2042 * device driver has sent the HostComplete command to the firmware) the
2043 * device driver has read access to the EEPROM by way of indirect addressing
2044 * through a couple of memory mapped registers.
2045 *
2046 * The following is a simplified implementation for pulling data out of the
2047 * the eeprom, along with some helper functions to find information in
2048 * the per device private data's copy of the eeprom.
2049 *
2050 * NOTE: To better understand how these functions work (i.e what is a chip
2051 * select and why do have to keep driving the eeprom clock?), read
2052 * just about any data sheet for a Microwire compatible EEPROM.
2053 */
2054
2055/* write a 32 bit value into the indirect accessor register */
2056static inline void eeprom_write_reg(struct ipw_priv *p, u32 data)
2057{
2058 ipw_write_reg32(p, FW_MEM_REG_EEPROM_ACCESS, data);
Jeff Garzikbf794512005-07-31 13:07:26 -04002059
James Ketrenos43f66a62005-03-25 12:31:53 -06002060 /* the eeprom requires some time to complete the operation */
2061 udelay(p->eeprom_delay);
2062
2063 return;
2064}
2065
2066/* perform a chip select operation */
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002067static inline void eeprom_cs(struct ipw_priv *priv)
James Ketrenos43f66a62005-03-25 12:31:53 -06002068{
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002069 eeprom_write_reg(priv, 0);
2070 eeprom_write_reg(priv, EEPROM_BIT_CS);
2071 eeprom_write_reg(priv, EEPROM_BIT_CS | EEPROM_BIT_SK);
2072 eeprom_write_reg(priv, EEPROM_BIT_CS);
James Ketrenos43f66a62005-03-25 12:31:53 -06002073}
2074
2075/* perform a chip select operation */
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002076static inline void eeprom_disable_cs(struct ipw_priv *priv)
James Ketrenos43f66a62005-03-25 12:31:53 -06002077{
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002078 eeprom_write_reg(priv, EEPROM_BIT_CS);
2079 eeprom_write_reg(priv, 0);
2080 eeprom_write_reg(priv, EEPROM_BIT_SK);
James Ketrenos43f66a62005-03-25 12:31:53 -06002081}
2082
2083/* push a single bit down to the eeprom */
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002084static inline void eeprom_write_bit(struct ipw_priv *p, u8 bit)
James Ketrenos43f66a62005-03-25 12:31:53 -06002085{
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002086 int d = (bit ? EEPROM_BIT_DI : 0);
2087 eeprom_write_reg(p, EEPROM_BIT_CS | d);
2088 eeprom_write_reg(p, EEPROM_BIT_CS | d | EEPROM_BIT_SK);
James Ketrenos43f66a62005-03-25 12:31:53 -06002089}
2090
2091/* push an opcode followed by an address down to the eeprom */
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002092static void eeprom_op(struct ipw_priv *priv, u8 op, u8 addr)
James Ketrenos43f66a62005-03-25 12:31:53 -06002093{
2094 int i;
2095
2096 eeprom_cs(priv);
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002097 eeprom_write_bit(priv, 1);
2098 eeprom_write_bit(priv, op & 2);
2099 eeprom_write_bit(priv, op & 1);
2100 for (i = 7; i >= 0; i--) {
2101 eeprom_write_bit(priv, addr & (1 << i));
James Ketrenos43f66a62005-03-25 12:31:53 -06002102 }
2103}
2104
2105/* pull 16 bits off the eeprom, one bit at a time */
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002106static u16 eeprom_read_u16(struct ipw_priv *priv, u8 addr)
James Ketrenos43f66a62005-03-25 12:31:53 -06002107{
2108 int i;
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002109 u16 r = 0;
Jeff Garzikbf794512005-07-31 13:07:26 -04002110
James Ketrenos43f66a62005-03-25 12:31:53 -06002111 /* Send READ Opcode */
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002112 eeprom_op(priv, EEPROM_CMD_READ, addr);
James Ketrenos43f66a62005-03-25 12:31:53 -06002113
2114 /* Send dummy bit */
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002115 eeprom_write_reg(priv, EEPROM_BIT_CS);
James Ketrenos43f66a62005-03-25 12:31:53 -06002116
2117 /* Read the byte off the eeprom one bit at a time */
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002118 for (i = 0; i < 16; i++) {
James Ketrenos43f66a62005-03-25 12:31:53 -06002119 u32 data = 0;
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002120 eeprom_write_reg(priv, EEPROM_BIT_CS | EEPROM_BIT_SK);
2121 eeprom_write_reg(priv, EEPROM_BIT_CS);
2122 data = ipw_read_reg32(priv, FW_MEM_REG_EEPROM_ACCESS);
2123 r = (r << 1) | ((data & EEPROM_BIT_DO) ? 1 : 0);
James Ketrenos43f66a62005-03-25 12:31:53 -06002124 }
Jeff Garzikbf794512005-07-31 13:07:26 -04002125
James Ketrenos43f66a62005-03-25 12:31:53 -06002126 /* Send another dummy bit */
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002127 eeprom_write_reg(priv, 0);
James Ketrenos43f66a62005-03-25 12:31:53 -06002128 eeprom_disable_cs(priv);
Jeff Garzikbf794512005-07-31 13:07:26 -04002129
James Ketrenos43f66a62005-03-25 12:31:53 -06002130 return r;
2131}
2132
2133/* helper function for pulling the mac address out of the private */
2134/* data's copy of the eeprom data */
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002135static void eeprom_parse_mac(struct ipw_priv *priv, u8 * mac)
James Ketrenos43f66a62005-03-25 12:31:53 -06002136{
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002137 u8 *ee = (u8 *) priv->eeprom;
James Ketrenos43f66a62005-03-25 12:31:53 -06002138 memcpy(mac, &ee[EEPROM_MAC_ADDRESS], 6);
2139}
2140
2141/*
2142 * Either the device driver (i.e. the host) or the firmware can
2143 * load eeprom data into the designated region in SRAM. If neither
2144 * happens then the FW will shutdown with a fatal error.
2145 *
2146 * In order to signal the FW to load the EEPROM, the EEPROM_LOAD_DISABLE
2147 * bit needs region of shared SRAM needs to be non-zero.
2148 */
2149static void ipw_eeprom_init_sram(struct ipw_priv *priv)
2150{
2151 int i;
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002152 u16 *eeprom = (u16 *) priv->eeprom;
Jeff Garzikbf794512005-07-31 13:07:26 -04002153
James Ketrenos43f66a62005-03-25 12:31:53 -06002154 IPW_DEBUG_TRACE(">>\n");
2155
2156 /* read entire contents of eeprom into private buffer */
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002157 for (i = 0; i < 128; i++)
James Ketrenosa613bff2005-08-24 21:43:11 -05002158 eeprom[i] = le16_to_cpu(eeprom_read_u16(priv, (u8) i));
James Ketrenos43f66a62005-03-25 12:31:53 -06002159
Jeff Garzikbf794512005-07-31 13:07:26 -04002160 /*
2161 If the data looks correct, then copy it to our private
James Ketrenos43f66a62005-03-25 12:31:53 -06002162 copy. Otherwise let the firmware know to perform the operation
2163 on it's own
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002164 */
James Ketrenos43f66a62005-03-25 12:31:53 -06002165 if ((priv->eeprom + EEPROM_VERSION) != 0) {
2166 IPW_DEBUG_INFO("Writing EEPROM data into SRAM\n");
2167
2168 /* write the eeprom data to sram */
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002169 for (i = 0; i < CX2_EEPROM_IMAGE_SIZE; i++)
2170 ipw_write8(priv, IPW_EEPROM_DATA + i, priv->eeprom[i]);
James Ketrenos43f66a62005-03-25 12:31:53 -06002171
2172 /* Do not load eeprom data on fatal error or suspend */
2173 ipw_write32(priv, IPW_EEPROM_LOAD_DISABLE, 0);
2174 } else {
2175 IPW_DEBUG_INFO("Enabling FW initializationg of SRAM\n");
2176
2177 /* Load eeprom data on fatal error or suspend */
2178 ipw_write32(priv, IPW_EEPROM_LOAD_DISABLE, 1);
2179 }
2180
2181 IPW_DEBUG_TRACE("<<\n");
2182}
2183
James Ketrenos43f66a62005-03-25 12:31:53 -06002184static inline void ipw_zero_memory(struct ipw_priv *priv, u32 start, u32 count)
2185{
2186 count >>= 2;
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002187 if (!count)
2188 return;
James Ketrenos43f66a62005-03-25 12:31:53 -06002189 _ipw_write32(priv, CX2_AUTOINC_ADDR, start);
Jeff Garzikbf794512005-07-31 13:07:26 -04002190 while (count--)
James Ketrenos43f66a62005-03-25 12:31:53 -06002191 _ipw_write32(priv, CX2_AUTOINC_DATA, 0);
2192}
2193
2194static inline void ipw_fw_dma_reset_command_blocks(struct ipw_priv *priv)
2195{
2196 ipw_zero_memory(priv, CX2_SHARED_SRAM_DMA_CONTROL,
Jeff Garzikbf794512005-07-31 13:07:26 -04002197 CB_NUMBER_OF_ELEMENTS_SMALL *
James Ketrenos43f66a62005-03-25 12:31:53 -06002198 sizeof(struct command_block));
2199}
2200
2201static int ipw_fw_dma_enable(struct ipw_priv *priv)
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002202{ /* start dma engine but no transfers yet */
James Ketrenos43f66a62005-03-25 12:31:53 -06002203
2204 IPW_DEBUG_FW(">> : \n");
Jeff Garzikbf794512005-07-31 13:07:26 -04002205
James Ketrenos43f66a62005-03-25 12:31:53 -06002206 /* Start the dma */
2207 ipw_fw_dma_reset_command_blocks(priv);
Jeff Garzikbf794512005-07-31 13:07:26 -04002208
James Ketrenos43f66a62005-03-25 12:31:53 -06002209 /* Write CB base address */
2210 ipw_write_reg32(priv, CX2_DMA_I_CB_BASE, CX2_SHARED_SRAM_DMA_CONTROL);
2211
2212 IPW_DEBUG_FW("<< : \n");
2213 return 0;
2214}
2215
2216static void ipw_fw_dma_abort(struct ipw_priv *priv)
2217{
2218 u32 control = 0;
2219
2220 IPW_DEBUG_FW(">> :\n");
Jeff Garzikbf794512005-07-31 13:07:26 -04002221
2222 //set the Stop and Abort bit
James Ketrenos43f66a62005-03-25 12:31:53 -06002223 control = DMA_CONTROL_SMALL_CB_CONST_VALUE | DMA_CB_STOP_AND_ABORT;
2224 ipw_write_reg32(priv, CX2_DMA_I_DMA_CONTROL, control);
2225 priv->sram_desc.last_cb_index = 0;
Jeff Garzikbf794512005-07-31 13:07:26 -04002226
James Ketrenos43f66a62005-03-25 12:31:53 -06002227 IPW_DEBUG_FW("<< \n");
2228}
2229
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002230static int ipw_fw_dma_write_command_block(struct ipw_priv *priv, int index,
2231 struct command_block *cb)
James Ketrenos43f66a62005-03-25 12:31:53 -06002232{
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002233 u32 address =
2234 CX2_SHARED_SRAM_DMA_CONTROL +
2235 (sizeof(struct command_block) * index);
James Ketrenos43f66a62005-03-25 12:31:53 -06002236 IPW_DEBUG_FW(">> :\n");
2237
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002238 ipw_write_indirect(priv, address, (u8 *) cb,
2239 (int)sizeof(struct command_block));
James Ketrenos43f66a62005-03-25 12:31:53 -06002240
2241 IPW_DEBUG_FW("<< :\n");
2242 return 0;
2243
2244}
2245
2246static int ipw_fw_dma_kick(struct ipw_priv *priv)
2247{
2248 u32 control = 0;
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002249 u32 index = 0;
James Ketrenos43f66a62005-03-25 12:31:53 -06002250
2251 IPW_DEBUG_FW(">> :\n");
Jeff Garzikbf794512005-07-31 13:07:26 -04002252
James Ketrenos43f66a62005-03-25 12:31:53 -06002253 for (index = 0; index < priv->sram_desc.last_cb_index; index++)
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002254 ipw_fw_dma_write_command_block(priv, index,
2255 &priv->sram_desc.cb_list[index]);
James Ketrenos43f66a62005-03-25 12:31:53 -06002256
2257 /* Enable the DMA in the CSR register */
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002258 ipw_clear_bit(priv, CX2_RESET_REG,
2259 CX2_RESET_REG_MASTER_DISABLED |
2260 CX2_RESET_REG_STOP_MASTER);
Jeff Garzikbf794512005-07-31 13:07:26 -04002261
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002262 /* Set the Start bit. */
James Ketrenos43f66a62005-03-25 12:31:53 -06002263 control = DMA_CONTROL_SMALL_CB_CONST_VALUE | DMA_CB_START;
2264 ipw_write_reg32(priv, CX2_DMA_I_DMA_CONTROL, control);
2265
2266 IPW_DEBUG_FW("<< :\n");
2267 return 0;
2268}
2269
2270static void ipw_fw_dma_dump_command_block(struct ipw_priv *priv)
2271{
2272 u32 address;
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002273 u32 register_value = 0;
2274 u32 cb_fields_address = 0;
James Ketrenos43f66a62005-03-25 12:31:53 -06002275
2276 IPW_DEBUG_FW(">> :\n");
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002277 address = ipw_read_reg32(priv, CX2_DMA_I_CURRENT_CB);
2278 IPW_DEBUG_FW_INFO("Current CB is 0x%x \n", address);
James Ketrenos43f66a62005-03-25 12:31:53 -06002279
2280 /* Read the DMA Controlor register */
2281 register_value = ipw_read_reg32(priv, CX2_DMA_I_DMA_CONTROL);
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002282 IPW_DEBUG_FW_INFO("CX2_DMA_I_DMA_CONTROL is 0x%x \n", register_value);
James Ketrenos43f66a62005-03-25 12:31:53 -06002283
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002284 /* Print the CB values */
James Ketrenos43f66a62005-03-25 12:31:53 -06002285 cb_fields_address = address;
2286 register_value = ipw_read_reg32(priv, cb_fields_address);
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002287 IPW_DEBUG_FW_INFO("Current CB ControlField is 0x%x \n", register_value);
James Ketrenos43f66a62005-03-25 12:31:53 -06002288
2289 cb_fields_address += sizeof(u32);
2290 register_value = ipw_read_reg32(priv, cb_fields_address);
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002291 IPW_DEBUG_FW_INFO("Current CB Source Field is 0x%x \n", register_value);
James Ketrenos43f66a62005-03-25 12:31:53 -06002292
2293 cb_fields_address += sizeof(u32);
2294 register_value = ipw_read_reg32(priv, cb_fields_address);
2295 IPW_DEBUG_FW_INFO("Current CB Destination Field is 0x%x \n",
2296 register_value);
2297
2298 cb_fields_address += sizeof(u32);
2299 register_value = ipw_read_reg32(priv, cb_fields_address);
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002300 IPW_DEBUG_FW_INFO("Current CB Status Field is 0x%x \n", register_value);
James Ketrenos43f66a62005-03-25 12:31:53 -06002301
2302 IPW_DEBUG_FW(">> :\n");
2303}
2304
2305static int ipw_fw_dma_command_block_index(struct ipw_priv *priv)
2306{
2307 u32 current_cb_address = 0;
2308 u32 current_cb_index = 0;
2309
2310 IPW_DEBUG_FW("<< :\n");
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002311 current_cb_address = ipw_read_reg32(priv, CX2_DMA_I_CURRENT_CB);
Jeff Garzikbf794512005-07-31 13:07:26 -04002312
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002313 current_cb_index = (current_cb_address - CX2_SHARED_SRAM_DMA_CONTROL) /
2314 sizeof(struct command_block);
Jeff Garzikbf794512005-07-31 13:07:26 -04002315
James Ketrenos43f66a62005-03-25 12:31:53 -06002316 IPW_DEBUG_FW_INFO("Current CB index 0x%x address = 0x%X \n",
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002317 current_cb_index, current_cb_address);
James Ketrenos43f66a62005-03-25 12:31:53 -06002318
2319 IPW_DEBUG_FW(">> :\n");
2320 return current_cb_index;
2321
2322}
2323
2324static int ipw_fw_dma_add_command_block(struct ipw_priv *priv,
2325 u32 src_address,
2326 u32 dest_address,
2327 u32 length,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002328 int interrupt_enabled, int is_last)
James Ketrenos43f66a62005-03-25 12:31:53 -06002329{
2330
Jeff Garzikbf794512005-07-31 13:07:26 -04002331 u32 control = CB_VALID | CB_SRC_LE | CB_DEST_LE | CB_SRC_AUTOINC |
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002332 CB_SRC_IO_GATED | CB_DEST_AUTOINC | CB_SRC_SIZE_LONG |
2333 CB_DEST_SIZE_LONG;
James Ketrenos43f66a62005-03-25 12:31:53 -06002334 struct command_block *cb;
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002335 u32 last_cb_element = 0;
James Ketrenos43f66a62005-03-25 12:31:53 -06002336
2337 IPW_DEBUG_FW_INFO("src_address=0x%x dest_address=0x%x length=0x%x\n",
2338 src_address, dest_address, length);
2339
2340 if (priv->sram_desc.last_cb_index >= CB_NUMBER_OF_ELEMENTS_SMALL)
2341 return -1;
2342
2343 last_cb_element = priv->sram_desc.last_cb_index;
2344 cb = &priv->sram_desc.cb_list[last_cb_element];
2345 priv->sram_desc.last_cb_index++;
2346
2347 /* Calculate the new CB control word */
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002348 if (interrupt_enabled)
James Ketrenos43f66a62005-03-25 12:31:53 -06002349 control |= CB_INT_ENABLED;
2350
2351 if (is_last)
2352 control |= CB_LAST_VALID;
Jeff Garzikbf794512005-07-31 13:07:26 -04002353
James Ketrenos43f66a62005-03-25 12:31:53 -06002354 control |= length;
2355
2356 /* Calculate the CB Element's checksum value */
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002357 cb->status = control ^ src_address ^ dest_address;
James Ketrenos43f66a62005-03-25 12:31:53 -06002358
2359 /* Copy the Source and Destination addresses */
2360 cb->dest_addr = dest_address;
2361 cb->source_addr = src_address;
2362
2363 /* Copy the Control Word last */
2364 cb->control = control;
2365
2366 return 0;
2367}
2368
2369static int ipw_fw_dma_add_buffer(struct ipw_priv *priv,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002370 u32 src_phys, u32 dest_address, u32 length)
James Ketrenos43f66a62005-03-25 12:31:53 -06002371{
2372 u32 bytes_left = length;
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002373 u32 src_offset = 0;
2374 u32 dest_offset = 0;
James Ketrenos43f66a62005-03-25 12:31:53 -06002375 int status = 0;
2376 IPW_DEBUG_FW(">> \n");
2377 IPW_DEBUG_FW_INFO("src_phys=0x%x dest_address=0x%x length=0x%x\n",
2378 src_phys, dest_address, length);
2379 while (bytes_left > CB_MAX_LENGTH) {
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002380 status = ipw_fw_dma_add_command_block(priv,
2381 src_phys + src_offset,
2382 dest_address +
2383 dest_offset,
2384 CB_MAX_LENGTH, 0, 0);
James Ketrenos43f66a62005-03-25 12:31:53 -06002385 if (status) {
2386 IPW_DEBUG_FW_INFO(": Failed\n");
2387 return -1;
Jeff Garzikbf794512005-07-31 13:07:26 -04002388 } else
James Ketrenos43f66a62005-03-25 12:31:53 -06002389 IPW_DEBUG_FW_INFO(": Added new cb\n");
2390
2391 src_offset += CB_MAX_LENGTH;
2392 dest_offset += CB_MAX_LENGTH;
2393 bytes_left -= CB_MAX_LENGTH;
2394 }
2395
2396 /* add the buffer tail */
2397 if (bytes_left > 0) {
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002398 status =
2399 ipw_fw_dma_add_command_block(priv, src_phys + src_offset,
2400 dest_address + dest_offset,
2401 bytes_left, 0, 0);
James Ketrenos43f66a62005-03-25 12:31:53 -06002402 if (status) {
2403 IPW_DEBUG_FW_INFO(": Failed on the buffer tail\n");
2404 return -1;
Jeff Garzikbf794512005-07-31 13:07:26 -04002405 } else
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002406 IPW_DEBUG_FW_INFO
2407 (": Adding new cb - the buffer tail\n");
James Ketrenos43f66a62005-03-25 12:31:53 -06002408 }
Jeff Garzikbf794512005-07-31 13:07:26 -04002409
James Ketrenos43f66a62005-03-25 12:31:53 -06002410 IPW_DEBUG_FW("<< \n");
2411 return 0;
2412}
2413
2414static int ipw_fw_dma_wait(struct ipw_priv *priv)
2415{
2416 u32 current_index = 0;
2417 u32 watchdog = 0;
2418
2419 IPW_DEBUG_FW(">> : \n");
2420
2421 current_index = ipw_fw_dma_command_block_index(priv);
Jeff Garzikbf794512005-07-31 13:07:26 -04002422 IPW_DEBUG_FW_INFO("sram_desc.last_cb_index:0x%8X\n",
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002423 (int)priv->sram_desc.last_cb_index);
James Ketrenos43f66a62005-03-25 12:31:53 -06002424
2425 while (current_index < priv->sram_desc.last_cb_index) {
2426 udelay(50);
2427 current_index = ipw_fw_dma_command_block_index(priv);
2428
2429 watchdog++;
2430
2431 if (watchdog > 400) {
2432 IPW_DEBUG_FW_INFO("Timeout\n");
2433 ipw_fw_dma_dump_command_block(priv);
2434 ipw_fw_dma_abort(priv);
2435 return -1;
2436 }
2437 }
2438
2439 ipw_fw_dma_abort(priv);
2440
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002441 /*Disable the DMA in the CSR register */
2442 ipw_set_bit(priv, CX2_RESET_REG,
James Ketrenos43f66a62005-03-25 12:31:53 -06002443 CX2_RESET_REG_MASTER_DISABLED | CX2_RESET_REG_STOP_MASTER);
2444
2445 IPW_DEBUG_FW("<< dmaWaitSync \n");
2446 return 0;
2447}
2448
Jeff Garzikbf794512005-07-31 13:07:26 -04002449static void ipw_remove_current_network(struct ipw_priv *priv)
James Ketrenos43f66a62005-03-25 12:31:53 -06002450{
2451 struct list_head *element, *safe;
Jeff Garzikbf794512005-07-31 13:07:26 -04002452 struct ieee80211_network *network = NULL;
James Ketrenosa613bff2005-08-24 21:43:11 -05002453 unsigned long flags;
2454
2455 spin_lock_irqsave(&priv->ieee->lock, flags);
James Ketrenos43f66a62005-03-25 12:31:53 -06002456 list_for_each_safe(element, safe, &priv->ieee->network_list) {
2457 network = list_entry(element, struct ieee80211_network, list);
2458 if (!memcmp(network->bssid, priv->bssid, ETH_ALEN)) {
2459 list_del(element);
Jeff Garzikbf794512005-07-31 13:07:26 -04002460 list_add_tail(&network->list,
James Ketrenos43f66a62005-03-25 12:31:53 -06002461 &priv->ieee->network_free_list);
2462 }
2463 }
James Ketrenosa613bff2005-08-24 21:43:11 -05002464 spin_unlock_irqrestore(&priv->ieee->lock, flags);
James Ketrenos43f66a62005-03-25 12:31:53 -06002465}
2466
2467/**
Jeff Garzikbf794512005-07-31 13:07:26 -04002468 * Check that card is still alive.
James Ketrenos43f66a62005-03-25 12:31:53 -06002469 * Reads debug register from domain0.
2470 * If card is present, pre-defined value should
2471 * be found there.
Jeff Garzikbf794512005-07-31 13:07:26 -04002472 *
James Ketrenos43f66a62005-03-25 12:31:53 -06002473 * @param priv
2474 * @return 1 if card is present, 0 otherwise
2475 */
2476static inline int ipw_alive(struct ipw_priv *priv)
2477{
2478 return ipw_read32(priv, 0x90) == 0xd55555d5;
2479}
2480
2481static inline int ipw_poll_bit(struct ipw_priv *priv, u32 addr, u32 mask,
2482 int timeout)
2483{
2484 int i = 0;
2485
2486 do {
Jeff Garzikbf794512005-07-31 13:07:26 -04002487 if ((ipw_read32(priv, addr) & mask) == mask)
James Ketrenos43f66a62005-03-25 12:31:53 -06002488 return i;
2489 mdelay(10);
2490 i += 10;
2491 } while (i < timeout);
Jeff Garzikbf794512005-07-31 13:07:26 -04002492
James Ketrenos43f66a62005-03-25 12:31:53 -06002493 return -ETIME;
2494}
2495
Jeff Garzikbf794512005-07-31 13:07:26 -04002496/* These functions load the firmware and micro code for the operation of
James Ketrenos43f66a62005-03-25 12:31:53 -06002497 * the ipw hardware. It assumes the buffer has all the bits for the
2498 * image and the caller is handling the memory allocation and clean up.
2499 */
2500
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002501static int ipw_stop_master(struct ipw_priv *priv)
James Ketrenos43f66a62005-03-25 12:31:53 -06002502{
2503 int rc;
Jeff Garzikbf794512005-07-31 13:07:26 -04002504
James Ketrenos43f66a62005-03-25 12:31:53 -06002505 IPW_DEBUG_TRACE(">> \n");
2506 /* stop master. typical delay - 0 */
2507 ipw_set_bit(priv, CX2_RESET_REG, CX2_RESET_REG_STOP_MASTER);
2508
2509 rc = ipw_poll_bit(priv, CX2_RESET_REG,
2510 CX2_RESET_REG_MASTER_DISABLED, 100);
2511 if (rc < 0) {
2512 IPW_ERROR("stop master failed in 10ms\n");
2513 return -1;
2514 }
2515
2516 IPW_DEBUG_INFO("stop master %dms\n", rc);
2517
2518 return rc;
2519}
2520
2521static void ipw_arc_release(struct ipw_priv *priv)
2522{
2523 IPW_DEBUG_TRACE(">> \n");
2524 mdelay(5);
2525
2526 ipw_clear_bit(priv, CX2_RESET_REG, CBD_RESET_REG_PRINCETON_RESET);
2527
2528 /* no one knows timing, for safety add some delay */
2529 mdelay(5);
2530}
2531
2532struct fw_header {
2533 u32 version;
2534 u32 mode;
2535};
2536
2537struct fw_chunk {
2538 u32 address;
2539 u32 length;
2540};
2541
2542#define IPW_FW_MAJOR_VERSION 2
2543#define IPW_FW_MINOR_VERSION 2
2544
2545#define IPW_FW_MINOR(x) ((x & 0xff) >> 8)
2546#define IPW_FW_MAJOR(x) (x & 0xff)
2547
2548#define IPW_FW_VERSION ((IPW_FW_MINOR_VERSION << 8) | \
2549 IPW_FW_MAJOR_VERSION)
2550
2551#define IPW_FW_PREFIX "ipw-" __stringify(IPW_FW_MAJOR_VERSION) \
2552"." __stringify(IPW_FW_MINOR_VERSION) "-"
2553
2554#if IPW_FW_MAJOR_VERSION >= 2 && IPW_FW_MINOR_VERSION > 0
2555#define IPW_FW_NAME(x) IPW_FW_PREFIX "" x ".fw"
2556#else
2557#define IPW_FW_NAME(x) "ipw2200_" x ".fw"
2558#endif
2559
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002560static int ipw_load_ucode(struct ipw_priv *priv, u8 * data, size_t len)
James Ketrenos43f66a62005-03-25 12:31:53 -06002561{
2562 int rc = 0, i, addr;
2563 u8 cr = 0;
2564 u16 *image;
2565
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002566 image = (u16 *) data;
Jeff Garzikbf794512005-07-31 13:07:26 -04002567
James Ketrenos43f66a62005-03-25 12:31:53 -06002568 IPW_DEBUG_TRACE(">> \n");
2569
2570 rc = ipw_stop_master(priv);
2571
2572 if (rc < 0)
2573 return rc;
Jeff Garzikbf794512005-07-31 13:07:26 -04002574
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002575// spin_lock_irqsave(&priv->lock, flags);
Jeff Garzikbf794512005-07-31 13:07:26 -04002576
James Ketrenos43f66a62005-03-25 12:31:53 -06002577 for (addr = CX2_SHARED_LOWER_BOUND;
2578 addr < CX2_REGISTER_DOMAIN1_END; addr += 4) {
2579 ipw_write32(priv, addr, 0);
2580 }
2581
2582 /* no ucode (yet) */
2583 memset(&priv->dino_alive, 0, sizeof(priv->dino_alive));
2584 /* destroy DMA queues */
2585 /* reset sequence */
2586
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002587 ipw_write_reg32(priv, CX2_MEM_HALT_AND_RESET, CX2_BIT_HALT_RESET_ON);
James Ketrenos43f66a62005-03-25 12:31:53 -06002588 ipw_arc_release(priv);
2589 ipw_write_reg32(priv, CX2_MEM_HALT_AND_RESET, CX2_BIT_HALT_RESET_OFF);
2590 mdelay(1);
2591
2592 /* reset PHY */
2593 ipw_write_reg32(priv, CX2_INTERNAL_CMD_EVENT, CX2_BASEBAND_POWER_DOWN);
2594 mdelay(1);
Jeff Garzikbf794512005-07-31 13:07:26 -04002595
James Ketrenos43f66a62005-03-25 12:31:53 -06002596 ipw_write_reg32(priv, CX2_INTERNAL_CMD_EVENT, 0);
2597 mdelay(1);
Jeff Garzikbf794512005-07-31 13:07:26 -04002598
James Ketrenos43f66a62005-03-25 12:31:53 -06002599 /* enable ucode store */
2600 ipw_write_reg8(priv, DINO_CONTROL_REG, 0x0);
2601 ipw_write_reg8(priv, DINO_CONTROL_REG, DINO_ENABLE_CS);
2602 mdelay(1);
2603
2604 /* write ucode */
2605 /**
2606 * @bug
2607 * Do NOT set indirect address register once and then
2608 * store data to indirect data register in the loop.
2609 * It seems very reasonable, but in this case DINO do not
2610 * accept ucode. It is essential to set address each time.
2611 */
2612 /* load new ipw uCode */
2613 for (i = 0; i < len / 2; i++)
James Ketrenosa613bff2005-08-24 21:43:11 -05002614 ipw_write_reg16(priv, CX2_BASEBAND_CONTROL_STORE,
2615 cpu_to_le16(image[i]));
James Ketrenos43f66a62005-03-25 12:31:53 -06002616
James Ketrenos43f66a62005-03-25 12:31:53 -06002617 /* enable DINO */
2618 ipw_write_reg8(priv, CX2_BASEBAND_CONTROL_STATUS, 0);
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002619 ipw_write_reg8(priv, CX2_BASEBAND_CONTROL_STATUS, DINO_ENABLE_SYSTEM);
James Ketrenos43f66a62005-03-25 12:31:53 -06002620
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002621 /* this is where the igx / win driver deveates from the VAP driver. */
James Ketrenos43f66a62005-03-25 12:31:53 -06002622
2623 /* wait for alive response */
2624 for (i = 0; i < 100; i++) {
2625 /* poll for incoming data */
2626 cr = ipw_read_reg8(priv, CX2_BASEBAND_CONTROL_STATUS);
2627 if (cr & DINO_RXFIFO_DATA)
2628 break;
2629 mdelay(1);
2630 }
2631
2632 if (cr & DINO_RXFIFO_DATA) {
2633 /* alive_command_responce size is NOT multiple of 4 */
2634 u32 response_buffer[(sizeof(priv->dino_alive) + 3) / 4];
Jeff Garzikbf794512005-07-31 13:07:26 -04002635
2636 for (i = 0; i < ARRAY_SIZE(response_buffer); i++)
James Ketrenos43f66a62005-03-25 12:31:53 -06002637 response_buffer[i] =
James Ketrenosa613bff2005-08-24 21:43:11 -05002638 le32_to_cpu(ipw_read_reg32(priv,
2639 CX2_BASEBAND_RX_FIFO_READ));
James Ketrenos43f66a62005-03-25 12:31:53 -06002640 memcpy(&priv->dino_alive, response_buffer,
2641 sizeof(priv->dino_alive));
2642 if (priv->dino_alive.alive_command == 1
2643 && priv->dino_alive.ucode_valid == 1) {
2644 rc = 0;
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002645 IPW_DEBUG_INFO
2646 ("Microcode OK, rev. %d (0x%x) dev. %d (0x%x) "
2647 "of %02d/%02d/%02d %02d:%02d\n",
2648 priv->dino_alive.software_revision,
2649 priv->dino_alive.software_revision,
2650 priv->dino_alive.device_identifier,
2651 priv->dino_alive.device_identifier,
2652 priv->dino_alive.time_stamp[0],
2653 priv->dino_alive.time_stamp[1],
2654 priv->dino_alive.time_stamp[2],
2655 priv->dino_alive.time_stamp[3],
2656 priv->dino_alive.time_stamp[4]);
James Ketrenos43f66a62005-03-25 12:31:53 -06002657 } else {
2658 IPW_DEBUG_INFO("Microcode is not alive\n");
2659 rc = -EINVAL;
2660 }
2661 } else {
2662 IPW_DEBUG_INFO("No alive response from DINO\n");
2663 rc = -ETIME;
2664 }
2665
2666 /* disable DINO, otherwise for some reason
2667 firmware have problem getting alive resp. */
2668 ipw_write_reg8(priv, CX2_BASEBAND_CONTROL_STATUS, 0);
2669
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002670// spin_unlock_irqrestore(&priv->lock, flags);
James Ketrenos43f66a62005-03-25 12:31:53 -06002671
2672 return rc;
2673}
2674
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002675static int ipw_load_firmware(struct ipw_priv *priv, u8 * data, size_t len)
James Ketrenos43f66a62005-03-25 12:31:53 -06002676{
2677 int rc = -1;
2678 int offset = 0;
2679 struct fw_chunk *chunk;
2680 dma_addr_t shared_phys;
2681 u8 *shared_virt;
2682
2683 IPW_DEBUG_TRACE("<< : \n");
2684 shared_virt = pci_alloc_consistent(priv->pci_dev, len, &shared_phys);
2685
2686 if (!shared_virt)
2687 return -ENOMEM;
2688
2689 memmove(shared_virt, data, len);
2690
2691 /* Start the Dma */
2692 rc = ipw_fw_dma_enable(priv);
2693
2694 if (priv->sram_desc.last_cb_index > 0) {
2695 /* the DMA is already ready this would be a bug. */
2696 BUG();
2697 goto out;
2698 }
2699
2700 do {
2701 chunk = (struct fw_chunk *)(data + offset);
2702 offset += sizeof(struct fw_chunk);
2703 /* build DMA packet and queue up for sending */
Jeff Garzikbf794512005-07-31 13:07:26 -04002704 /* dma to chunk->address, the chunk->length bytes from data +
James Ketrenos43f66a62005-03-25 12:31:53 -06002705 * offeset*/
2706 /* Dma loading */
2707 rc = ipw_fw_dma_add_buffer(priv, shared_phys + offset,
James Ketrenosa613bff2005-08-24 21:43:11 -05002708 le32_to_cpu(chunk->address),
2709 le32_to_cpu(chunk->length));
James Ketrenos43f66a62005-03-25 12:31:53 -06002710 if (rc) {
2711 IPW_DEBUG_INFO("dmaAddBuffer Failed\n");
2712 goto out;
2713 }
Jeff Garzikbf794512005-07-31 13:07:26 -04002714
James Ketrenosa613bff2005-08-24 21:43:11 -05002715 offset += le32_to_cpu(chunk->length);
James Ketrenos43f66a62005-03-25 12:31:53 -06002716 } while (offset < len);
2717
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002718 /* Run the DMA and wait for the answer */
James Ketrenos43f66a62005-03-25 12:31:53 -06002719 rc = ipw_fw_dma_kick(priv);
2720 if (rc) {
2721 IPW_ERROR("dmaKick Failed\n");
2722 goto out;
2723 }
2724
2725 rc = ipw_fw_dma_wait(priv);
2726 if (rc) {
2727 IPW_ERROR("dmaWaitSync Failed\n");
2728 goto out;
2729 }
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002730 out:
2731 pci_free_consistent(priv->pci_dev, len, shared_virt, shared_phys);
James Ketrenos43f66a62005-03-25 12:31:53 -06002732 return rc;
2733}
2734
2735/* stop nic */
2736static int ipw_stop_nic(struct ipw_priv *priv)
2737{
2738 int rc = 0;
2739
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002740 /* stop */
James Ketrenos43f66a62005-03-25 12:31:53 -06002741 ipw_write32(priv, CX2_RESET_REG, CX2_RESET_REG_STOP_MASTER);
Jeff Garzikbf794512005-07-31 13:07:26 -04002742
2743 rc = ipw_poll_bit(priv, CX2_RESET_REG,
2744 CX2_RESET_REG_MASTER_DISABLED, 500);
James Ketrenos43f66a62005-03-25 12:31:53 -06002745 if (rc < 0) {
2746 IPW_ERROR("wait for reg master disabled failed\n");
2747 return rc;
Jeff Garzikbf794512005-07-31 13:07:26 -04002748 }
James Ketrenos43f66a62005-03-25 12:31:53 -06002749
2750 ipw_set_bit(priv, CX2_RESET_REG, CBD_RESET_REG_PRINCETON_RESET);
Jeff Garzikbf794512005-07-31 13:07:26 -04002751
James Ketrenos43f66a62005-03-25 12:31:53 -06002752 return rc;
2753}
2754
2755static void ipw_start_nic(struct ipw_priv *priv)
2756{
2757 IPW_DEBUG_TRACE(">>\n");
2758
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002759 /* prvHwStartNic release ARC */
James Ketrenos43f66a62005-03-25 12:31:53 -06002760 ipw_clear_bit(priv, CX2_RESET_REG,
Jeff Garzikbf794512005-07-31 13:07:26 -04002761 CX2_RESET_REG_MASTER_DISABLED |
2762 CX2_RESET_REG_STOP_MASTER |
James Ketrenos43f66a62005-03-25 12:31:53 -06002763 CBD_RESET_REG_PRINCETON_RESET);
Jeff Garzikbf794512005-07-31 13:07:26 -04002764
James Ketrenos43f66a62005-03-25 12:31:53 -06002765 /* enable power management */
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002766 ipw_set_bit(priv, CX2_GP_CNTRL_RW,
2767 CX2_GP_CNTRL_BIT_HOST_ALLOWS_STANDBY);
James Ketrenos43f66a62005-03-25 12:31:53 -06002768
2769 IPW_DEBUG_TRACE("<<\n");
2770}
Jeff Garzikbf794512005-07-31 13:07:26 -04002771
James Ketrenos43f66a62005-03-25 12:31:53 -06002772static int ipw_init_nic(struct ipw_priv *priv)
2773{
2774 int rc;
2775
2776 IPW_DEBUG_TRACE(">>\n");
Jeff Garzikbf794512005-07-31 13:07:26 -04002777 /* reset */
James Ketrenos43f66a62005-03-25 12:31:53 -06002778 /*prvHwInitNic */
2779 /* set "initialization complete" bit to move adapter to D0 state */
2780 ipw_set_bit(priv, CX2_GP_CNTRL_RW, CX2_GP_CNTRL_BIT_INIT_DONE);
2781
2782 /* low-level PLL activation */
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002783 ipw_write32(priv, CX2_READ_INT_REGISTER,
2784 CX2_BIT_INT_HOST_SRAM_READ_INT_REGISTER);
James Ketrenos43f66a62005-03-25 12:31:53 -06002785
2786 /* wait for clock stabilization */
Jeff Garzikbf794512005-07-31 13:07:26 -04002787 rc = ipw_poll_bit(priv, CX2_GP_CNTRL_RW,
2788 CX2_GP_CNTRL_BIT_CLOCK_READY, 250);
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002789 if (rc < 0)
James Ketrenos43f66a62005-03-25 12:31:53 -06002790 IPW_DEBUG_INFO("FAILED wait for clock stablization\n");
2791
2792 /* assert SW reset */
2793 ipw_set_bit(priv, CX2_RESET_REG, CX2_RESET_REG_SW_RESET);
2794
2795 udelay(10);
2796
2797 /* set "initialization complete" bit to move adapter to D0 state */
2798 ipw_set_bit(priv, CX2_GP_CNTRL_RW, CX2_GP_CNTRL_BIT_INIT_DONE);
2799
2800 IPW_DEBUG_TRACE(">>\n");
2801 return 0;
2802}
2803
Jeff Garzikbf794512005-07-31 13:07:26 -04002804/* Call this function from process context, it will sleep in request_firmware.
James Ketrenos43f66a62005-03-25 12:31:53 -06002805 * Probe is an ok place to call this from.
2806 */
2807static int ipw_reset_nic(struct ipw_priv *priv)
2808{
2809 int rc = 0;
James Ketrenosa613bff2005-08-24 21:43:11 -05002810 unsigned long flags;
James Ketrenos43f66a62005-03-25 12:31:53 -06002811
2812 IPW_DEBUG_TRACE(">>\n");
Jeff Garzikbf794512005-07-31 13:07:26 -04002813
James Ketrenos43f66a62005-03-25 12:31:53 -06002814 rc = ipw_init_nic(priv);
Jeff Garzikbf794512005-07-31 13:07:26 -04002815
James Ketrenosa613bff2005-08-24 21:43:11 -05002816 spin_lock_irqsave(&priv->lock, flags);
James Ketrenos43f66a62005-03-25 12:31:53 -06002817 /* Clear the 'host command active' bit... */
2818 priv->status &= ~STATUS_HCMD_ACTIVE;
2819 wake_up_interruptible(&priv->wait_command_queue);
James Ketrenosa613bff2005-08-24 21:43:11 -05002820 spin_unlock_irqrestore(&priv->lock, flags);
James Ketrenos43f66a62005-03-25 12:31:53 -06002821
2822 IPW_DEBUG_TRACE("<<\n");
2823 return rc;
Jeff Garzikbf794512005-07-31 13:07:26 -04002824}
James Ketrenos43f66a62005-03-25 12:31:53 -06002825
Jeff Garzikbf794512005-07-31 13:07:26 -04002826static int ipw_get_fw(struct ipw_priv *priv,
James Ketrenos43f66a62005-03-25 12:31:53 -06002827 const struct firmware **fw, const char *name)
2828{
2829 struct fw_header *header;
2830 int rc;
2831
2832 /* ask firmware_class module to get the boot firmware off disk */
2833 rc = request_firmware(fw, name, &priv->pci_dev->dev);
2834 if (rc < 0) {
2835 IPW_ERROR("%s load failed: Reason %d\n", name, rc);
2836 return rc;
Jeff Garzikbf794512005-07-31 13:07:26 -04002837 }
James Ketrenos43f66a62005-03-25 12:31:53 -06002838
2839 header = (struct fw_header *)(*fw)->data;
James Ketrenosa613bff2005-08-24 21:43:11 -05002840 if (IPW_FW_MAJOR(le32_to_cpu(header->version)) != IPW_FW_MAJOR_VERSION) {
James Ketrenos43f66a62005-03-25 12:31:53 -06002841 IPW_ERROR("'%s' firmware version not compatible (%d != %d)\n",
2842 name,
James Ketrenosa613bff2005-08-24 21:43:11 -05002843 IPW_FW_MAJOR(le32_to_cpu(header->version)),
2844 IPW_FW_MAJOR_VERSION);
James Ketrenos43f66a62005-03-25 12:31:53 -06002845 return -EINVAL;
2846 }
2847
Jiri Bencaaa4d302005-06-07 14:58:41 +02002848 IPW_DEBUG_INFO("Loading firmware '%s' file v%d.%d (%zd bytes)\n",
James Ketrenos43f66a62005-03-25 12:31:53 -06002849 name,
James Ketrenosa613bff2005-08-24 21:43:11 -05002850 IPW_FW_MAJOR(le32_to_cpu(header->version)),
2851 IPW_FW_MINOR(le32_to_cpu(header->version)),
James Ketrenos43f66a62005-03-25 12:31:53 -06002852 (*fw)->size - sizeof(struct fw_header));
2853 return 0;
2854}
2855
2856#define CX2_RX_BUF_SIZE (3000)
2857
2858static inline void ipw_rx_queue_reset(struct ipw_priv *priv,
2859 struct ipw_rx_queue *rxq)
2860{
2861 unsigned long flags;
2862 int i;
2863
2864 spin_lock_irqsave(&rxq->lock, flags);
2865
2866 INIT_LIST_HEAD(&rxq->rx_free);
2867 INIT_LIST_HEAD(&rxq->rx_used);
2868
2869 /* Fill the rx_used queue with _all_ of the Rx buffers */
2870 for (i = 0; i < RX_FREE_BUFFERS + RX_QUEUE_SIZE; i++) {
2871 /* In the reset function, these buffers may have been allocated
2872 * to an SKB, so we need to unmap and free potential storage */
2873 if (rxq->pool[i].skb != NULL) {
2874 pci_unmap_single(priv->pci_dev, rxq->pool[i].dma_addr,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002875 CX2_RX_BUF_SIZE, PCI_DMA_FROMDEVICE);
James Ketrenos43f66a62005-03-25 12:31:53 -06002876 dev_kfree_skb(rxq->pool[i].skb);
James Ketrenosa613bff2005-08-24 21:43:11 -05002877 rxq->pool[i].skb = NULL;
James Ketrenos43f66a62005-03-25 12:31:53 -06002878 }
2879 list_add_tail(&rxq->pool[i].list, &rxq->rx_used);
2880 }
Jeff Garzikbf794512005-07-31 13:07:26 -04002881
James Ketrenos43f66a62005-03-25 12:31:53 -06002882 /* Set us so that we have processed and used all buffers, but have
2883 * not restocked the Rx queue with fresh buffers */
2884 rxq->read = rxq->write = 0;
2885 rxq->processed = RX_QUEUE_SIZE - 1;
2886 rxq->free_count = 0;
2887 spin_unlock_irqrestore(&rxq->lock, flags);
2888}
2889
2890#ifdef CONFIG_PM
2891static int fw_loaded = 0;
2892static const struct firmware *bootfw = NULL;
2893static const struct firmware *firmware = NULL;
2894static const struct firmware *ucode = NULL;
2895#endif
2896
2897static int ipw_load(struct ipw_priv *priv)
2898{
2899#ifndef CONFIG_PM
2900 const struct firmware *bootfw = NULL;
2901 const struct firmware *firmware = NULL;
2902 const struct firmware *ucode = NULL;
2903#endif
2904 int rc = 0, retries = 3;
2905
2906#ifdef CONFIG_PM
2907 if (!fw_loaded) {
2908#endif
2909 rc = ipw_get_fw(priv, &bootfw, IPW_FW_NAME("boot"));
Jeff Garzikbf794512005-07-31 13:07:26 -04002910 if (rc)
James Ketrenos43f66a62005-03-25 12:31:53 -06002911 goto error;
Jeff Garzikbf794512005-07-31 13:07:26 -04002912
James Ketrenos43f66a62005-03-25 12:31:53 -06002913 switch (priv->ieee->iw_mode) {
2914 case IW_MODE_ADHOC:
Jeff Garzikbf794512005-07-31 13:07:26 -04002915 rc = ipw_get_fw(priv, &ucode,
James Ketrenos43f66a62005-03-25 12:31:53 -06002916 IPW_FW_NAME("ibss_ucode"));
Jeff Garzikbf794512005-07-31 13:07:26 -04002917 if (rc)
James Ketrenos43f66a62005-03-25 12:31:53 -06002918 goto error;
Jeff Garzikbf794512005-07-31 13:07:26 -04002919
James Ketrenos43f66a62005-03-25 12:31:53 -06002920 rc = ipw_get_fw(priv, &firmware, IPW_FW_NAME("ibss"));
2921 break;
Jeff Garzikbf794512005-07-31 13:07:26 -04002922
James Ketrenosea2b26e2005-08-24 21:25:16 -05002923#ifdef CONFIG_IPW_MONITOR
James Ketrenos43f66a62005-03-25 12:31:53 -06002924 case IW_MODE_MONITOR:
Jeff Garzikbf794512005-07-31 13:07:26 -04002925 rc = ipw_get_fw(priv, &ucode,
James Ketrenosea2b26e2005-08-24 21:25:16 -05002926 IPW_FW_NAME("sniffer_ucode"));
Jeff Garzikbf794512005-07-31 13:07:26 -04002927 if (rc)
James Ketrenos43f66a62005-03-25 12:31:53 -06002928 goto error;
Jeff Garzikbf794512005-07-31 13:07:26 -04002929
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002930 rc = ipw_get_fw(priv, &firmware,
2931 IPW_FW_NAME("sniffer"));
James Ketrenos43f66a62005-03-25 12:31:53 -06002932 break;
2933#endif
2934 case IW_MODE_INFRA:
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002935 rc = ipw_get_fw(priv, &ucode, IPW_FW_NAME("bss_ucode"));
Jeff Garzikbf794512005-07-31 13:07:26 -04002936 if (rc)
James Ketrenos43f66a62005-03-25 12:31:53 -06002937 goto error;
Jeff Garzikbf794512005-07-31 13:07:26 -04002938
James Ketrenos43f66a62005-03-25 12:31:53 -06002939 rc = ipw_get_fw(priv, &firmware, IPW_FW_NAME("bss"));
2940 break;
Jeff Garzikbf794512005-07-31 13:07:26 -04002941
James Ketrenos43f66a62005-03-25 12:31:53 -06002942 default:
2943 rc = -EINVAL;
2944 }
2945
Jeff Garzikbf794512005-07-31 13:07:26 -04002946 if (rc)
James Ketrenos43f66a62005-03-25 12:31:53 -06002947 goto error;
2948
2949#ifdef CONFIG_PM
2950 fw_loaded = 1;
2951 }
2952#endif
2953
2954 if (!priv->rxq)
2955 priv->rxq = ipw_rx_queue_alloc(priv);
2956 else
2957 ipw_rx_queue_reset(priv, priv->rxq);
2958 if (!priv->rxq) {
2959 IPW_ERROR("Unable to initialize Rx queue\n");
2960 goto error;
2961 }
2962
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002963 retry:
James Ketrenos43f66a62005-03-25 12:31:53 -06002964 /* Ensure interrupts are disabled */
2965 ipw_write32(priv, CX2_INTA_MASK_R, ~CX2_INTA_MASK_ALL);
2966 priv->status &= ~STATUS_INT_ENABLED;
2967
2968 /* ack pending interrupts */
2969 ipw_write32(priv, CX2_INTA_RW, CX2_INTA_MASK_ALL);
Jeff Garzikbf794512005-07-31 13:07:26 -04002970
James Ketrenos43f66a62005-03-25 12:31:53 -06002971 ipw_stop_nic(priv);
2972
2973 rc = ipw_reset_nic(priv);
2974 if (rc) {
2975 IPW_ERROR("Unable to reset NIC\n");
2976 goto error;
2977 }
2978
Jeff Garzikbf794512005-07-31 13:07:26 -04002979 ipw_zero_memory(priv, CX2_NIC_SRAM_LOWER_BOUND,
James Ketrenos43f66a62005-03-25 12:31:53 -06002980 CX2_NIC_SRAM_UPPER_BOUND - CX2_NIC_SRAM_LOWER_BOUND);
2981
2982 /* DMA the initial boot firmware into the device */
Jeff Garzikbf794512005-07-31 13:07:26 -04002983 rc = ipw_load_firmware(priv, bootfw->data + sizeof(struct fw_header),
James Ketrenos43f66a62005-03-25 12:31:53 -06002984 bootfw->size - sizeof(struct fw_header));
2985 if (rc < 0) {
2986 IPW_ERROR("Unable to load boot firmware\n");
2987 goto error;
2988 }
2989
2990 /* kick start the device */
2991 ipw_start_nic(priv);
2992
2993 /* wait for the device to finish it's initial startup sequence */
Jeff Garzikbf794512005-07-31 13:07:26 -04002994 rc = ipw_poll_bit(priv, CX2_INTA_RW,
2995 CX2_INTA_BIT_FW_INITIALIZATION_DONE, 500);
James Ketrenos43f66a62005-03-25 12:31:53 -06002996 if (rc < 0) {
2997 IPW_ERROR("device failed to boot initial fw image\n");
2998 goto error;
2999 }
3000 IPW_DEBUG_INFO("initial device response after %dms\n", rc);
3001
Jeff Garzikbf794512005-07-31 13:07:26 -04003002 /* ack fw init done interrupt */
James Ketrenos43f66a62005-03-25 12:31:53 -06003003 ipw_write32(priv, CX2_INTA_RW, CX2_INTA_BIT_FW_INITIALIZATION_DONE);
3004
3005 /* DMA the ucode into the device */
Jeff Garzikbf794512005-07-31 13:07:26 -04003006 rc = ipw_load_ucode(priv, ucode->data + sizeof(struct fw_header),
James Ketrenos43f66a62005-03-25 12:31:53 -06003007 ucode->size - sizeof(struct fw_header));
3008 if (rc < 0) {
3009 IPW_ERROR("Unable to load ucode\n");
3010 goto error;
3011 }
Jeff Garzikbf794512005-07-31 13:07:26 -04003012
James Ketrenos43f66a62005-03-25 12:31:53 -06003013 /* stop nic */
3014 ipw_stop_nic(priv);
3015
3016 /* DMA bss firmware into the device */
Jeff Garzikbf794512005-07-31 13:07:26 -04003017 rc = ipw_load_firmware(priv, firmware->data +
3018 sizeof(struct fw_header),
James Ketrenos43f66a62005-03-25 12:31:53 -06003019 firmware->size - sizeof(struct fw_header));
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003020 if (rc < 0) {
James Ketrenos43f66a62005-03-25 12:31:53 -06003021 IPW_ERROR("Unable to load firmware\n");
3022 goto error;
3023 }
3024
3025 ipw_write32(priv, IPW_EEPROM_LOAD_DISABLE, 0);
3026
3027 rc = ipw_queue_reset(priv);
3028 if (rc) {
3029 IPW_ERROR("Unable to initialize queues\n");
3030 goto error;
3031 }
3032
3033 /* Ensure interrupts are disabled */
3034 ipw_write32(priv, CX2_INTA_MASK_R, ~CX2_INTA_MASK_ALL);
James Ketrenosc848d0a2005-08-24 21:56:24 -05003035 /* ack pending interrupts */
3036 ipw_write32(priv, CX2_INTA_RW, CX2_INTA_MASK_ALL);
Jeff Garzikbf794512005-07-31 13:07:26 -04003037
James Ketrenos43f66a62005-03-25 12:31:53 -06003038 /* kick start the device */
3039 ipw_start_nic(priv);
3040
3041 if (ipw_read32(priv, CX2_INTA_RW) & CX2_INTA_BIT_PARITY_ERROR) {
3042 if (retries > 0) {
3043 IPW_WARNING("Parity error. Retrying init.\n");
3044 retries--;
3045 goto retry;
3046 }
3047
3048 IPW_ERROR("TODO: Handle parity error -- schedule restart?\n");
3049 rc = -EIO;
3050 goto error;
3051 }
3052
3053 /* wait for the device */
Jeff Garzikbf794512005-07-31 13:07:26 -04003054 rc = ipw_poll_bit(priv, CX2_INTA_RW,
3055 CX2_INTA_BIT_FW_INITIALIZATION_DONE, 500);
James Ketrenos43f66a62005-03-25 12:31:53 -06003056 if (rc < 0) {
3057 IPW_ERROR("device failed to start after 500ms\n");
3058 goto error;
3059 }
3060 IPW_DEBUG_INFO("device response after %dms\n", rc);
3061
3062 /* ack fw init done interrupt */
3063 ipw_write32(priv, CX2_INTA_RW, CX2_INTA_BIT_FW_INITIALIZATION_DONE);
3064
3065 /* read eeprom data and initialize the eeprom region of sram */
3066 priv->eeprom_delay = 1;
Jeff Garzikbf794512005-07-31 13:07:26 -04003067 ipw_eeprom_init_sram(priv);
James Ketrenos43f66a62005-03-25 12:31:53 -06003068
3069 /* enable interrupts */
3070 ipw_enable_interrupts(priv);
3071
3072 /* Ensure our queue has valid packets */
3073 ipw_rx_queue_replenish(priv);
3074
3075 ipw_write32(priv, CX2_RX_READ_INDEX, priv->rxq->read);
3076
3077 /* ack pending interrupts */
3078 ipw_write32(priv, CX2_INTA_RW, CX2_INTA_MASK_ALL);
3079
3080#ifndef CONFIG_PM
3081 release_firmware(bootfw);
3082 release_firmware(ucode);
3083 release_firmware(firmware);
3084#endif
3085 return 0;
3086
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003087 error:
James Ketrenos43f66a62005-03-25 12:31:53 -06003088 if (priv->rxq) {
3089 ipw_rx_queue_free(priv, priv->rxq);
3090 priv->rxq = NULL;
3091 }
3092 ipw_tx_queue_free(priv);
3093 if (bootfw)
3094 release_firmware(bootfw);
3095 if (ucode)
3096 release_firmware(ucode);
3097 if (firmware)
3098 release_firmware(firmware);
3099#ifdef CONFIG_PM
3100 fw_loaded = 0;
3101 bootfw = ucode = firmware = NULL;
3102#endif
3103
3104 return rc;
3105}
3106
Jeff Garzikbf794512005-07-31 13:07:26 -04003107/**
James Ketrenos43f66a62005-03-25 12:31:53 -06003108 * DMA services
3109 *
3110 * Theory of operation
3111 *
3112 * A queue is a circular buffers with 'Read' and 'Write' pointers.
3113 * 2 empty entries always kept in the buffer to protect from overflow.
3114 *
3115 * For Tx queue, there are low mark and high mark limits. If, after queuing
Jeff Garzikbf794512005-07-31 13:07:26 -04003116 * the packet for Tx, free space become < low mark, Tx queue stopped. When
3117 * reclaiming packets (on 'tx done IRQ), if free space become > high mark,
James Ketrenos43f66a62005-03-25 12:31:53 -06003118 * Tx queue resumed.
3119 *
3120 * The IPW operates with six queues, one receive queue in the device's
3121 * sram, one transmit queue for sending commands to the device firmware,
Jeff Garzikbf794512005-07-31 13:07:26 -04003122 * and four transmit queues for data.
James Ketrenos43f66a62005-03-25 12:31:53 -06003123 *
Jeff Garzikbf794512005-07-31 13:07:26 -04003124 * The four transmit queues allow for performing quality of service (qos)
James Ketrenos43f66a62005-03-25 12:31:53 -06003125 * transmissions as per the 802.11 protocol. Currently Linux does not
Jeff Garzikbf794512005-07-31 13:07:26 -04003126 * provide a mechanism to the user for utilizing prioritized queues, so
James Ketrenos43f66a62005-03-25 12:31:53 -06003127 * we only utilize the first data transmit queue (queue1).
3128 */
3129
3130/**
3131 * Driver allocates buffers of this size for Rx
3132 */
3133
3134static inline int ipw_queue_space(const struct clx2_queue *q)
3135{
3136 int s = q->last_used - q->first_empty;
3137 if (s <= 0)
3138 s += q->n_bd;
3139 s -= 2; /* keep some reserve to not confuse empty and full situations */
3140 if (s < 0)
3141 s = 0;
3142 return s;
3143}
3144
3145static inline int ipw_queue_inc_wrap(int index, int n_bd)
3146{
3147 return (++index == n_bd) ? 0 : index;
3148}
3149
3150/**
3151 * Initialize common DMA queue structure
Jeff Garzikbf794512005-07-31 13:07:26 -04003152 *
James Ketrenos43f66a62005-03-25 12:31:53 -06003153 * @param q queue to init
3154 * @param count Number of BD's to allocate. Should be power of 2
3155 * @param read_register Address for 'read' register
3156 * (not offset within BAR, full address)
3157 * @param write_register Address for 'write' register
3158 * (not offset within BAR, full address)
3159 * @param base_register Address for 'base' register
3160 * (not offset within BAR, full address)
3161 * @param size Address for 'size' register
3162 * (not offset within BAR, full address)
3163 */
Jeff Garzikbf794512005-07-31 13:07:26 -04003164static void ipw_queue_init(struct ipw_priv *priv, struct clx2_queue *q,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003165 int count, u32 read, u32 write, u32 base, u32 size)
James Ketrenos43f66a62005-03-25 12:31:53 -06003166{
3167 q->n_bd = count;
3168
3169 q->low_mark = q->n_bd / 4;
3170 if (q->low_mark < 4)
3171 q->low_mark = 4;
3172
3173 q->high_mark = q->n_bd / 8;
3174 if (q->high_mark < 2)
3175 q->high_mark = 2;
3176
3177 q->first_empty = q->last_used = 0;
3178 q->reg_r = read;
3179 q->reg_w = write;
3180
3181 ipw_write32(priv, base, q->dma_addr);
3182 ipw_write32(priv, size, count);
3183 ipw_write32(priv, read, 0);
3184 ipw_write32(priv, write, 0);
3185
3186 _ipw_read32(priv, 0x90);
3187}
3188
Jeff Garzikbf794512005-07-31 13:07:26 -04003189static int ipw_queue_tx_init(struct ipw_priv *priv,
James Ketrenos43f66a62005-03-25 12:31:53 -06003190 struct clx2_tx_queue *q,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003191 int count, u32 read, u32 write, u32 base, u32 size)
James Ketrenos43f66a62005-03-25 12:31:53 -06003192{
3193 struct pci_dev *dev = priv->pci_dev;
3194
3195 q->txb = kmalloc(sizeof(q->txb[0]) * count, GFP_KERNEL);
3196 if (!q->txb) {
3197 IPW_ERROR("vmalloc for auxilary BD structures failed\n");
3198 return -ENOMEM;
3199 }
3200
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003201 q->bd =
3202 pci_alloc_consistent(dev, sizeof(q->bd[0]) * count, &q->q.dma_addr);
James Ketrenos43f66a62005-03-25 12:31:53 -06003203 if (!q->bd) {
Jiri Bencaaa4d302005-06-07 14:58:41 +02003204 IPW_ERROR("pci_alloc_consistent(%zd) failed\n",
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003205 sizeof(q->bd[0]) * count);
James Ketrenos43f66a62005-03-25 12:31:53 -06003206 kfree(q->txb);
3207 q->txb = NULL;
3208 return -ENOMEM;
3209 }
3210
3211 ipw_queue_init(priv, &q->q, count, read, write, base, size);
3212 return 0;
3213}
3214
3215/**
3216 * Free one TFD, those at index [txq->q.last_used].
3217 * Do NOT advance any indexes
Jeff Garzikbf794512005-07-31 13:07:26 -04003218 *
James Ketrenos43f66a62005-03-25 12:31:53 -06003219 * @param dev
3220 * @param txq
3221 */
3222static void ipw_queue_tx_free_tfd(struct ipw_priv *priv,
3223 struct clx2_tx_queue *txq)
3224{
3225 struct tfd_frame *bd = &txq->bd[txq->q.last_used];
3226 struct pci_dev *dev = priv->pci_dev;
3227 int i;
Jeff Garzikbf794512005-07-31 13:07:26 -04003228
James Ketrenos43f66a62005-03-25 12:31:53 -06003229 /* classify bd */
3230 if (bd->control_flags.message_type == TX_HOST_COMMAND_TYPE)
3231 /* nothing to cleanup after for host commands */
3232 return;
3233
3234 /* sanity check */
James Ketrenosa613bff2005-08-24 21:43:11 -05003235 if (le32_to_cpu(bd->u.data.num_chunks) > NUM_TFD_CHUNKS) {
3236 IPW_ERROR("Too many chunks: %i\n",
3237 le32_to_cpu(bd->u.data.num_chunks));
James Ketrenos43f66a62005-03-25 12:31:53 -06003238 /** @todo issue fatal error, it is quite serious situation */
3239 return;
3240 }
3241
3242 /* unmap chunks if any */
James Ketrenosa613bff2005-08-24 21:43:11 -05003243 for (i = 0; i < le32_to_cpu(bd->u.data.num_chunks); i++) {
3244 pci_unmap_single(dev, le32_to_cpu(bd->u.data.chunk_ptr[i]),
3245 le16_to_cpu(bd->u.data.chunk_len[i]),
3246 PCI_DMA_TODEVICE);
James Ketrenos43f66a62005-03-25 12:31:53 -06003247 if (txq->txb[txq->q.last_used]) {
3248 ieee80211_txb_free(txq->txb[txq->q.last_used]);
3249 txq->txb[txq->q.last_used] = NULL;
3250 }
3251 }
3252}
3253
3254/**
3255 * Deallocate DMA queue.
Jeff Garzikbf794512005-07-31 13:07:26 -04003256 *
James Ketrenos43f66a62005-03-25 12:31:53 -06003257 * Empty queue by removing and destroying all BD's.
3258 * Free all buffers.
Jeff Garzikbf794512005-07-31 13:07:26 -04003259 *
James Ketrenos43f66a62005-03-25 12:31:53 -06003260 * @param dev
3261 * @param q
3262 */
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003263static void ipw_queue_tx_free(struct ipw_priv *priv, struct clx2_tx_queue *txq)
James Ketrenos43f66a62005-03-25 12:31:53 -06003264{
3265 struct clx2_queue *q = &txq->q;
3266 struct pci_dev *dev = priv->pci_dev;
3267
Jeff Garzikbf794512005-07-31 13:07:26 -04003268 if (q->n_bd == 0)
3269 return;
James Ketrenos43f66a62005-03-25 12:31:53 -06003270
3271 /* first, empty all BD's */
3272 for (; q->first_empty != q->last_used;
3273 q->last_used = ipw_queue_inc_wrap(q->last_used, q->n_bd)) {
3274 ipw_queue_tx_free_tfd(priv, txq);
3275 }
Jeff Garzikbf794512005-07-31 13:07:26 -04003276
James Ketrenos43f66a62005-03-25 12:31:53 -06003277 /* free buffers belonging to queue itself */
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003278 pci_free_consistent(dev, sizeof(txq->bd[0]) * q->n_bd, txq->bd,
James Ketrenos43f66a62005-03-25 12:31:53 -06003279 q->dma_addr);
3280 kfree(txq->txb);
3281
3282 /* 0 fill whole structure */
3283 memset(txq, 0, sizeof(*txq));
3284}
3285
James Ketrenos43f66a62005-03-25 12:31:53 -06003286/**
3287 * Destroy all DMA queues and structures
Jeff Garzikbf794512005-07-31 13:07:26 -04003288 *
James Ketrenos43f66a62005-03-25 12:31:53 -06003289 * @param priv
3290 */
3291static void ipw_tx_queue_free(struct ipw_priv *priv)
3292{
3293 /* Tx CMD queue */
3294 ipw_queue_tx_free(priv, &priv->txq_cmd);
3295
3296 /* Tx queues */
3297 ipw_queue_tx_free(priv, &priv->txq[0]);
3298 ipw_queue_tx_free(priv, &priv->txq[1]);
3299 ipw_queue_tx_free(priv, &priv->txq[2]);
3300 ipw_queue_tx_free(priv, &priv->txq[3]);
3301}
3302
3303static void inline __maybe_wake_tx(struct ipw_priv *priv)
3304{
3305 if (netif_running(priv->net_dev)) {
3306 switch (priv->port_type) {
3307 case DCR_TYPE_MU_BSS:
3308 case DCR_TYPE_MU_IBSS:
James Ketrenosa613bff2005-08-24 21:43:11 -05003309 if (!(priv->status & STATUS_ASSOCIATED))
James Ketrenos43f66a62005-03-25 12:31:53 -06003310 return;
James Ketrenos43f66a62005-03-25 12:31:53 -06003311 }
3312 netif_wake_queue(priv->net_dev);
3313 }
3314
3315}
3316
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003317static inline void ipw_create_bssid(struct ipw_priv *priv, u8 * bssid)
James Ketrenos43f66a62005-03-25 12:31:53 -06003318{
3319 /* First 3 bytes are manufacturer */
3320 bssid[0] = priv->mac_addr[0];
3321 bssid[1] = priv->mac_addr[1];
3322 bssid[2] = priv->mac_addr[2];
3323
3324 /* Last bytes are random */
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003325 get_random_bytes(&bssid[3], ETH_ALEN - 3);
James Ketrenos43f66a62005-03-25 12:31:53 -06003326
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003327 bssid[0] &= 0xfe; /* clear multicast bit */
3328 bssid[0] |= 0x02; /* set local assignment bit (IEEE802) */
James Ketrenos43f66a62005-03-25 12:31:53 -06003329}
3330
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003331static inline u8 ipw_add_station(struct ipw_priv *priv, u8 * bssid)
James Ketrenos43f66a62005-03-25 12:31:53 -06003332{
3333 struct ipw_station_entry entry;
3334 int i;
3335
3336 for (i = 0; i < priv->num_stations; i++) {
3337 if (!memcmp(priv->stations[i], bssid, ETH_ALEN)) {
3338 /* Another node is active in network */
3339 priv->missed_adhoc_beacons = 0;
3340 if (!(priv->config & CFG_STATIC_CHANNEL))
3341 /* when other nodes drop out, we drop out */
3342 priv->config &= ~CFG_ADHOC_PERSIST;
3343
3344 return i;
3345 }
3346 }
3347
3348 if (i == MAX_STATIONS)
3349 return IPW_INVALID_STATION;
3350
3351 IPW_DEBUG_SCAN("Adding AdHoc station: " MAC_FMT "\n", MAC_ARG(bssid));
3352
3353 entry.reserved = 0;
3354 entry.support_mode = 0;
3355 memcpy(entry.mac_addr, bssid, ETH_ALEN);
3356 memcpy(priv->stations[i], bssid, ETH_ALEN);
3357 ipw_write_direct(priv, IPW_STATION_TABLE_LOWER + i * sizeof(entry),
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003358 &entry, sizeof(entry));
James Ketrenos43f66a62005-03-25 12:31:53 -06003359 priv->num_stations++;
3360
3361 return i;
3362}
3363
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003364static inline u8 ipw_find_station(struct ipw_priv *priv, u8 * bssid)
James Ketrenos43f66a62005-03-25 12:31:53 -06003365{
3366 int i;
3367
Jeff Garzikbf794512005-07-31 13:07:26 -04003368 for (i = 0; i < priv->num_stations; i++)
3369 if (!memcmp(priv->stations[i], bssid, ETH_ALEN))
James Ketrenos43f66a62005-03-25 12:31:53 -06003370 return i;
3371
3372 return IPW_INVALID_STATION;
3373}
3374
3375static void ipw_send_disassociate(struct ipw_priv *priv, int quiet)
3376{
3377 int err;
3378
3379 if (!(priv->status & (STATUS_ASSOCIATING | STATUS_ASSOCIATED))) {
3380 IPW_DEBUG_ASSOC("Disassociating while not associated.\n");
3381 return;
3382 }
3383
3384 IPW_DEBUG_ASSOC("Disassocation attempt from " MAC_FMT " "
3385 "on channel %d.\n",
Jeff Garzikbf794512005-07-31 13:07:26 -04003386 MAC_ARG(priv->assoc_request.bssid),
James Ketrenos43f66a62005-03-25 12:31:53 -06003387 priv->assoc_request.channel);
3388
3389 priv->status &= ~(STATUS_ASSOCIATING | STATUS_ASSOCIATED);
3390 priv->status |= STATUS_DISASSOCIATING;
3391
3392 if (quiet)
3393 priv->assoc_request.assoc_type = HC_DISASSOC_QUIET;
3394 else
3395 priv->assoc_request.assoc_type = HC_DISASSOCIATE;
3396 err = ipw_send_associate(priv, &priv->assoc_request);
3397 if (err) {
3398 IPW_DEBUG_HC("Attempt to send [dis]associate command "
3399 "failed.\n");
3400 return;
3401 }
3402
3403}
3404
James Ketrenosc848d0a2005-08-24 21:56:24 -05003405static int ipw_disassociate(void *data)
James Ketrenos43f66a62005-03-25 12:31:53 -06003406{
James Ketrenosc848d0a2005-08-24 21:56:24 -05003407 struct ipw_priv *priv = data;
3408 if (!(priv->status & (STATUS_ASSOCIATED | STATUS_ASSOCIATING)))
3409 return 0;
James Ketrenos43f66a62005-03-25 12:31:53 -06003410 ipw_send_disassociate(data, 0);
James Ketrenosc848d0a2005-08-24 21:56:24 -05003411 return 1;
3412}
3413
3414static void ipw_bg_disassociate(void *data)
3415{
3416 struct ipw_priv *priv = data;
3417 down(&priv->sem);
3418 ipw_disassociate(data);
3419 up(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06003420}
3421
James Ketrenos43f66a62005-03-25 12:31:53 -06003422struct ipw_status_code {
3423 u16 status;
3424 const char *reason;
3425};
3426
3427static const struct ipw_status_code ipw_status_codes[] = {
3428 {0x00, "Successful"},
3429 {0x01, "Unspecified failure"},
3430 {0x0A, "Cannot support all requested capabilities in the "
3431 "Capability information field"},
3432 {0x0B, "Reassociation denied due to inability to confirm that "
3433 "association exists"},
3434 {0x0C, "Association denied due to reason outside the scope of this "
3435 "standard"},
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003436 {0x0D,
3437 "Responding station does not support the specified authentication "
James Ketrenos43f66a62005-03-25 12:31:53 -06003438 "algorithm"},
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003439 {0x0E,
3440 "Received an Authentication frame with authentication sequence "
James Ketrenos43f66a62005-03-25 12:31:53 -06003441 "transaction sequence number out of expected sequence"},
3442 {0x0F, "Authentication rejected because of challenge failure"},
3443 {0x10, "Authentication rejected due to timeout waiting for next "
3444 "frame in sequence"},
3445 {0x11, "Association denied because AP is unable to handle additional "
3446 "associated stations"},
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003447 {0x12,
3448 "Association denied due to requesting station not supporting all "
James Ketrenos43f66a62005-03-25 12:31:53 -06003449 "of the datarates in the BSSBasicServiceSet Parameter"},
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003450 {0x13,
3451 "Association denied due to requesting station not supporting "
James Ketrenos43f66a62005-03-25 12:31:53 -06003452 "short preamble operation"},
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003453 {0x14,
3454 "Association denied due to requesting station not supporting "
James Ketrenos43f66a62005-03-25 12:31:53 -06003455 "PBCC encoding"},
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003456 {0x15,
3457 "Association denied due to requesting station not supporting "
James Ketrenos43f66a62005-03-25 12:31:53 -06003458 "channel agility"},
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003459 {0x19,
3460 "Association denied due to requesting station not supporting "
James Ketrenos43f66a62005-03-25 12:31:53 -06003461 "short slot operation"},
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003462 {0x1A,
3463 "Association denied due to requesting station not supporting "
James Ketrenos43f66a62005-03-25 12:31:53 -06003464 "DSSS-OFDM operation"},
3465 {0x28, "Invalid Information Element"},
3466 {0x29, "Group Cipher is not valid"},
3467 {0x2A, "Pairwise Cipher is not valid"},
3468 {0x2B, "AKMP is not valid"},
3469 {0x2C, "Unsupported RSN IE version"},
3470 {0x2D, "Invalid RSN IE Capabilities"},
3471 {0x2E, "Cipher suite is rejected per security policy"},
3472};
3473
3474#ifdef CONFIG_IPW_DEBUG
Jeff Garzikbf794512005-07-31 13:07:26 -04003475static const char *ipw_get_status_code(u16 status)
James Ketrenos43f66a62005-03-25 12:31:53 -06003476{
3477 int i;
Jeff Garzikbf794512005-07-31 13:07:26 -04003478 for (i = 0; i < ARRAY_SIZE(ipw_status_codes); i++)
James Ketrenosea2b26e2005-08-24 21:25:16 -05003479 if (ipw_status_codes[i].status == (status & 0xff))
James Ketrenos43f66a62005-03-25 12:31:53 -06003480 return ipw_status_codes[i].reason;
3481 return "Unknown status value.";
3482}
3483#endif
3484
3485static void inline average_init(struct average *avg)
3486{
3487 memset(avg, 0, sizeof(*avg));
3488}
3489
3490static void inline average_add(struct average *avg, s16 val)
3491{
3492 avg->sum -= avg->entries[avg->pos];
3493 avg->sum += val;
3494 avg->entries[avg->pos++] = val;
3495 if (unlikely(avg->pos == AVG_ENTRIES)) {
3496 avg->init = 1;
3497 avg->pos = 0;
3498 }
3499}
3500
3501static s16 inline average_value(struct average *avg)
3502{
3503 if (!unlikely(avg->init)) {
3504 if (avg->pos)
3505 return avg->sum / avg->pos;
3506 return 0;
3507 }
3508
3509 return avg->sum / AVG_ENTRIES;
3510}
3511
3512static void ipw_reset_stats(struct ipw_priv *priv)
3513{
3514 u32 len = sizeof(u32);
3515
3516 priv->quality = 0;
3517
3518 average_init(&priv->average_missed_beacons);
3519 average_init(&priv->average_rssi);
3520 average_init(&priv->average_noise);
3521
3522 priv->last_rate = 0;
3523 priv->last_missed_beacons = 0;
3524 priv->last_rx_packets = 0;
3525 priv->last_tx_packets = 0;
3526 priv->last_tx_failures = 0;
Jeff Garzikbf794512005-07-31 13:07:26 -04003527
James Ketrenos43f66a62005-03-25 12:31:53 -06003528 /* Firmware managed, reset only when NIC is restarted, so we have to
3529 * normalize on the current value */
Jeff Garzikbf794512005-07-31 13:07:26 -04003530 ipw_get_ordinal(priv, IPW_ORD_STAT_RX_ERR_CRC,
James Ketrenos43f66a62005-03-25 12:31:53 -06003531 &priv->last_rx_err, &len);
Jeff Garzikbf794512005-07-31 13:07:26 -04003532 ipw_get_ordinal(priv, IPW_ORD_STAT_TX_FAILURE,
James Ketrenos43f66a62005-03-25 12:31:53 -06003533 &priv->last_tx_failures, &len);
3534
3535 /* Driver managed, reset with each association */
3536 priv->missed_adhoc_beacons = 0;
3537 priv->missed_beacons = 0;
3538 priv->tx_packets = 0;
3539 priv->rx_packets = 0;
3540
3541}
3542
James Ketrenos43f66a62005-03-25 12:31:53 -06003543static inline u32 ipw_get_max_rate(struct ipw_priv *priv)
3544{
3545 u32 i = 0x80000000;
3546 u32 mask = priv->rates_mask;
3547 /* If currently associated in B mode, restrict the maximum
3548 * rate match to B rates */
3549 if (priv->assoc_request.ieee_mode == IPW_B_MODE)
3550 mask &= IEEE80211_CCK_RATES_MASK;
3551
3552 /* TODO: Verify that the rate is supported by the current rates
3553 * list. */
3554
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003555 while (i && !(mask & i))
3556 i >>= 1;
James Ketrenos43f66a62005-03-25 12:31:53 -06003557 switch (i) {
James Ketrenosea2b26e2005-08-24 21:25:16 -05003558 case IEEE80211_CCK_RATE_1MB_MASK:
3559 return 1000000;
3560 case IEEE80211_CCK_RATE_2MB_MASK:
3561 return 2000000;
3562 case IEEE80211_CCK_RATE_5MB_MASK:
3563 return 5500000;
3564 case IEEE80211_OFDM_RATE_6MB_MASK:
3565 return 6000000;
3566 case IEEE80211_OFDM_RATE_9MB_MASK:
3567 return 9000000;
3568 case IEEE80211_CCK_RATE_11MB_MASK:
3569 return 11000000;
3570 case IEEE80211_OFDM_RATE_12MB_MASK:
3571 return 12000000;
3572 case IEEE80211_OFDM_RATE_18MB_MASK:
3573 return 18000000;
3574 case IEEE80211_OFDM_RATE_24MB_MASK:
3575 return 24000000;
3576 case IEEE80211_OFDM_RATE_36MB_MASK:
3577 return 36000000;
3578 case IEEE80211_OFDM_RATE_48MB_MASK:
3579 return 48000000;
3580 case IEEE80211_OFDM_RATE_54MB_MASK:
3581 return 54000000;
James Ketrenos43f66a62005-03-25 12:31:53 -06003582 }
3583
Jeff Garzikbf794512005-07-31 13:07:26 -04003584 if (priv->ieee->mode == IEEE_B)
James Ketrenos43f66a62005-03-25 12:31:53 -06003585 return 11000000;
3586 else
3587 return 54000000;
3588}
3589
3590static u32 ipw_get_current_rate(struct ipw_priv *priv)
3591{
3592 u32 rate, len = sizeof(rate);
3593 int err;
3594
Jeff Garzikbf794512005-07-31 13:07:26 -04003595 if (!(priv->status & STATUS_ASSOCIATED))
James Ketrenos43f66a62005-03-25 12:31:53 -06003596 return 0;
3597
3598 if (priv->tx_packets > IPW_REAL_RATE_RX_PACKET_THRESHOLD) {
Jeff Garzikbf794512005-07-31 13:07:26 -04003599 err = ipw_get_ordinal(priv, IPW_ORD_STAT_TX_CURR_RATE, &rate,
James Ketrenos43f66a62005-03-25 12:31:53 -06003600 &len);
3601 if (err) {
3602 IPW_DEBUG_INFO("failed querying ordinals.\n");
3603 return 0;
3604 }
Jeff Garzikbf794512005-07-31 13:07:26 -04003605 } else
James Ketrenos43f66a62005-03-25 12:31:53 -06003606 return ipw_get_max_rate(priv);
3607
3608 switch (rate) {
James Ketrenosea2b26e2005-08-24 21:25:16 -05003609 case IPW_TX_RATE_1MB:
3610 return 1000000;
3611 case IPW_TX_RATE_2MB:
3612 return 2000000;
3613 case IPW_TX_RATE_5MB:
3614 return 5500000;
3615 case IPW_TX_RATE_6MB:
3616 return 6000000;
3617 case IPW_TX_RATE_9MB:
3618 return 9000000;
3619 case IPW_TX_RATE_11MB:
3620 return 11000000;
3621 case IPW_TX_RATE_12MB:
3622 return 12000000;
3623 case IPW_TX_RATE_18MB:
3624 return 18000000;
3625 case IPW_TX_RATE_24MB:
3626 return 24000000;
3627 case IPW_TX_RATE_36MB:
3628 return 36000000;
3629 case IPW_TX_RATE_48MB:
3630 return 48000000;
3631 case IPW_TX_RATE_54MB:
3632 return 54000000;
James Ketrenos43f66a62005-03-25 12:31:53 -06003633 }
3634
3635 return 0;
3636}
3637
James Ketrenos43f66a62005-03-25 12:31:53 -06003638#define IPW_STATS_INTERVAL (2 * HZ)
3639static void ipw_gather_stats(struct ipw_priv *priv)
3640{
3641 u32 rx_err, rx_err_delta, rx_packets_delta;
3642 u32 tx_failures, tx_failures_delta, tx_packets_delta;
3643 u32 missed_beacons_percent, missed_beacons_delta;
3644 u32 quality = 0;
3645 u32 len = sizeof(u32);
3646 s16 rssi;
Jeff Garzikbf794512005-07-31 13:07:26 -04003647 u32 beacon_quality, signal_quality, tx_quality, rx_quality,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003648 rate_quality;
James Ketrenosea2b26e2005-08-24 21:25:16 -05003649 u32 max_rate;
James Ketrenos43f66a62005-03-25 12:31:53 -06003650
3651 if (!(priv->status & STATUS_ASSOCIATED)) {
3652 priv->quality = 0;
3653 return;
3654 }
3655
3656 /* Update the statistics */
Jeff Garzikbf794512005-07-31 13:07:26 -04003657 ipw_get_ordinal(priv, IPW_ORD_STAT_MISSED_BEACONS,
James Ketrenos43f66a62005-03-25 12:31:53 -06003658 &priv->missed_beacons, &len);
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003659 missed_beacons_delta = priv->missed_beacons - priv->last_missed_beacons;
James Ketrenos43f66a62005-03-25 12:31:53 -06003660 priv->last_missed_beacons = priv->missed_beacons;
3661 if (priv->assoc_request.beacon_interval) {
3662 missed_beacons_percent = missed_beacons_delta *
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003663 (HZ * priv->assoc_request.beacon_interval) /
3664 (IPW_STATS_INTERVAL * 10);
James Ketrenos43f66a62005-03-25 12:31:53 -06003665 } else {
3666 missed_beacons_percent = 0;
3667 }
3668 average_add(&priv->average_missed_beacons, missed_beacons_percent);
3669
3670 ipw_get_ordinal(priv, IPW_ORD_STAT_RX_ERR_CRC, &rx_err, &len);
3671 rx_err_delta = rx_err - priv->last_rx_err;
3672 priv->last_rx_err = rx_err;
3673
3674 ipw_get_ordinal(priv, IPW_ORD_STAT_TX_FAILURE, &tx_failures, &len);
3675 tx_failures_delta = tx_failures - priv->last_tx_failures;
3676 priv->last_tx_failures = tx_failures;
3677
3678 rx_packets_delta = priv->rx_packets - priv->last_rx_packets;
3679 priv->last_rx_packets = priv->rx_packets;
3680
3681 tx_packets_delta = priv->tx_packets - priv->last_tx_packets;
3682 priv->last_tx_packets = priv->tx_packets;
3683
3684 /* Calculate quality based on the following:
Jeff Garzikbf794512005-07-31 13:07:26 -04003685 *
James Ketrenos43f66a62005-03-25 12:31:53 -06003686 * Missed beacon: 100% = 0, 0% = 70% missed
3687 * Rate: 60% = 1Mbs, 100% = Max
3688 * Rx and Tx errors represent a straight % of total Rx/Tx
3689 * RSSI: 100% = > -50, 0% = < -80
3690 * Rx errors: 100% = 0, 0% = 50% missed
Jeff Garzikbf794512005-07-31 13:07:26 -04003691 *
James Ketrenos43f66a62005-03-25 12:31:53 -06003692 * The lowest computed quality is used.
3693 *
3694 */
3695#define BEACON_THRESHOLD 5
3696 beacon_quality = 100 - missed_beacons_percent;
3697 if (beacon_quality < BEACON_THRESHOLD)
3698 beacon_quality = 0;
3699 else
Jeff Garzikbf794512005-07-31 13:07:26 -04003700 beacon_quality = (beacon_quality - BEACON_THRESHOLD) * 100 /
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003701 (100 - BEACON_THRESHOLD);
Jeff Garzikbf794512005-07-31 13:07:26 -04003702 IPW_DEBUG_STATS("Missed beacon: %3d%% (%d%%)\n",
James Ketrenos43f66a62005-03-25 12:31:53 -06003703 beacon_quality, missed_beacons_percent);
Jeff Garzikbf794512005-07-31 13:07:26 -04003704
James Ketrenos43f66a62005-03-25 12:31:53 -06003705 priv->last_rate = ipw_get_current_rate(priv);
James Ketrenosea2b26e2005-08-24 21:25:16 -05003706 max_rate = ipw_get_max_rate(priv);
3707 rate_quality = priv->last_rate * 40 / max_rate + 60;
James Ketrenos43f66a62005-03-25 12:31:53 -06003708 IPW_DEBUG_STATS("Rate quality : %3d%% (%dMbs)\n",
3709 rate_quality, priv->last_rate / 1000000);
Jeff Garzikbf794512005-07-31 13:07:26 -04003710
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003711 if (rx_packets_delta > 100 && rx_packets_delta + rx_err_delta)
Jeff Garzikbf794512005-07-31 13:07:26 -04003712 rx_quality = 100 - (rx_err_delta * 100) /
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003713 (rx_packets_delta + rx_err_delta);
James Ketrenos43f66a62005-03-25 12:31:53 -06003714 else
3715 rx_quality = 100;
3716 IPW_DEBUG_STATS("Rx quality : %3d%% (%u errors, %u packets)\n",
3717 rx_quality, rx_err_delta, rx_packets_delta);
Jeff Garzikbf794512005-07-31 13:07:26 -04003718
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003719 if (tx_packets_delta > 100 && tx_packets_delta + tx_failures_delta)
Jeff Garzikbf794512005-07-31 13:07:26 -04003720 tx_quality = 100 - (tx_failures_delta * 100) /
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003721 (tx_packets_delta + tx_failures_delta);
James Ketrenos43f66a62005-03-25 12:31:53 -06003722 else
3723 tx_quality = 100;
3724 IPW_DEBUG_STATS("Tx quality : %3d%% (%u errors, %u packets)\n",
3725 tx_quality, tx_failures_delta, tx_packets_delta);
Jeff Garzikbf794512005-07-31 13:07:26 -04003726
James Ketrenos43f66a62005-03-25 12:31:53 -06003727 rssi = average_value(&priv->average_rssi);
James Ketrenosc848d0a2005-08-24 21:56:24 -05003728 signal_quality =
3729 (100 *
3730 (priv->ieee->perfect_rssi - priv->ieee->worst_rssi) *
3731 (priv->ieee->perfect_rssi - priv->ieee->worst_rssi) -
3732 (priv->ieee->perfect_rssi - rssi) *
3733 (15 * (priv->ieee->perfect_rssi - priv->ieee->worst_rssi) +
3734 62 * (priv->ieee->perfect_rssi - rssi))) /
3735 ((priv->ieee->perfect_rssi - priv->ieee->worst_rssi) *
3736 (priv->ieee->perfect_rssi - priv->ieee->worst_rssi));
3737 if (signal_quality > 100)
James Ketrenos43f66a62005-03-25 12:31:53 -06003738 signal_quality = 100;
James Ketrenosc848d0a2005-08-24 21:56:24 -05003739 else if (signal_quality < 1)
James Ketrenos43f66a62005-03-25 12:31:53 -06003740 signal_quality = 0;
James Ketrenosea2b26e2005-08-24 21:25:16 -05003741
James Ketrenos43f66a62005-03-25 12:31:53 -06003742 IPW_DEBUG_STATS("Signal level : %3d%% (%d dBm)\n",
3743 signal_quality, rssi);
Jeff Garzikbf794512005-07-31 13:07:26 -04003744
3745 quality = min(beacon_quality,
James Ketrenos43f66a62005-03-25 12:31:53 -06003746 min(rate_quality,
3747 min(tx_quality, min(rx_quality, signal_quality))));
3748 if (quality == beacon_quality)
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003749 IPW_DEBUG_STATS("Quality (%d%%): Clamped to missed beacons.\n",
3750 quality);
James Ketrenos43f66a62005-03-25 12:31:53 -06003751 if (quality == rate_quality)
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003752 IPW_DEBUG_STATS("Quality (%d%%): Clamped to rate quality.\n",
3753 quality);
James Ketrenos43f66a62005-03-25 12:31:53 -06003754 if (quality == tx_quality)
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003755 IPW_DEBUG_STATS("Quality (%d%%): Clamped to Tx quality.\n",
3756 quality);
James Ketrenos43f66a62005-03-25 12:31:53 -06003757 if (quality == rx_quality)
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003758 IPW_DEBUG_STATS("Quality (%d%%): Clamped to Rx quality.\n",
3759 quality);
James Ketrenos43f66a62005-03-25 12:31:53 -06003760 if (quality == signal_quality)
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003761 IPW_DEBUG_STATS("Quality (%d%%): Clamped to signal quality.\n",
3762 quality);
James Ketrenos43f66a62005-03-25 12:31:53 -06003763
3764 priv->quality = quality;
Jeff Garzikbf794512005-07-31 13:07:26 -04003765
3766 queue_delayed_work(priv->workqueue, &priv->gather_stats,
James Ketrenos43f66a62005-03-25 12:31:53 -06003767 IPW_STATS_INTERVAL);
3768}
3769
James Ketrenosc848d0a2005-08-24 21:56:24 -05003770static void ipw_bg_gather_stats(void *data)
3771{
3772 struct ipw_priv *priv = data;
3773 down(&priv->sem);
3774 ipw_gather_stats(data);
3775 up(&priv->sem);
3776}
3777
James Ketrenosea2b26e2005-08-24 21:25:16 -05003778static inline void ipw_handle_missed_beacon(struct ipw_priv *priv,
3779 int missed_count)
3780{
3781 priv->notif_missed_beacons = missed_count;
3782
3783 if (missed_count > priv->missed_beacon_threshold &&
3784 priv->status & STATUS_ASSOCIATED) {
3785 /* If associated and we've hit the missed
3786 * beacon threshold, disassociate, turn
3787 * off roaming, and abort any active scans */
3788 IPW_DEBUG(IPW_DL_INFO | IPW_DL_NOTIF |
3789 IPW_DL_STATE,
3790 "Missed beacon: %d - disassociate\n", missed_count);
3791 priv->status &= ~STATUS_ROAMING;
James Ketrenosa613bff2005-08-24 21:43:11 -05003792 if (priv->status & STATUS_SCANNING) {
3793 IPW_DEBUG(IPW_DL_INFO | IPW_DL_NOTIF |
3794 IPW_DL_STATE,
3795 "Aborting scan with missed beacon.\n");
James Ketrenosea2b26e2005-08-24 21:25:16 -05003796 queue_work(priv->workqueue, &priv->abort_scan);
James Ketrenosa613bff2005-08-24 21:43:11 -05003797 }
3798
James Ketrenosea2b26e2005-08-24 21:25:16 -05003799 queue_work(priv->workqueue, &priv->disassociate);
3800 return;
3801 }
3802
3803 if (priv->status & STATUS_ROAMING) {
3804 /* If we are currently roaming, then just
3805 * print a debug statement... */
3806 IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE,
3807 "Missed beacon: %d - roam in progress\n",
3808 missed_count);
3809 return;
3810 }
3811
3812 if (missed_count > priv->roaming_threshold) {
3813 /* If we are not already roaming, set the ROAM
3814 * bit in the status and kick off a scan */
3815 IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE,
3816 "Missed beacon: %d - initiate "
3817 "roaming\n", missed_count);
3818 if (!(priv->status & STATUS_ROAMING)) {
3819 priv->status |= STATUS_ROAMING;
3820 if (!(priv->status & STATUS_SCANNING))
3821 queue_work(priv->workqueue,
3822 &priv->request_scan);
3823 }
3824 return;
3825 }
3826
3827 if (priv->status & STATUS_SCANNING) {
3828 /* Stop scan to keep fw from getting
3829 * stuck (only if we aren't roaming --
3830 * otherwise we'll never scan more than 2 or 3
3831 * channels..) */
James Ketrenosa613bff2005-08-24 21:43:11 -05003832 IPW_DEBUG(IPW_DL_INFO | IPW_DL_NOTIF |
3833 IPW_DL_STATE, "Aborting scan with missed beacon.\n");
James Ketrenosea2b26e2005-08-24 21:25:16 -05003834 queue_work(priv->workqueue, &priv->abort_scan);
3835 }
3836
3837 IPW_DEBUG_NOTIF("Missed beacon: %d\n", missed_count);
3838
3839}
3840
James Ketrenos43f66a62005-03-25 12:31:53 -06003841/**
3842 * Handle host notification packet.
3843 * Called from interrupt routine
3844 */
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003845static inline void ipw_rx_notification(struct ipw_priv *priv,
James Ketrenos43f66a62005-03-25 12:31:53 -06003846 struct ipw_rx_notification *notif)
3847{
James Ketrenosa613bff2005-08-24 21:43:11 -05003848 notif->size = le16_to_cpu(notif->size);
3849
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003850 IPW_DEBUG_NOTIF("type = %i (%d bytes)\n", notif->subtype, notif->size);
Jeff Garzikbf794512005-07-31 13:07:26 -04003851
James Ketrenos43f66a62005-03-25 12:31:53 -06003852 switch (notif->subtype) {
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003853 case HOST_NOTIFICATION_STATUS_ASSOCIATED:{
3854 struct notif_association *assoc = &notif->u.assoc;
Jeff Garzikbf794512005-07-31 13:07:26 -04003855
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003856 switch (assoc->state) {
3857 case CMAS_ASSOCIATED:{
3858 IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE |
3859 IPW_DL_ASSOC,
3860 "associated: '%s' " MAC_FMT
3861 " \n",
3862 escape_essid(priv->essid,
3863 priv->essid_len),
3864 MAC_ARG(priv->bssid));
Jeff Garzikbf794512005-07-31 13:07:26 -04003865
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003866 switch (priv->ieee->iw_mode) {
3867 case IW_MODE_INFRA:
3868 memcpy(priv->ieee->bssid,
3869 priv->bssid, ETH_ALEN);
3870 break;
James Ketrenos43f66a62005-03-25 12:31:53 -06003871
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003872 case IW_MODE_ADHOC:
3873 memcpy(priv->ieee->bssid,
3874 priv->bssid, ETH_ALEN);
Jeff Garzikbf794512005-07-31 13:07:26 -04003875
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003876 /* clear out the station table */
3877 priv->num_stations = 0;
James Ketrenos43f66a62005-03-25 12:31:53 -06003878
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003879 IPW_DEBUG_ASSOC
3880 ("queueing adhoc check\n");
3881 queue_delayed_work(priv->
3882 workqueue,
3883 &priv->
3884 adhoc_check,
3885 priv->
3886 assoc_request.
3887 beacon_interval);
3888 break;
3889 }
James Ketrenos43f66a62005-03-25 12:31:53 -06003890
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003891 priv->status &= ~STATUS_ASSOCIATING;
3892 priv->status |= STATUS_ASSOCIATED;
James Ketrenos43f66a62005-03-25 12:31:53 -06003893
James Ketrenosa613bff2005-08-24 21:43:11 -05003894 schedule_work(&priv->link_up);
James Ketrenos43f66a62005-03-25 12:31:53 -06003895
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003896 break;
3897 }
3898
3899 case CMAS_AUTHENTICATED:{
3900 if (priv->
3901 status & (STATUS_ASSOCIATED |
3902 STATUS_AUTH)) {
3903#ifdef CONFIG_IPW_DEBUG
3904 struct notif_authenticate *auth
3905 = &notif->u.auth;
3906 IPW_DEBUG(IPW_DL_NOTIF |
3907 IPW_DL_STATE |
3908 IPW_DL_ASSOC,
3909 "deauthenticated: '%s' "
3910 MAC_FMT
3911 ": (0x%04X) - %s \n",
3912 escape_essid(priv->
3913 essid,
3914 priv->
3915 essid_len),
3916 MAC_ARG(priv->bssid),
3917 ntohs(auth->status),
3918 ipw_get_status_code
3919 (ntohs
3920 (auth->status)));
3921#endif
3922
3923 priv->status &=
3924 ~(STATUS_ASSOCIATING |
3925 STATUS_AUTH |
3926 STATUS_ASSOCIATED);
3927
James Ketrenosa613bff2005-08-24 21:43:11 -05003928 schedule_work(&priv->link_down);
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003929 break;
3930 }
3931
3932 IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE |
3933 IPW_DL_ASSOC,
3934 "authenticated: '%s' " MAC_FMT
3935 "\n",
3936 escape_essid(priv->essid,
3937 priv->essid_len),
3938 MAC_ARG(priv->bssid));
3939 break;
3940 }
3941
3942 case CMAS_INIT:{
James Ketrenosea2b26e2005-08-24 21:25:16 -05003943 if (priv->status & STATUS_AUTH) {
3944 struct
3945 ieee80211_assoc_response
3946 *resp;
3947 resp =
3948 (struct
3949 ieee80211_assoc_response
3950 *)&notif->u.raw;
3951 IPW_DEBUG(IPW_DL_NOTIF |
3952 IPW_DL_STATE |
3953 IPW_DL_ASSOC,
3954 "association failed (0x%04X): %s\n",
3955 ntohs(resp->status),
3956 ipw_get_status_code
3957 (ntohs
3958 (resp->status)));
3959 }
3960
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003961 IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE |
3962 IPW_DL_ASSOC,
3963 "disassociated: '%s' " MAC_FMT
3964 " \n",
3965 escape_essid(priv->essid,
3966 priv->essid_len),
3967 MAC_ARG(priv->bssid));
3968
3969 priv->status &=
3970 ~(STATUS_DISASSOCIATING |
3971 STATUS_ASSOCIATING |
3972 STATUS_ASSOCIATED | STATUS_AUTH);
3973
James Ketrenosa613bff2005-08-24 21:43:11 -05003974 schedule_work(&priv->link_down);
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003975
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003976 break;
3977 }
3978
3979 default:
3980 IPW_ERROR("assoc: unknown (%d)\n",
3981 assoc->state);
3982 break;
3983 }
3984
James Ketrenos43f66a62005-03-25 12:31:53 -06003985 break;
3986 }
Jeff Garzikbf794512005-07-31 13:07:26 -04003987
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003988 case HOST_NOTIFICATION_STATUS_AUTHENTICATE:{
3989 struct notif_authenticate *auth = &notif->u.auth;
3990 switch (auth->state) {
3991 case CMAS_AUTHENTICATED:
3992 IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE,
3993 "authenticated: '%s' " MAC_FMT " \n",
3994 escape_essid(priv->essid,
3995 priv->essid_len),
3996 MAC_ARG(priv->bssid));
3997 priv->status |= STATUS_AUTH;
3998 break;
3999
4000 case CMAS_INIT:
4001 if (priv->status & STATUS_AUTH) {
4002 IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE |
4003 IPW_DL_ASSOC,
4004 "authentication failed (0x%04X): %s\n",
4005 ntohs(auth->status),
4006 ipw_get_status_code(ntohs
4007 (auth->
4008 status)));
4009 }
4010 IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE |
4011 IPW_DL_ASSOC,
4012 "deauthenticated: '%s' " MAC_FMT "\n",
4013 escape_essid(priv->essid,
4014 priv->essid_len),
4015 MAC_ARG(priv->bssid));
James Ketrenos43f66a62005-03-25 12:31:53 -06004016
4017 priv->status &= ~(STATUS_ASSOCIATING |
4018 STATUS_AUTH |
4019 STATUS_ASSOCIATED);
4020
James Ketrenosa613bff2005-08-24 21:43:11 -05004021 schedule_work(&priv->link_down);
James Ketrenos43f66a62005-03-25 12:31:53 -06004022 break;
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004023
4024 case CMAS_TX_AUTH_SEQ_1:
4025 IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE |
4026 IPW_DL_ASSOC, "AUTH_SEQ_1\n");
4027 break;
4028 case CMAS_RX_AUTH_SEQ_2:
4029 IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE |
4030 IPW_DL_ASSOC, "AUTH_SEQ_2\n");
4031 break;
4032 case CMAS_AUTH_SEQ_1_PASS:
4033 IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE |
4034 IPW_DL_ASSOC, "AUTH_SEQ_1_PASS\n");
4035 break;
4036 case CMAS_AUTH_SEQ_1_FAIL:
4037 IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE |
4038 IPW_DL_ASSOC, "AUTH_SEQ_1_FAIL\n");
4039 break;
4040 case CMAS_TX_AUTH_SEQ_3:
4041 IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE |
4042 IPW_DL_ASSOC, "AUTH_SEQ_3\n");
4043 break;
4044 case CMAS_RX_AUTH_SEQ_4:
4045 IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE |
4046 IPW_DL_ASSOC, "RX_AUTH_SEQ_4\n");
4047 break;
4048 case CMAS_AUTH_SEQ_2_PASS:
4049 IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE |
4050 IPW_DL_ASSOC, "AUTH_SEQ_2_PASS\n");
4051 break;
4052 case CMAS_AUTH_SEQ_2_FAIL:
4053 IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE |
4054 IPW_DL_ASSOC, "AUT_SEQ_2_FAIL\n");
4055 break;
4056 case CMAS_TX_ASSOC:
4057 IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE |
4058 IPW_DL_ASSOC, "TX_ASSOC\n");
4059 break;
4060 case CMAS_RX_ASSOC_RESP:
4061 IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE |
4062 IPW_DL_ASSOC, "RX_ASSOC_RESP\n");
4063 break;
4064 case CMAS_ASSOCIATED:
4065 IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE |
4066 IPW_DL_ASSOC, "ASSOCIATED\n");
4067 break;
4068 default:
4069 IPW_DEBUG_NOTIF("auth: failure - %d\n",
4070 auth->state);
4071 break;
4072 }
4073 break;
4074 }
4075
4076 case HOST_NOTIFICATION_STATUS_SCAN_CHANNEL_RESULT:{
4077 struct notif_channel_result *x =
4078 &notif->u.channel_result;
4079
4080 if (notif->size == sizeof(*x)) {
4081 IPW_DEBUG_SCAN("Scan result for channel %d\n",
4082 x->channel_num);
4083 } else {
4084 IPW_DEBUG_SCAN("Scan result of wrong size %d "
4085 "(should be %zd)\n",
4086 notif->size, sizeof(*x));
4087 }
4088 break;
4089 }
4090
4091 case HOST_NOTIFICATION_STATUS_SCAN_COMPLETED:{
4092 struct notif_scan_complete *x = &notif->u.scan_complete;
4093 if (notif->size == sizeof(*x)) {
4094 IPW_DEBUG_SCAN
4095 ("Scan completed: type %d, %d channels, "
4096 "%d status\n", x->scan_type,
4097 x->num_channels, x->status);
4098 } else {
4099 IPW_ERROR("Scan completed of wrong size %d "
4100 "(should be %zd)\n",
4101 notif->size, sizeof(*x));
4102 }
4103
4104 priv->status &=
4105 ~(STATUS_SCANNING | STATUS_SCAN_ABORTING);
4106
4107 cancel_delayed_work(&priv->scan_check);
4108
4109 if (!(priv->status & (STATUS_ASSOCIATED |
4110 STATUS_ASSOCIATING |
4111 STATUS_ROAMING |
4112 STATUS_DISASSOCIATING)))
4113 queue_work(priv->workqueue, &priv->associate);
4114 else if (priv->status & STATUS_ROAMING) {
4115 /* If a scan completed and we are in roam mode, then
4116 * the scan that completed was the one requested as a
4117 * result of entering roam... so, schedule the
4118 * roam work */
4119 queue_work(priv->workqueue, &priv->roam);
4120 } else if (priv->status & STATUS_SCAN_PENDING)
4121 queue_work(priv->workqueue,
4122 &priv->request_scan);
James Ketrenosa613bff2005-08-24 21:43:11 -05004123 else if (priv->config & CFG_BACKGROUND_SCAN
4124 && priv->status & STATUS_ASSOCIATED)
4125 queue_delayed_work(priv->workqueue,
4126 &priv->request_scan, HZ);
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004127
4128 priv->ieee->scans++;
4129 break;
4130 }
4131
4132 case HOST_NOTIFICATION_STATUS_FRAG_LENGTH:{
4133 struct notif_frag_length *x = &notif->u.frag_len;
4134
James Ketrenosa613bff2005-08-24 21:43:11 -05004135 if (notif->size == sizeof(*x))
4136 IPW_ERROR("Frag length: %d\n",
4137 le16_to_cpu(x->frag_length));
4138 else
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004139 IPW_ERROR("Frag length of wrong size %d "
4140 "(should be %zd)\n",
4141 notif->size, sizeof(*x));
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004142 break;
4143 }
4144
4145 case HOST_NOTIFICATION_STATUS_LINK_DETERIORATION:{
4146 struct notif_link_deterioration *x =
4147 &notif->u.link_deterioration;
4148 if (notif->size == sizeof(*x)) {
4149 IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE,
4150 "link deterioration: '%s' " MAC_FMT
4151 " \n", escape_essid(priv->essid,
4152 priv->essid_len),
4153 MAC_ARG(priv->bssid));
4154 memcpy(&priv->last_link_deterioration, x,
4155 sizeof(*x));
4156 } else {
4157 IPW_ERROR("Link Deterioration of wrong size %d "
4158 "(should be %zd)\n",
4159 notif->size, sizeof(*x));
4160 }
4161 break;
4162 }
4163
4164 case HOST_NOTIFICATION_DINO_CONFIG_RESPONSE:{
4165 IPW_ERROR("Dino config\n");
4166 if (priv->hcmd
James Ketrenosa613bff2005-08-24 21:43:11 -05004167 && priv->hcmd->cmd != HOST_CMD_DINO_CONFIG)
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004168 IPW_ERROR("Unexpected DINO_CONFIG_RESPONSE\n");
James Ketrenosa613bff2005-08-24 21:43:11 -05004169
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004170 break;
4171 }
4172
4173 case HOST_NOTIFICATION_STATUS_BEACON_STATE:{
4174 struct notif_beacon_state *x = &notif->u.beacon_state;
4175 if (notif->size != sizeof(*x)) {
4176 IPW_ERROR
4177 ("Beacon state of wrong size %d (should "
4178 "be %zd)\n", notif->size, sizeof(*x));
4179 break;
Jeff Garzikbf794512005-07-31 13:07:26 -04004180 }
James Ketrenos43f66a62005-03-25 12:31:53 -06004181
James Ketrenosa613bff2005-08-24 21:43:11 -05004182 if (le32_to_cpu(x->state) ==
4183 HOST_NOTIFICATION_STATUS_BEACON_MISSING)
4184 ipw_handle_missed_beacon(priv,
4185 le32_to_cpu(x->
4186 number));
Jeff Garzikbf794512005-07-31 13:07:26 -04004187
James Ketrenos43f66a62005-03-25 12:31:53 -06004188 break;
4189 }
Jeff Garzikbf794512005-07-31 13:07:26 -04004190
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004191 case HOST_NOTIFICATION_STATUS_TGI_TX_KEY:{
4192 struct notif_tgi_tx_key *x = &notif->u.tgi_tx_key;
4193 if (notif->size == sizeof(*x)) {
4194 IPW_ERROR("TGi Tx Key: state 0x%02x sec type "
4195 "0x%02x station %d\n",
4196 x->key_state, x->security_type,
4197 x->station_index);
4198 break;
James Ketrenos43f66a62005-03-25 12:31:53 -06004199 }
4200
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004201 IPW_ERROR
4202 ("TGi Tx Key of wrong size %d (should be %zd)\n",
4203 notif->size, sizeof(*x));
4204 break;
4205 }
4206
4207 case HOST_NOTIFICATION_CALIB_KEEP_RESULTS:{
4208 struct notif_calibration *x = &notif->u.calibration;
4209
4210 if (notif->size == sizeof(*x)) {
4211 memcpy(&priv->calib, x, sizeof(*x));
4212 IPW_DEBUG_INFO("TODO: Calibration\n");
4213 break;
James Ketrenos43f66a62005-03-25 12:31:53 -06004214 }
4215
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004216 IPW_ERROR
4217 ("Calibration of wrong size %d (should be %zd)\n",
4218 notif->size, sizeof(*x));
James Ketrenos43f66a62005-03-25 12:31:53 -06004219 break;
Jeff Garzikbf794512005-07-31 13:07:26 -04004220 }
James Ketrenos43f66a62005-03-25 12:31:53 -06004221
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004222 case HOST_NOTIFICATION_NOISE_STATS:{
4223 if (notif->size == sizeof(u32)) {
4224 priv->last_noise =
James Ketrenosa613bff2005-08-24 21:43:11 -05004225 (u8) (le32_to_cpu(notif->u.noise.value) &
4226 0xff);
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004227 average_add(&priv->average_noise,
4228 priv->last_noise);
4229 break;
4230 }
James Ketrenos43f66a62005-03-25 12:31:53 -06004231
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004232 IPW_ERROR
4233 ("Noise stat is wrong size %d (should be %zd)\n",
4234 notif->size, sizeof(u32));
James Ketrenos43f66a62005-03-25 12:31:53 -06004235 break;
Jeff Garzikbf794512005-07-31 13:07:26 -04004236 }
4237
James Ketrenos43f66a62005-03-25 12:31:53 -06004238 default:
4239 IPW_ERROR("Unknown notification: "
4240 "subtype=%d,flags=0x%2x,size=%d\n",
4241 notif->subtype, notif->flags, notif->size);
4242 }
4243}
4244
4245/**
4246 * Destroys all DMA structures and initialise them again
Jeff Garzikbf794512005-07-31 13:07:26 -04004247 *
James Ketrenos43f66a62005-03-25 12:31:53 -06004248 * @param priv
4249 * @return error code
4250 */
4251static int ipw_queue_reset(struct ipw_priv *priv)
4252{
4253 int rc = 0;
4254 /** @todo customize queue sizes */
4255 int nTx = 64, nTxCmd = 8;
4256 ipw_tx_queue_free(priv);
4257 /* Tx CMD queue */
4258 rc = ipw_queue_tx_init(priv, &priv->txq_cmd, nTxCmd,
4259 CX2_TX_CMD_QUEUE_READ_INDEX,
4260 CX2_TX_CMD_QUEUE_WRITE_INDEX,
4261 CX2_TX_CMD_QUEUE_BD_BASE,
4262 CX2_TX_CMD_QUEUE_BD_SIZE);
4263 if (rc) {
4264 IPW_ERROR("Tx Cmd queue init failed\n");
4265 goto error;
4266 }
4267 /* Tx queue(s) */
4268 rc = ipw_queue_tx_init(priv, &priv->txq[0], nTx,
4269 CX2_TX_QUEUE_0_READ_INDEX,
4270 CX2_TX_QUEUE_0_WRITE_INDEX,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004271 CX2_TX_QUEUE_0_BD_BASE, CX2_TX_QUEUE_0_BD_SIZE);
James Ketrenos43f66a62005-03-25 12:31:53 -06004272 if (rc) {
4273 IPW_ERROR("Tx 0 queue init failed\n");
4274 goto error;
4275 }
4276 rc = ipw_queue_tx_init(priv, &priv->txq[1], nTx,
4277 CX2_TX_QUEUE_1_READ_INDEX,
4278 CX2_TX_QUEUE_1_WRITE_INDEX,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004279 CX2_TX_QUEUE_1_BD_BASE, CX2_TX_QUEUE_1_BD_SIZE);
James Ketrenos43f66a62005-03-25 12:31:53 -06004280 if (rc) {
4281 IPW_ERROR("Tx 1 queue init failed\n");
4282 goto error;
4283 }
4284 rc = ipw_queue_tx_init(priv, &priv->txq[2], nTx,
4285 CX2_TX_QUEUE_2_READ_INDEX,
4286 CX2_TX_QUEUE_2_WRITE_INDEX,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004287 CX2_TX_QUEUE_2_BD_BASE, CX2_TX_QUEUE_2_BD_SIZE);
James Ketrenos43f66a62005-03-25 12:31:53 -06004288 if (rc) {
4289 IPW_ERROR("Tx 2 queue init failed\n");
4290 goto error;
4291 }
4292 rc = ipw_queue_tx_init(priv, &priv->txq[3], nTx,
4293 CX2_TX_QUEUE_3_READ_INDEX,
4294 CX2_TX_QUEUE_3_WRITE_INDEX,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004295 CX2_TX_QUEUE_3_BD_BASE, CX2_TX_QUEUE_3_BD_SIZE);
James Ketrenos43f66a62005-03-25 12:31:53 -06004296 if (rc) {
4297 IPW_ERROR("Tx 3 queue init failed\n");
4298 goto error;
4299 }
4300 /* statistics */
4301 priv->rx_bufs_min = 0;
4302 priv->rx_pend_max = 0;
4303 return rc;
4304
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004305 error:
James Ketrenos43f66a62005-03-25 12:31:53 -06004306 ipw_tx_queue_free(priv);
4307 return rc;
4308}
4309
4310/**
4311 * Reclaim Tx queue entries no more used by NIC.
Jeff Garzikbf794512005-07-31 13:07:26 -04004312 *
James Ketrenos43f66a62005-03-25 12:31:53 -06004313 * When FW adwances 'R' index, all entries between old and
4314 * new 'R' index need to be reclaimed. As result, some free space
4315 * forms. If there is enough free space (> low mark), wake Tx queue.
Jeff Garzikbf794512005-07-31 13:07:26 -04004316 *
James Ketrenos43f66a62005-03-25 12:31:53 -06004317 * @note Need to protect against garbage in 'R' index
4318 * @param priv
4319 * @param txq
4320 * @param qindex
4321 * @return Number of used entries remains in the queue
4322 */
Jeff Garzikbf794512005-07-31 13:07:26 -04004323static int ipw_queue_tx_reclaim(struct ipw_priv *priv,
James Ketrenos43f66a62005-03-25 12:31:53 -06004324 struct clx2_tx_queue *txq, int qindex)
4325{
4326 u32 hw_tail;
4327 int used;
4328 struct clx2_queue *q = &txq->q;
4329
4330 hw_tail = ipw_read32(priv, q->reg_r);
4331 if (hw_tail >= q->n_bd) {
4332 IPW_ERROR
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004333 ("Read index for DMA queue (%d) is out of range [0-%d)\n",
4334 hw_tail, q->n_bd);
James Ketrenos43f66a62005-03-25 12:31:53 -06004335 goto done;
4336 }
4337 for (; q->last_used != hw_tail;
4338 q->last_used = ipw_queue_inc_wrap(q->last_used, q->n_bd)) {
4339 ipw_queue_tx_free_tfd(priv, txq);
4340 priv->tx_packets++;
4341 }
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004342 done:
James Ketrenosa613bff2005-08-24 21:43:11 -05004343 if (ipw_queue_space(q) > q->low_mark && qindex >= 0)
James Ketrenos43f66a62005-03-25 12:31:53 -06004344 __maybe_wake_tx(priv);
James Ketrenos43f66a62005-03-25 12:31:53 -06004345 used = q->first_empty - q->last_used;
4346 if (used < 0)
4347 used += q->n_bd;
4348
4349 return used;
4350}
4351
4352static int ipw_queue_tx_hcmd(struct ipw_priv *priv, int hcmd, void *buf,
4353 int len, int sync)
4354{
4355 struct clx2_tx_queue *txq = &priv->txq_cmd;
4356 struct clx2_queue *q = &txq->q;
4357 struct tfd_frame *tfd;
4358
4359 if (ipw_queue_space(q) < (sync ? 1 : 2)) {
4360 IPW_ERROR("No space for Tx\n");
4361 return -EBUSY;
4362 }
4363
4364 tfd = &txq->bd[q->first_empty];
4365 txq->txb[q->first_empty] = NULL;
4366
4367 memset(tfd, 0, sizeof(*tfd));
4368 tfd->control_flags.message_type = TX_HOST_COMMAND_TYPE;
4369 tfd->control_flags.control_bits = TFD_NEED_IRQ_MASK;
4370 priv->hcmd_seq++;
4371 tfd->u.cmd.index = hcmd;
4372 tfd->u.cmd.length = len;
4373 memcpy(tfd->u.cmd.payload, buf, len);
4374 q->first_empty = ipw_queue_inc_wrap(q->first_empty, q->n_bd);
4375 ipw_write32(priv, q->reg_w, q->first_empty);
4376 _ipw_read32(priv, 0x90);
4377
4378 return 0;
4379}
4380
Jeff Garzikbf794512005-07-31 13:07:26 -04004381/*
James Ketrenos43f66a62005-03-25 12:31:53 -06004382 * Rx theory of operation
4383 *
4384 * The host allocates 32 DMA target addresses and passes the host address
4385 * to the firmware at register CX2_RFDS_TABLE_LOWER + N * RFD_SIZE where N is
4386 * 0 to 31
4387 *
4388 * Rx Queue Indexes
4389 * The host/firmware share two index registers for managing the Rx buffers.
4390 *
Jeff Garzikbf794512005-07-31 13:07:26 -04004391 * The READ index maps to the first position that the firmware may be writing
4392 * to -- the driver can read up to (but not including) this position and get
4393 * good data.
James Ketrenos43f66a62005-03-25 12:31:53 -06004394 * The READ index is managed by the firmware once the card is enabled.
4395 *
4396 * The WRITE index maps to the last position the driver has read from -- the
4397 * position preceding WRITE is the last slot the firmware can place a packet.
4398 *
4399 * The queue is empty (no good data) if WRITE = READ - 1, and is full if
Jeff Garzikbf794512005-07-31 13:07:26 -04004400 * WRITE = READ.
James Ketrenos43f66a62005-03-25 12:31:53 -06004401 *
Jeff Garzikbf794512005-07-31 13:07:26 -04004402 * During initialization the host sets up the READ queue position to the first
James Ketrenos43f66a62005-03-25 12:31:53 -06004403 * INDEX position, and WRITE to the last (READ - 1 wrapped)
4404 *
4405 * When the firmware places a packet in a buffer it will advance the READ index
4406 * and fire the RX interrupt. The driver can then query the READ index and
4407 * process as many packets as possible, moving the WRITE index forward as it
4408 * resets the Rx queue buffers with new memory.
Jeff Garzikbf794512005-07-31 13:07:26 -04004409 *
James Ketrenos43f66a62005-03-25 12:31:53 -06004410 * The management in the driver is as follows:
Jeff Garzikbf794512005-07-31 13:07:26 -04004411 * + A list of pre-allocated SKBs is stored in ipw->rxq->rx_free. When
James Ketrenos43f66a62005-03-25 12:31:53 -06004412 * ipw->rxq->free_count drops to or below RX_LOW_WATERMARK, work is scheduled
Jeff Garzikbf794512005-07-31 13:07:26 -04004413 * to replensish the ipw->rxq->rx_free.
James Ketrenos43f66a62005-03-25 12:31:53 -06004414 * + In ipw_rx_queue_replenish (scheduled) if 'processed' != 'read' then the
4415 * ipw->rxq is replenished and the READ INDEX is updated (updating the
4416 * 'processed' and 'read' driver indexes as well)
4417 * + A received packet is processed and handed to the kernel network stack,
4418 * detached from the ipw->rxq. The driver 'processed' index is updated.
4419 * + The Host/Firmware ipw->rxq is replenished at tasklet time from the rx_free
Jeff Garzikbf794512005-07-31 13:07:26 -04004420 * list. If there are no allocated buffers in ipw->rxq->rx_free, the READ
4421 * INDEX is not incremented and ipw->status(RX_STALLED) is set. If there
James Ketrenos43f66a62005-03-25 12:31:53 -06004422 * were enough free buffers and RX_STALLED is set it is cleared.
4423 *
4424 *
4425 * Driver sequence:
4426 *
Jeff Garzikbf794512005-07-31 13:07:26 -04004427 * ipw_rx_queue_alloc() Allocates rx_free
James Ketrenos43f66a62005-03-25 12:31:53 -06004428 * ipw_rx_queue_replenish() Replenishes rx_free list from rx_used, and calls
4429 * ipw_rx_queue_restock
4430 * ipw_rx_queue_restock() Moves available buffers from rx_free into Rx
4431 * queue, updates firmware pointers, and updates
4432 * the WRITE index. If insufficient rx_free buffers
4433 * are available, schedules ipw_rx_queue_replenish
4434 *
4435 * -- enable interrupts --
4436 * ISR - ipw_rx() Detach ipw_rx_mem_buffers from pool up to the
Jeff Garzikbf794512005-07-31 13:07:26 -04004437 * READ INDEX, detaching the SKB from the pool.
James Ketrenos43f66a62005-03-25 12:31:53 -06004438 * Moves the packet buffer from queue to rx_used.
4439 * Calls ipw_rx_queue_restock to refill any empty
4440 * slots.
4441 * ...
4442 *
4443 */
4444
Jeff Garzikbf794512005-07-31 13:07:26 -04004445/*
James Ketrenos43f66a62005-03-25 12:31:53 -06004446 * If there are slots in the RX queue that need to be restocked,
4447 * and we have free pre-allocated buffers, fill the ranks as much
4448 * as we can pulling from rx_free.
4449 *
4450 * This moves the 'write' index forward to catch up with 'processed', and
4451 * also updates the memory address in the firmware to reference the new
4452 * target buffer.
4453 */
4454static void ipw_rx_queue_restock(struct ipw_priv *priv)
4455{
4456 struct ipw_rx_queue *rxq = priv->rxq;
4457 struct list_head *element;
4458 struct ipw_rx_mem_buffer *rxb;
4459 unsigned long flags;
4460 int write;
4461
4462 spin_lock_irqsave(&rxq->lock, flags);
4463 write = rxq->write;
4464 while ((rxq->write != rxq->processed) && (rxq->free_count)) {
4465 element = rxq->rx_free.next;
4466 rxb = list_entry(element, struct ipw_rx_mem_buffer, list);
4467 list_del(element);
4468
4469 ipw_write32(priv, CX2_RFDS_TABLE_LOWER + rxq->write * RFD_SIZE,
4470 rxb->dma_addr);
4471 rxq->queue[rxq->write] = rxb;
4472 rxq->write = (rxq->write + 1) % RX_QUEUE_SIZE;
4473 rxq->free_count--;
4474 }
4475 spin_unlock_irqrestore(&rxq->lock, flags);
4476
Jeff Garzikbf794512005-07-31 13:07:26 -04004477 /* If the pre-allocated buffer pool is dropping low, schedule to
James Ketrenos43f66a62005-03-25 12:31:53 -06004478 * refill it */
4479 if (rxq->free_count <= RX_LOW_WATERMARK)
4480 queue_work(priv->workqueue, &priv->rx_replenish);
4481
4482 /* If we've added more space for the firmware to place data, tell it */
Jeff Garzikbf794512005-07-31 13:07:26 -04004483 if (write != rxq->write)
James Ketrenos43f66a62005-03-25 12:31:53 -06004484 ipw_write32(priv, CX2_RX_WRITE_INDEX, rxq->write);
4485}
4486
4487/*
4488 * Move all used packet from rx_used to rx_free, allocating a new SKB for each.
Jeff Garzikbf794512005-07-31 13:07:26 -04004489 * Also restock the Rx queue via ipw_rx_queue_restock.
4490 *
James Ketrenos43f66a62005-03-25 12:31:53 -06004491 * This is called as a scheduled work item (except for during intialization)
4492 */
4493static void ipw_rx_queue_replenish(void *data)
4494{
4495 struct ipw_priv *priv = data;
4496 struct ipw_rx_queue *rxq = priv->rxq;
4497 struct list_head *element;
4498 struct ipw_rx_mem_buffer *rxb;
4499 unsigned long flags;
4500
4501 spin_lock_irqsave(&rxq->lock, flags);
4502 while (!list_empty(&rxq->rx_used)) {
4503 element = rxq->rx_used.next;
4504 rxb = list_entry(element, struct ipw_rx_mem_buffer, list);
4505 rxb->skb = alloc_skb(CX2_RX_BUF_SIZE, GFP_ATOMIC);
4506 if (!rxb->skb) {
4507 printk(KERN_CRIT "%s: Can not allocate SKB buffers.\n",
4508 priv->net_dev->name);
4509 /* We don't reschedule replenish work here -- we will
4510 * call the restock method and if it still needs
4511 * more buffers it will schedule replenish */
4512 break;
4513 }
4514 list_del(element);
Jeff Garzikbf794512005-07-31 13:07:26 -04004515
James Ketrenos43f66a62005-03-25 12:31:53 -06004516 rxb->rxb = (struct ipw_rx_buffer *)rxb->skb->data;
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004517 rxb->dma_addr =
4518 pci_map_single(priv->pci_dev, rxb->skb->data,
4519 CX2_RX_BUF_SIZE, PCI_DMA_FROMDEVICE);
Jeff Garzikbf794512005-07-31 13:07:26 -04004520
James Ketrenos43f66a62005-03-25 12:31:53 -06004521 list_add_tail(&rxb->list, &rxq->rx_free);
4522 rxq->free_count++;
4523 }
4524 spin_unlock_irqrestore(&rxq->lock, flags);
4525
4526 ipw_rx_queue_restock(priv);
4527}
4528
James Ketrenosc848d0a2005-08-24 21:56:24 -05004529static void ipw_bg_rx_queue_replenish(void *data)
4530{
4531 struct ipw_priv *priv = data;
4532 down(&priv->sem);
4533 ipw_rx_queue_replenish(data);
4534 up(&priv->sem);
4535}
4536
James Ketrenos43f66a62005-03-25 12:31:53 -06004537/* Assumes that the skb field of the buffers in 'pool' is kept accurate.
4538 * If an SKB has been detached, the POOL needs to have it's SKB set to NULL
Jeff Garzikbf794512005-07-31 13:07:26 -04004539 * This free routine walks the list of POOL entries and if SKB is set to
James Ketrenos43f66a62005-03-25 12:31:53 -06004540 * non NULL it is unmapped and freed
4541 */
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004542static void ipw_rx_queue_free(struct ipw_priv *priv, struct ipw_rx_queue *rxq)
James Ketrenos43f66a62005-03-25 12:31:53 -06004543{
4544 int i;
4545
4546 if (!rxq)
4547 return;
Jeff Garzikbf794512005-07-31 13:07:26 -04004548
James Ketrenos43f66a62005-03-25 12:31:53 -06004549 for (i = 0; i < RX_QUEUE_SIZE + RX_FREE_BUFFERS; i++) {
4550 if (rxq->pool[i].skb != NULL) {
4551 pci_unmap_single(priv->pci_dev, rxq->pool[i].dma_addr,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004552 CX2_RX_BUF_SIZE, PCI_DMA_FROMDEVICE);
James Ketrenos43f66a62005-03-25 12:31:53 -06004553 dev_kfree_skb(rxq->pool[i].skb);
4554 }
4555 }
4556
4557 kfree(rxq);
4558}
4559
4560static struct ipw_rx_queue *ipw_rx_queue_alloc(struct ipw_priv *priv)
4561{
4562 struct ipw_rx_queue *rxq;
4563 int i;
4564
4565 rxq = (struct ipw_rx_queue *)kmalloc(sizeof(*rxq), GFP_KERNEL);
Panagiotis Issarisad18b0e2005-09-05 04:14:10 +02004566 if (unlikely(!rxq)) {
4567 IPW_ERROR("memory allocation failed\n");
4568 return NULL;
4569 }
James Ketrenos43f66a62005-03-25 12:31:53 -06004570 memset(rxq, 0, sizeof(*rxq));
4571 spin_lock_init(&rxq->lock);
4572 INIT_LIST_HEAD(&rxq->rx_free);
4573 INIT_LIST_HEAD(&rxq->rx_used);
4574
4575 /* Fill the rx_used queue with _all_ of the Rx buffers */
Jeff Garzikbf794512005-07-31 13:07:26 -04004576 for (i = 0; i < RX_FREE_BUFFERS + RX_QUEUE_SIZE; i++)
James Ketrenos43f66a62005-03-25 12:31:53 -06004577 list_add_tail(&rxq->pool[i].list, &rxq->rx_used);
4578
4579 /* Set us so that we have processed and used all buffers, but have
4580 * not restocked the Rx queue with fresh buffers */
4581 rxq->read = rxq->write = 0;
4582 rxq->processed = RX_QUEUE_SIZE - 1;
4583 rxq->free_count = 0;
4584
4585 return rxq;
4586}
4587
4588static int ipw_is_rate_in_mask(struct ipw_priv *priv, int ieee_mode, u8 rate)
4589{
4590 rate &= ~IEEE80211_BASIC_RATE_MASK;
4591 if (ieee_mode == IEEE_A) {
4592 switch (rate) {
Jeff Garzikbf794512005-07-31 13:07:26 -04004593 case IEEE80211_OFDM_RATE_6MB:
4594 return priv->rates_mask & IEEE80211_OFDM_RATE_6MB_MASK ?
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004595 1 : 0;
Jeff Garzikbf794512005-07-31 13:07:26 -04004596 case IEEE80211_OFDM_RATE_9MB:
4597 return priv->rates_mask & IEEE80211_OFDM_RATE_9MB_MASK ?
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004598 1 : 0;
Jeff Garzikbf794512005-07-31 13:07:26 -04004599 case IEEE80211_OFDM_RATE_12MB:
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004600 return priv->
4601 rates_mask & IEEE80211_OFDM_RATE_12MB_MASK ? 1 : 0;
Jeff Garzikbf794512005-07-31 13:07:26 -04004602 case IEEE80211_OFDM_RATE_18MB:
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004603 return priv->
4604 rates_mask & IEEE80211_OFDM_RATE_18MB_MASK ? 1 : 0;
Jeff Garzikbf794512005-07-31 13:07:26 -04004605 case IEEE80211_OFDM_RATE_24MB:
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004606 return priv->
4607 rates_mask & IEEE80211_OFDM_RATE_24MB_MASK ? 1 : 0;
Jeff Garzikbf794512005-07-31 13:07:26 -04004608 case IEEE80211_OFDM_RATE_36MB:
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004609 return priv->
4610 rates_mask & IEEE80211_OFDM_RATE_36MB_MASK ? 1 : 0;
Jeff Garzikbf794512005-07-31 13:07:26 -04004611 case IEEE80211_OFDM_RATE_48MB:
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004612 return priv->
4613 rates_mask & IEEE80211_OFDM_RATE_48MB_MASK ? 1 : 0;
Jeff Garzikbf794512005-07-31 13:07:26 -04004614 case IEEE80211_OFDM_RATE_54MB:
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004615 return priv->
4616 rates_mask & IEEE80211_OFDM_RATE_54MB_MASK ? 1 : 0;
James Ketrenos43f66a62005-03-25 12:31:53 -06004617 default:
4618 return 0;
4619 }
4620 }
Jeff Garzikbf794512005-07-31 13:07:26 -04004621
James Ketrenos43f66a62005-03-25 12:31:53 -06004622 /* B and G mixed */
4623 switch (rate) {
Jeff Garzikbf794512005-07-31 13:07:26 -04004624 case IEEE80211_CCK_RATE_1MB:
James Ketrenos43f66a62005-03-25 12:31:53 -06004625 return priv->rates_mask & IEEE80211_CCK_RATE_1MB_MASK ? 1 : 0;
Jeff Garzikbf794512005-07-31 13:07:26 -04004626 case IEEE80211_CCK_RATE_2MB:
James Ketrenos43f66a62005-03-25 12:31:53 -06004627 return priv->rates_mask & IEEE80211_CCK_RATE_2MB_MASK ? 1 : 0;
Jeff Garzikbf794512005-07-31 13:07:26 -04004628 case IEEE80211_CCK_RATE_5MB:
James Ketrenos43f66a62005-03-25 12:31:53 -06004629 return priv->rates_mask & IEEE80211_CCK_RATE_5MB_MASK ? 1 : 0;
Jeff Garzikbf794512005-07-31 13:07:26 -04004630 case IEEE80211_CCK_RATE_11MB:
James Ketrenos43f66a62005-03-25 12:31:53 -06004631 return priv->rates_mask & IEEE80211_CCK_RATE_11MB_MASK ? 1 : 0;
4632 }
4633
4634 /* If we are limited to B modulations, bail at this point */
4635 if (ieee_mode == IEEE_B)
4636 return 0;
4637
4638 /* G */
4639 switch (rate) {
Jeff Garzikbf794512005-07-31 13:07:26 -04004640 case IEEE80211_OFDM_RATE_6MB:
James Ketrenos43f66a62005-03-25 12:31:53 -06004641 return priv->rates_mask & IEEE80211_OFDM_RATE_6MB_MASK ? 1 : 0;
Jeff Garzikbf794512005-07-31 13:07:26 -04004642 case IEEE80211_OFDM_RATE_9MB:
James Ketrenos43f66a62005-03-25 12:31:53 -06004643 return priv->rates_mask & IEEE80211_OFDM_RATE_9MB_MASK ? 1 : 0;
Jeff Garzikbf794512005-07-31 13:07:26 -04004644 case IEEE80211_OFDM_RATE_12MB:
James Ketrenos43f66a62005-03-25 12:31:53 -06004645 return priv->rates_mask & IEEE80211_OFDM_RATE_12MB_MASK ? 1 : 0;
Jeff Garzikbf794512005-07-31 13:07:26 -04004646 case IEEE80211_OFDM_RATE_18MB:
James Ketrenos43f66a62005-03-25 12:31:53 -06004647 return priv->rates_mask & IEEE80211_OFDM_RATE_18MB_MASK ? 1 : 0;
Jeff Garzikbf794512005-07-31 13:07:26 -04004648 case IEEE80211_OFDM_RATE_24MB:
James Ketrenos43f66a62005-03-25 12:31:53 -06004649 return priv->rates_mask & IEEE80211_OFDM_RATE_24MB_MASK ? 1 : 0;
Jeff Garzikbf794512005-07-31 13:07:26 -04004650 case IEEE80211_OFDM_RATE_36MB:
James Ketrenos43f66a62005-03-25 12:31:53 -06004651 return priv->rates_mask & IEEE80211_OFDM_RATE_36MB_MASK ? 1 : 0;
Jeff Garzikbf794512005-07-31 13:07:26 -04004652 case IEEE80211_OFDM_RATE_48MB:
James Ketrenos43f66a62005-03-25 12:31:53 -06004653 return priv->rates_mask & IEEE80211_OFDM_RATE_48MB_MASK ? 1 : 0;
Jeff Garzikbf794512005-07-31 13:07:26 -04004654 case IEEE80211_OFDM_RATE_54MB:
James Ketrenos43f66a62005-03-25 12:31:53 -06004655 return priv->rates_mask & IEEE80211_OFDM_RATE_54MB_MASK ? 1 : 0;
4656 }
4657
4658 return 0;
4659}
4660
Jeff Garzikbf794512005-07-31 13:07:26 -04004661static int ipw_compatible_rates(struct ipw_priv *priv,
James Ketrenos43f66a62005-03-25 12:31:53 -06004662 const struct ieee80211_network *network,
4663 struct ipw_supported_rates *rates)
4664{
4665 int num_rates, i;
4666
4667 memset(rates, 0, sizeof(*rates));
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004668 num_rates = min(network->rates_len, (u8) IPW_MAX_RATES);
James Ketrenos43f66a62005-03-25 12:31:53 -06004669 rates->num_rates = 0;
4670 for (i = 0; i < num_rates; i++) {
James Ketrenosa613bff2005-08-24 21:43:11 -05004671 if (!ipw_is_rate_in_mask(priv, network->mode,
4672 network->rates[i])) {
4673
James Ketrenosea2b26e2005-08-24 21:25:16 -05004674 if (network->rates[i] & IEEE80211_BASIC_RATE_MASK) {
James Ketrenosa613bff2005-08-24 21:43:11 -05004675 IPW_DEBUG_SCAN("Adding masked mandatory "
4676 "rate %02X\n",
4677 network->rates[i]);
4678 rates->supported_rates[rates->num_rates++] =
4679 network->rates[i];
4680 continue;
James Ketrenosea2b26e2005-08-24 21:25:16 -05004681 }
4682
James Ketrenos43f66a62005-03-25 12:31:53 -06004683 IPW_DEBUG_SCAN("Rate %02X masked : 0x%08X\n",
4684 network->rates[i], priv->rates_mask);
4685 continue;
4686 }
Jeff Garzikbf794512005-07-31 13:07:26 -04004687
James Ketrenos43f66a62005-03-25 12:31:53 -06004688 rates->supported_rates[rates->num_rates++] = network->rates[i];
4689 }
4690
James Ketrenosa613bff2005-08-24 21:43:11 -05004691 num_rates = min(network->rates_ex_len,
4692 (u8) (IPW_MAX_RATES - num_rates));
James Ketrenos43f66a62005-03-25 12:31:53 -06004693 for (i = 0; i < num_rates; i++) {
James Ketrenosa613bff2005-08-24 21:43:11 -05004694 if (!ipw_is_rate_in_mask(priv, network->mode,
4695 network->rates_ex[i])) {
James Ketrenosea2b26e2005-08-24 21:25:16 -05004696 if (network->rates_ex[i] & IEEE80211_BASIC_RATE_MASK) {
James Ketrenosa613bff2005-08-24 21:43:11 -05004697 IPW_DEBUG_SCAN("Adding masked mandatory "
4698 "rate %02X\n",
4699 network->rates_ex[i]);
4700 rates->supported_rates[rates->num_rates++] =
4701 network->rates[i];
4702 continue;
James Ketrenosea2b26e2005-08-24 21:25:16 -05004703 }
4704
James Ketrenos43f66a62005-03-25 12:31:53 -06004705 IPW_DEBUG_SCAN("Rate %02X masked : 0x%08X\n",
4706 network->rates_ex[i], priv->rates_mask);
4707 continue;
4708 }
Jeff Garzikbf794512005-07-31 13:07:26 -04004709
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004710 rates->supported_rates[rates->num_rates++] =
4711 network->rates_ex[i];
James Ketrenos43f66a62005-03-25 12:31:53 -06004712 }
4713
James Ketrenosea2b26e2005-08-24 21:25:16 -05004714 return 1;
James Ketrenos43f66a62005-03-25 12:31:53 -06004715}
4716
4717static inline void ipw_copy_rates(struct ipw_supported_rates *dest,
4718 const struct ipw_supported_rates *src)
4719{
4720 u8 i;
4721 for (i = 0; i < src->num_rates; i++)
4722 dest->supported_rates[i] = src->supported_rates[i];
4723 dest->num_rates = src->num_rates;
4724}
4725
4726/* TODO: Look at sniffed packets in the air to determine if the basic rate
4727 * mask should ever be used -- right now all callers to add the scan rates are
4728 * set with the modulation = CCK, so BASIC_RATE_MASK is never set... */
4729static void ipw_add_cck_scan_rates(struct ipw_supported_rates *rates,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004730 u8 modulation, u32 rate_mask)
James Ketrenos43f66a62005-03-25 12:31:53 -06004731{
Jeff Garzikbf794512005-07-31 13:07:26 -04004732 u8 basic_mask = (IEEE80211_OFDM_MODULATION == modulation) ?
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004733 IEEE80211_BASIC_RATE_MASK : 0;
Jeff Garzikbf794512005-07-31 13:07:26 -04004734
James Ketrenos43f66a62005-03-25 12:31:53 -06004735 if (rate_mask & IEEE80211_CCK_RATE_1MB_MASK)
Jeff Garzikbf794512005-07-31 13:07:26 -04004736 rates->supported_rates[rates->num_rates++] =
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004737 IEEE80211_BASIC_RATE_MASK | IEEE80211_CCK_RATE_1MB;
James Ketrenos43f66a62005-03-25 12:31:53 -06004738
4739 if (rate_mask & IEEE80211_CCK_RATE_2MB_MASK)
Jeff Garzikbf794512005-07-31 13:07:26 -04004740 rates->supported_rates[rates->num_rates++] =
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004741 IEEE80211_BASIC_RATE_MASK | IEEE80211_CCK_RATE_2MB;
James Ketrenos43f66a62005-03-25 12:31:53 -06004742
4743 if (rate_mask & IEEE80211_CCK_RATE_5MB_MASK)
Jeff Garzikbf794512005-07-31 13:07:26 -04004744 rates->supported_rates[rates->num_rates++] = basic_mask |
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004745 IEEE80211_CCK_RATE_5MB;
James Ketrenos43f66a62005-03-25 12:31:53 -06004746
4747 if (rate_mask & IEEE80211_CCK_RATE_11MB_MASK)
Jeff Garzikbf794512005-07-31 13:07:26 -04004748 rates->supported_rates[rates->num_rates++] = basic_mask |
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004749 IEEE80211_CCK_RATE_11MB;
James Ketrenos43f66a62005-03-25 12:31:53 -06004750}
4751
4752static void ipw_add_ofdm_scan_rates(struct ipw_supported_rates *rates,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004753 u8 modulation, u32 rate_mask)
James Ketrenos43f66a62005-03-25 12:31:53 -06004754{
Jeff Garzikbf794512005-07-31 13:07:26 -04004755 u8 basic_mask = (IEEE80211_OFDM_MODULATION == modulation) ?
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004756 IEEE80211_BASIC_RATE_MASK : 0;
James Ketrenos43f66a62005-03-25 12:31:53 -06004757
4758 if (rate_mask & IEEE80211_OFDM_RATE_6MB_MASK)
Jeff Garzikbf794512005-07-31 13:07:26 -04004759 rates->supported_rates[rates->num_rates++] = basic_mask |
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004760 IEEE80211_OFDM_RATE_6MB;
James Ketrenos43f66a62005-03-25 12:31:53 -06004761
4762 if (rate_mask & IEEE80211_OFDM_RATE_9MB_MASK)
Jeff Garzikbf794512005-07-31 13:07:26 -04004763 rates->supported_rates[rates->num_rates++] =
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004764 IEEE80211_OFDM_RATE_9MB;
James Ketrenos43f66a62005-03-25 12:31:53 -06004765
4766 if (rate_mask & IEEE80211_OFDM_RATE_12MB_MASK)
Jeff Garzikbf794512005-07-31 13:07:26 -04004767 rates->supported_rates[rates->num_rates++] = basic_mask |
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004768 IEEE80211_OFDM_RATE_12MB;
James Ketrenos43f66a62005-03-25 12:31:53 -06004769
4770 if (rate_mask & IEEE80211_OFDM_RATE_18MB_MASK)
Jeff Garzikbf794512005-07-31 13:07:26 -04004771 rates->supported_rates[rates->num_rates++] =
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004772 IEEE80211_OFDM_RATE_18MB;
James Ketrenos43f66a62005-03-25 12:31:53 -06004773
4774 if (rate_mask & IEEE80211_OFDM_RATE_24MB_MASK)
Jeff Garzikbf794512005-07-31 13:07:26 -04004775 rates->supported_rates[rates->num_rates++] = basic_mask |
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004776 IEEE80211_OFDM_RATE_24MB;
James Ketrenos43f66a62005-03-25 12:31:53 -06004777
4778 if (rate_mask & IEEE80211_OFDM_RATE_36MB_MASK)
Jeff Garzikbf794512005-07-31 13:07:26 -04004779 rates->supported_rates[rates->num_rates++] =
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004780 IEEE80211_OFDM_RATE_36MB;
James Ketrenos43f66a62005-03-25 12:31:53 -06004781
4782 if (rate_mask & IEEE80211_OFDM_RATE_48MB_MASK)
Jeff Garzikbf794512005-07-31 13:07:26 -04004783 rates->supported_rates[rates->num_rates++] =
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004784 IEEE80211_OFDM_RATE_48MB;
James Ketrenos43f66a62005-03-25 12:31:53 -06004785
4786 if (rate_mask & IEEE80211_OFDM_RATE_54MB_MASK)
Jeff Garzikbf794512005-07-31 13:07:26 -04004787 rates->supported_rates[rates->num_rates++] =
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004788 IEEE80211_OFDM_RATE_54MB;
James Ketrenos43f66a62005-03-25 12:31:53 -06004789}
4790
4791struct ipw_network_match {
4792 struct ieee80211_network *network;
4793 struct ipw_supported_rates rates;
4794};
4795
James Ketrenosc848d0a2005-08-24 21:56:24 -05004796static int ipw_find_adhoc_network(struct ipw_priv *priv,
4797 struct ipw_network_match *match,
4798 struct ieee80211_network *network,
4799 int roaming)
4800{
4801 struct ipw_supported_rates rates;
4802
4803 /* Verify that this network's capability is compatible with the
4804 * current mode (AdHoc or Infrastructure) */
4805 if ((priv->ieee->iw_mode == IW_MODE_ADHOC &&
4806 !(network->capability & WLAN_CAPABILITY_IBSS))) {
4807 IPW_DEBUG_MERGE("Network '%s (" MAC_FMT ")' excluded due to "
4808 "capability mismatch.\n",
4809 escape_essid(network->ssid, network->ssid_len),
4810 MAC_ARG(network->bssid));
4811 return 0;
4812 }
4813
4814 /* If we do not have an ESSID for this AP, we can not associate with
4815 * it */
4816 if (network->flags & NETWORK_EMPTY_ESSID) {
4817 IPW_DEBUG_MERGE("Network '%s (" MAC_FMT ")' excluded "
4818 "because of hidden ESSID.\n",
4819 escape_essid(network->ssid, network->ssid_len),
4820 MAC_ARG(network->bssid));
4821 return 0;
4822 }
4823
4824 if (unlikely(roaming)) {
4825 /* If we are roaming, then ensure check if this is a valid
4826 * network to try and roam to */
4827 if ((network->ssid_len != match->network->ssid_len) ||
4828 memcmp(network->ssid, match->network->ssid,
4829 network->ssid_len)) {
4830 IPW_DEBUG_MERGE("Netowrk '%s (" MAC_FMT ")' excluded "
4831 "because of non-network ESSID.\n",
4832 escape_essid(network->ssid,
4833 network->ssid_len),
4834 MAC_ARG(network->bssid));
4835 return 0;
4836 }
4837 } else {
4838 /* If an ESSID has been configured then compare the broadcast
4839 * ESSID to ours */
4840 if ((priv->config & CFG_STATIC_ESSID) &&
4841 ((network->ssid_len != priv->essid_len) ||
4842 memcmp(network->ssid, priv->essid,
4843 min(network->ssid_len, priv->essid_len)))) {
4844 char escaped[IW_ESSID_MAX_SIZE * 2 + 1];
4845 strncpy(escaped,
4846 escape_essid(network->ssid, network->ssid_len),
4847 sizeof(escaped));
4848 IPW_DEBUG_MERGE("Network '%s (" MAC_FMT ")' excluded "
4849 "because of ESSID mismatch: '%s'.\n",
4850 escaped, MAC_ARG(network->bssid),
4851 escape_essid(priv->essid,
4852 priv->essid_len));
4853 return 0;
4854 }
4855 }
4856
4857 /* If the old network rate is better than this one, don't bother
4858 * testing everything else. */
4859
4860 if (network->time_stamp[0] < match->network->time_stamp[0]) {
4861 IPW_DEBUG_MERGE
4862 ("Network '%s excluded because newer than current network.\n",
4863 escape_essid(match->network->ssid,
4864 match->network->ssid_len));
4865 return 0;
4866 } else if (network->time_stamp[1] < match->network->time_stamp[1]) {
4867 IPW_DEBUG_MERGE
4868 ("Network '%s excluded because newer than current network.\n",
4869 escape_essid(match->network->ssid,
4870 match->network->ssid_len));
4871 return 0;
4872 }
4873
4874 /* Now go through and see if the requested network is valid... */
4875 if (priv->ieee->scan_age != 0 &&
4876 time_after(jiffies, network->last_scanned + priv->ieee->scan_age)) {
4877 IPW_DEBUG_MERGE("Network '%s (" MAC_FMT ")' excluded "
4878 "because of age: %lums.\n",
4879 escape_essid(network->ssid, network->ssid_len),
4880 MAC_ARG(network->bssid),
4881 (jiffies - network->last_scanned) / (HZ / 100));
4882 return 0;
4883 }
4884
4885 if ((priv->config & CFG_STATIC_CHANNEL) &&
4886 (network->channel != priv->channel)) {
4887 IPW_DEBUG_MERGE("Network '%s (" MAC_FMT ")' excluded "
4888 "because of channel mismatch: %d != %d.\n",
4889 escape_essid(network->ssid, network->ssid_len),
4890 MAC_ARG(network->bssid),
4891 network->channel, priv->channel);
4892 return 0;
4893 }
4894
4895 /* Verify privacy compatability */
4896 if (((priv->capability & CAP_PRIVACY_ON) ? 1 : 0) !=
4897 ((network->capability & WLAN_CAPABILITY_PRIVACY) ? 1 : 0)) {
4898 IPW_DEBUG_MERGE("Network '%s (" MAC_FMT ")' excluded "
4899 "because of privacy mismatch: %s != %s.\n",
4900 escape_essid(network->ssid, network->ssid_len),
4901 MAC_ARG(network->bssid),
4902 priv->capability & CAP_PRIVACY_ON ? "on" :
4903 "off",
4904 network->capability &
4905 WLAN_CAPABILITY_PRIVACY ? "on" : "off");
4906 return 0;
4907 }
4908
4909 if (!memcmp(network->bssid, priv->bssid, ETH_ALEN)) {
4910 IPW_DEBUG_MERGE("Network '%s (" MAC_FMT ")' excluded "
4911 "because of the same BSSID match: " MAC_FMT
4912 ".\n", escape_essid(network->ssid,
4913 network->ssid_len),
4914 MAC_ARG(network->bssid), MAC_ARG(priv->bssid));
4915 return 0;
4916 }
4917
4918 /* Filter out any incompatible freq / mode combinations */
4919 if (!ieee80211_is_valid_mode(priv->ieee, network->mode)) {
4920 IPW_DEBUG_MERGE("Network '%s (" MAC_FMT ")' excluded "
4921 "because of invalid frequency/mode "
4922 "combination.\n",
4923 escape_essid(network->ssid, network->ssid_len),
4924 MAC_ARG(network->bssid));
4925 return 0;
4926 }
4927
4928 /* Ensure that the rates supported by the driver are compatible with
4929 * this AP, including verification of basic rates (mandatory) */
4930 if (!ipw_compatible_rates(priv, network, &rates)) {
4931 IPW_DEBUG_MERGE("Network '%s (" MAC_FMT ")' excluded "
4932 "because configured rate mask excludes "
4933 "AP mandatory rate.\n",
4934 escape_essid(network->ssid, network->ssid_len),
4935 MAC_ARG(network->bssid));
4936 return 0;
4937 }
4938
4939 if (rates.num_rates == 0) {
4940 IPW_DEBUG_MERGE("Network '%s (" MAC_FMT ")' excluded "
4941 "because of no compatible rates.\n",
4942 escape_essid(network->ssid, network->ssid_len),
4943 MAC_ARG(network->bssid));
4944 return 0;
4945 }
4946
4947 /* TODO: Perform any further minimal comparititive tests. We do not
4948 * want to put too much policy logic here; intelligent scan selection
4949 * should occur within a generic IEEE 802.11 user space tool. */
4950
4951 /* Set up 'new' AP to this network */
4952 ipw_copy_rates(&match->rates, &rates);
4953 match->network = network;
4954 IPW_DEBUG_MERGE("Network '%s (" MAC_FMT ")' is a viable match.\n",
4955 escape_essid(network->ssid, network->ssid_len),
4956 MAC_ARG(network->bssid));
4957
4958 return 1;
4959}
4960
4961static void ipw_merge_adhoc_network(void *data)
4962{
4963 struct ipw_priv *priv = data;
4964 struct ieee80211_network *network = NULL;
4965 struct ipw_network_match match = {
4966 .network = priv->assoc_network
4967 };
4968
4969 if ((priv->status & STATUS_ASSOCIATED)
4970 && (priv->ieee->iw_mode == IW_MODE_ADHOC)) {
4971 /* First pass through ROAM process -- look for a better
4972 * network */
4973 unsigned long flags;
4974
4975 spin_lock_irqsave(&priv->ieee->lock, flags);
4976 list_for_each_entry(network, &priv->ieee->network_list, list) {
4977 if (network != priv->assoc_network)
4978 ipw_find_adhoc_network(priv, &match, network,
4979 1);
4980 }
4981 spin_unlock_irqrestore(&priv->ieee->lock, flags);
4982
4983 if (match.network == priv->assoc_network) {
4984 IPW_DEBUG_MERGE("No better ADHOC in this network to "
4985 "merge to.\n");
4986 return;
4987 }
4988
4989 down(&priv->sem);
4990 if ((priv->ieee->iw_mode == IW_MODE_ADHOC)) {
4991 IPW_DEBUG_MERGE("remove network %s\n",
4992 escape_essid(priv->essid,
4993 priv->essid_len));
4994 ipw_remove_current_network(priv);
4995 }
4996
4997 ipw_disassociate(priv);
4998 priv->assoc_network = match.network;
4999 up(&priv->sem);
5000 return;
5001 }
5002
5003}
5004
Jeff Garzik0edd5b42005-09-07 00:48:31 -04005005static int ipw_best_network(struct ipw_priv *priv,
5006 struct ipw_network_match *match,
5007 struct ieee80211_network *network, int roaming)
James Ketrenos43f66a62005-03-25 12:31:53 -06005008{
5009 struct ipw_supported_rates rates;
5010
5011 /* Verify that this network's capability is compatible with the
5012 * current mode (AdHoc or Infrastructure) */
5013 if ((priv->ieee->iw_mode == IW_MODE_INFRA &&
Jouni Malinen24743852005-08-14 20:59:59 -07005014 !(network->capability & WLAN_CAPABILITY_ESS)) ||
James Ketrenos43f66a62005-03-25 12:31:53 -06005015 (priv->ieee->iw_mode == IW_MODE_ADHOC &&
5016 !(network->capability & WLAN_CAPABILITY_IBSS))) {
5017 IPW_DEBUG_ASSOC("Network '%s (" MAC_FMT ")' excluded due to "
Jeff Garzikbf794512005-07-31 13:07:26 -04005018 "capability mismatch.\n",
James Ketrenos43f66a62005-03-25 12:31:53 -06005019 escape_essid(network->ssid, network->ssid_len),
5020 MAC_ARG(network->bssid));
5021 return 0;
5022 }
5023
5024 /* If we do not have an ESSID for this AP, we can not associate with
5025 * it */
5026 if (network->flags & NETWORK_EMPTY_ESSID) {
5027 IPW_DEBUG_ASSOC("Network '%s (" MAC_FMT ")' excluded "
5028 "because of hidden ESSID.\n",
5029 escape_essid(network->ssid, network->ssid_len),
5030 MAC_ARG(network->bssid));
5031 return 0;
5032 }
Jeff Garzikbf794512005-07-31 13:07:26 -04005033
James Ketrenos43f66a62005-03-25 12:31:53 -06005034 if (unlikely(roaming)) {
5035 /* If we are roaming, then ensure check if this is a valid
5036 * network to try and roam to */
5037 if ((network->ssid_len != match->network->ssid_len) ||
Jeff Garzikbf794512005-07-31 13:07:26 -04005038 memcmp(network->ssid, match->network->ssid,
James Ketrenos43f66a62005-03-25 12:31:53 -06005039 network->ssid_len)) {
5040 IPW_DEBUG_ASSOC("Netowrk '%s (" MAC_FMT ")' excluded "
5041 "because of non-network ESSID.\n",
Jeff Garzikbf794512005-07-31 13:07:26 -04005042 escape_essid(network->ssid,
James Ketrenos43f66a62005-03-25 12:31:53 -06005043 network->ssid_len),
5044 MAC_ARG(network->bssid));
5045 return 0;
5046 }
5047 } else {
Jeff Garzikbf794512005-07-31 13:07:26 -04005048 /* If an ESSID has been configured then compare the broadcast
5049 * ESSID to ours */
5050 if ((priv->config & CFG_STATIC_ESSID) &&
James Ketrenos43f66a62005-03-25 12:31:53 -06005051 ((network->ssid_len != priv->essid_len) ||
Jeff Garzikbf794512005-07-31 13:07:26 -04005052 memcmp(network->ssid, priv->essid,
James Ketrenos43f66a62005-03-25 12:31:53 -06005053 min(network->ssid_len, priv->essid_len)))) {
5054 char escaped[IW_ESSID_MAX_SIZE * 2 + 1];
Jeff Garzik0edd5b42005-09-07 00:48:31 -04005055 strncpy(escaped,
5056 escape_essid(network->ssid, network->ssid_len),
James Ketrenos43f66a62005-03-25 12:31:53 -06005057 sizeof(escaped));
5058 IPW_DEBUG_ASSOC("Network '%s (" MAC_FMT ")' excluded "
Jeff Garzikbf794512005-07-31 13:07:26 -04005059 "because of ESSID mismatch: '%s'.\n",
James Ketrenos43f66a62005-03-25 12:31:53 -06005060 escaped, MAC_ARG(network->bssid),
Jeff Garzik0edd5b42005-09-07 00:48:31 -04005061 escape_essid(priv->essid,
5062 priv->essid_len));
James Ketrenos43f66a62005-03-25 12:31:53 -06005063 return 0;
5064 }
5065 }
5066
5067 /* If the old network rate is better than this one, don't bother
5068 * testing everything else. */
Jeff Garzik0edd5b42005-09-07 00:48:31 -04005069 if (match->network && match->network->stats.rssi > network->stats.rssi) {
James Ketrenos43f66a62005-03-25 12:31:53 -06005070 char escaped[IW_ESSID_MAX_SIZE * 2 + 1];
Jeff Garzikbf794512005-07-31 13:07:26 -04005071 strncpy(escaped,
5072 escape_essid(network->ssid, network->ssid_len),
James Ketrenos43f66a62005-03-25 12:31:53 -06005073 sizeof(escaped));
5074 IPW_DEBUG_ASSOC("Network '%s (" MAC_FMT ")' excluded because "
5075 "'%s (" MAC_FMT ")' has a stronger signal.\n",
5076 escaped, MAC_ARG(network->bssid),
5077 escape_essid(match->network->ssid,
5078 match->network->ssid_len),
5079 MAC_ARG(match->network->bssid));
5080 return 0;
5081 }
Jeff Garzikbf794512005-07-31 13:07:26 -04005082
James Ketrenos43f66a62005-03-25 12:31:53 -06005083 /* If this network has already had an association attempt within the
5084 * last 3 seconds, do not try and associate again... */
5085 if (network->last_associate &&
James Ketrenosea2b26e2005-08-24 21:25:16 -05005086 time_after(network->last_associate + (HZ * 3UL), jiffies)) {
James Ketrenos43f66a62005-03-25 12:31:53 -06005087 IPW_DEBUG_ASSOC("Network '%s (" MAC_FMT ")' excluded "
5088 "because of storming (%lu since last "
5089 "assoc attempt).\n",
5090 escape_essid(network->ssid, network->ssid_len),
5091 MAC_ARG(network->bssid),
5092 (jiffies - network->last_associate) / HZ);
5093 return 0;
5094 }
5095
5096 /* Now go through and see if the requested network is valid... */
Jeff Garzikbf794512005-07-31 13:07:26 -04005097 if (priv->ieee->scan_age != 0 &&
James Ketrenosea2b26e2005-08-24 21:25:16 -05005098 time_after(jiffies, network->last_scanned + priv->ieee->scan_age)) {
James Ketrenos43f66a62005-03-25 12:31:53 -06005099 IPW_DEBUG_ASSOC("Network '%s (" MAC_FMT ")' excluded "
5100 "because of age: %lums.\n",
5101 escape_essid(network->ssid, network->ssid_len),
5102 MAC_ARG(network->bssid),
5103 (jiffies - network->last_scanned) / (HZ / 100));
5104 return 0;
Jeff Garzikbf794512005-07-31 13:07:26 -04005105 }
James Ketrenos43f66a62005-03-25 12:31:53 -06005106
Jeff Garzikbf794512005-07-31 13:07:26 -04005107 if ((priv->config & CFG_STATIC_CHANNEL) &&
James Ketrenos43f66a62005-03-25 12:31:53 -06005108 (network->channel != priv->channel)) {
5109 IPW_DEBUG_ASSOC("Network '%s (" MAC_FMT ")' excluded "
5110 "because of channel mismatch: %d != %d.\n",
5111 escape_essid(network->ssid, network->ssid_len),
5112 MAC_ARG(network->bssid),
5113 network->channel, priv->channel);
5114 return 0;
5115 }
Jeff Garzikbf794512005-07-31 13:07:26 -04005116
James Ketrenos43f66a62005-03-25 12:31:53 -06005117 /* Verify privacy compatability */
Jeff Garzikbf794512005-07-31 13:07:26 -04005118 if (((priv->capability & CAP_PRIVACY_ON) ? 1 : 0) !=
James Ketrenos43f66a62005-03-25 12:31:53 -06005119 ((network->capability & WLAN_CAPABILITY_PRIVACY) ? 1 : 0)) {
5120 IPW_DEBUG_ASSOC("Network '%s (" MAC_FMT ")' excluded "
5121 "because of privacy mismatch: %s != %s.\n",
5122 escape_essid(network->ssid, network->ssid_len),
5123 MAC_ARG(network->bssid),
Jeff Garzikbf794512005-07-31 13:07:26 -04005124 priv->capability & CAP_PRIVACY_ON ? "on" :
James Ketrenos43f66a62005-03-25 12:31:53 -06005125 "off",
Jeff Garzikbf794512005-07-31 13:07:26 -04005126 network->capability &
Jeff Garzik0edd5b42005-09-07 00:48:31 -04005127 WLAN_CAPABILITY_PRIVACY ? "on" : "off");
James Ketrenos43f66a62005-03-25 12:31:53 -06005128 return 0;
5129 }
Jeff Garzikbf794512005-07-31 13:07:26 -04005130
5131 if ((priv->config & CFG_STATIC_BSSID) &&
James Ketrenos43f66a62005-03-25 12:31:53 -06005132 memcmp(network->bssid, priv->bssid, ETH_ALEN)) {
5133 IPW_DEBUG_ASSOC("Network '%s (" MAC_FMT ")' excluded "
5134 "because of BSSID mismatch: " MAC_FMT ".\n",
5135 escape_essid(network->ssid, network->ssid_len),
Jeff Garzik0edd5b42005-09-07 00:48:31 -04005136 MAC_ARG(network->bssid), MAC_ARG(priv->bssid));
James Ketrenos43f66a62005-03-25 12:31:53 -06005137 return 0;
5138 }
Jeff Garzikbf794512005-07-31 13:07:26 -04005139
James Ketrenos43f66a62005-03-25 12:31:53 -06005140 /* Filter out any incompatible freq / mode combinations */
5141 if (!ieee80211_is_valid_mode(priv->ieee, network->mode)) {
5142 IPW_DEBUG_ASSOC("Network '%s (" MAC_FMT ")' excluded "
5143 "because of invalid frequency/mode "
5144 "combination.\n",
5145 escape_essid(network->ssid, network->ssid_len),
5146 MAC_ARG(network->bssid));
5147 return 0;
5148 }
Jeff Garzikbf794512005-07-31 13:07:26 -04005149
James Ketrenosea2b26e2005-08-24 21:25:16 -05005150 /* Ensure that the rates supported by the driver are compatible with
5151 * this AP, including verification of basic rates (mandatory) */
5152 if (!ipw_compatible_rates(priv, network, &rates)) {
5153 IPW_DEBUG_ASSOC("Network '%s (" MAC_FMT ")' excluded "
5154 "because configured rate mask excludes "
5155 "AP mandatory rate.\n",
5156 escape_essid(network->ssid, network->ssid_len),
5157 MAC_ARG(network->bssid));
5158 return 0;
5159 }
5160
James Ketrenos43f66a62005-03-25 12:31:53 -06005161 if (rates.num_rates == 0) {
5162 IPW_DEBUG_ASSOC("Network '%s (" MAC_FMT ")' excluded "
5163 "because of no compatible rates.\n",
5164 escape_essid(network->ssid, network->ssid_len),
5165 MAC_ARG(network->bssid));
5166 return 0;
5167 }
Jeff Garzikbf794512005-07-31 13:07:26 -04005168
James Ketrenos43f66a62005-03-25 12:31:53 -06005169 /* TODO: Perform any further minimal comparititive tests. We do not
5170 * want to put too much policy logic here; intelligent scan selection
5171 * should occur within a generic IEEE 802.11 user space tool. */
5172
5173 /* Set up 'new' AP to this network */
5174 ipw_copy_rates(&match->rates, &rates);
5175 match->network = network;
5176
5177 IPW_DEBUG_ASSOC("Network '%s (" MAC_FMT ")' is a viable match.\n",
5178 escape_essid(network->ssid, network->ssid_len),
5179 MAC_ARG(network->bssid));
5180
5181 return 1;
5182}
5183
Jeff Garzikbf794512005-07-31 13:07:26 -04005184static void ipw_adhoc_create(struct ipw_priv *priv,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04005185 struct ieee80211_network *network)
James Ketrenos43f66a62005-03-25 12:31:53 -06005186{
5187 /*
5188 * For the purposes of scanning, we can set our wireless mode
5189 * to trigger scans across combinations of bands, but when it
5190 * comes to creating a new ad-hoc network, we have tell the FW
5191 * exactly which band to use.
5192 *
Jeff Garzikbf794512005-07-31 13:07:26 -04005193 * We also have the possibility of an invalid channel for the
James Ketrenos43f66a62005-03-25 12:31:53 -06005194 * chossen band. Attempting to create a new ad-hoc network
5195 * with an invalid channel for wireless mode will trigger a
5196 * FW fatal error.
5197 */
5198 network->mode = is_valid_channel(priv->ieee->mode, priv->channel);
James Ketrenosa613bff2005-08-24 21:43:11 -05005199 if (!network->mode) {
James Ketrenos43f66a62005-03-25 12:31:53 -06005200 IPW_WARNING("Overriding invalid channel\n");
5201 if (priv->ieee->mode & IEEE_A) {
5202 network->mode = IEEE_A;
5203 priv->channel = band_a_active_channel[0];
5204 } else if (priv->ieee->mode & IEEE_G) {
5205 network->mode = IEEE_G;
5206 priv->channel = band_b_active_channel[0];
5207 } else {
5208 network->mode = IEEE_B;
5209 priv->channel = band_b_active_channel[0];
5210 }
5211 }
5212
5213 network->channel = priv->channel;
5214 priv->config |= CFG_ADHOC_PERSIST;
5215 ipw_create_bssid(priv, network->bssid);
5216 network->ssid_len = priv->essid_len;
5217 memcpy(network->ssid, priv->essid, priv->essid_len);
5218 memset(&network->stats, 0, sizeof(network->stats));
5219 network->capability = WLAN_CAPABILITY_IBSS;
James Ketrenosea2b26e2005-08-24 21:25:16 -05005220 if (!(priv->config & CFG_PREAMBLE_LONG))
5221 network->capability |= WLAN_CAPABILITY_SHORT_PREAMBLE;
James Ketrenos43f66a62005-03-25 12:31:53 -06005222 if (priv->capability & CAP_PRIVACY_ON)
5223 network->capability |= WLAN_CAPABILITY_PRIVACY;
5224 network->rates_len = min(priv->rates.num_rates, MAX_RATES_LENGTH);
Jeff Garzik0edd5b42005-09-07 00:48:31 -04005225 memcpy(network->rates, priv->rates.supported_rates, network->rates_len);
James Ketrenos43f66a62005-03-25 12:31:53 -06005226 network->rates_ex_len = priv->rates.num_rates - network->rates_len;
Jeff Garzikbf794512005-07-31 13:07:26 -04005227 memcpy(network->rates_ex,
James Ketrenos43f66a62005-03-25 12:31:53 -06005228 &priv->rates.supported_rates[network->rates_len],
5229 network->rates_ex_len);
5230 network->last_scanned = 0;
5231 network->flags = 0;
5232 network->last_associate = 0;
5233 network->time_stamp[0] = 0;
5234 network->time_stamp[1] = 0;
Jeff Garzik0edd5b42005-09-07 00:48:31 -04005235 network->beacon_interval = 100; /* Default */
5236 network->listen_interval = 10; /* Default */
5237 network->atim_window = 0; /* Default */
James Ketrenos43f66a62005-03-25 12:31:53 -06005238 network->wpa_ie_len = 0;
5239 network->rsn_ie_len = 0;
James Ketrenos43f66a62005-03-25 12:31:53 -06005240}
5241
5242static void ipw_send_wep_keys(struct ipw_priv *priv)
5243{
5244 struct ipw_wep_key *key;
5245 int i;
5246 struct host_cmd cmd = {
5247 .cmd = IPW_CMD_WEP_KEY,
5248 .len = sizeof(*key)
5249 };
5250
5251 key = (struct ipw_wep_key *)&cmd.param;
5252 key->cmd_id = DINO_CMD_WEP_KEY;
5253 key->seq_num = 0;
5254
Jeff Garzikbf794512005-07-31 13:07:26 -04005255 for (i = 0; i < 4; i++) {
James Ketrenos43f66a62005-03-25 12:31:53 -06005256 key->key_index = i;
James Ketrenosa613bff2005-08-24 21:43:11 -05005257 if (!(priv->sec.flags & (1 << i)))
James Ketrenos43f66a62005-03-25 12:31:53 -06005258 key->key_size = 0;
James Ketrenosa613bff2005-08-24 21:43:11 -05005259 else {
James Ketrenos43f66a62005-03-25 12:31:53 -06005260 key->key_size = priv->sec.key_sizes[i];
5261 memcpy(key->key, priv->sec.keys[i], key->key_size);
5262 }
5263
5264 if (ipw_send_cmd(priv, &cmd)) {
5265 IPW_ERROR("failed to send WEP_KEY command\n");
5266 return;
5267 }
Jeff Garzikbf794512005-07-31 13:07:26 -04005268 }
James Ketrenos43f66a62005-03-25 12:31:53 -06005269}
5270
5271static void ipw_adhoc_check(void *data)
5272{
5273 struct ipw_priv *priv = data;
Jeff Garzikbf794512005-07-31 13:07:26 -04005274
James Ketrenos43f66a62005-03-25 12:31:53 -06005275 if (priv->missed_adhoc_beacons++ > priv->missed_beacon_threshold &&
5276 !(priv->config & CFG_ADHOC_PERSIST)) {
5277 IPW_DEBUG_SCAN("Disassociating due to missed beacons\n");
5278 ipw_remove_current_network(priv);
5279 ipw_disassociate(priv);
5280 return;
5281 }
5282
Jeff Garzikbf794512005-07-31 13:07:26 -04005283 queue_delayed_work(priv->workqueue, &priv->adhoc_check,
James Ketrenos43f66a62005-03-25 12:31:53 -06005284 priv->assoc_request.beacon_interval);
5285}
5286
James Ketrenosc848d0a2005-08-24 21:56:24 -05005287static void ipw_bg_adhoc_check(void *data)
5288{
5289 struct ipw_priv *priv = data;
5290 down(&priv->sem);
5291 ipw_adhoc_check(data);
5292 up(&priv->sem);
5293}
5294
James Ketrenos43f66a62005-03-25 12:31:53 -06005295#ifdef CONFIG_IPW_DEBUG
5296static void ipw_debug_config(struct ipw_priv *priv)
5297{
5298 IPW_DEBUG_INFO("Scan completed, no valid APs matched "
5299 "[CFG 0x%08X]\n", priv->config);
5300 if (priv->config & CFG_STATIC_CHANNEL)
Jeff Garzik0edd5b42005-09-07 00:48:31 -04005301 IPW_DEBUG_INFO("Channel locked to %d\n", priv->channel);
James Ketrenos43f66a62005-03-25 12:31:53 -06005302 else
5303 IPW_DEBUG_INFO("Channel unlocked.\n");
5304 if (priv->config & CFG_STATIC_ESSID)
Jeff Garzikbf794512005-07-31 13:07:26 -04005305 IPW_DEBUG_INFO("ESSID locked to '%s'\n",
Jeff Garzik0edd5b42005-09-07 00:48:31 -04005306 escape_essid(priv->essid, priv->essid_len));
James Ketrenos43f66a62005-03-25 12:31:53 -06005307 else
5308 IPW_DEBUG_INFO("ESSID unlocked.\n");
5309 if (priv->config & CFG_STATIC_BSSID)
James Ketrenosea2b26e2005-08-24 21:25:16 -05005310 IPW_DEBUG_INFO("BSSID locked to " MAC_FMT "\n",
5311 MAC_ARG(priv->bssid));
James Ketrenos43f66a62005-03-25 12:31:53 -06005312 else
5313 IPW_DEBUG_INFO("BSSID unlocked.\n");
5314 if (priv->capability & CAP_PRIVACY_ON)
5315 IPW_DEBUG_INFO("PRIVACY on\n");
5316 else
5317 IPW_DEBUG_INFO("PRIVACY off\n");
5318 IPW_DEBUG_INFO("RATE MASK: 0x%08X\n", priv->rates_mask);
5319}
5320#else
Jiri Benc8d45ff72005-08-25 20:09:39 -04005321#define ipw_debug_config(x) do {} while (0)
James Ketrenos43f66a62005-03-25 12:31:53 -06005322#endif
5323
5324static inline void ipw_set_fixed_rate(struct ipw_priv *priv,
5325 struct ieee80211_network *network)
5326{
5327 /* TODO: Verify that this works... */
5328 struct ipw_fixed_rate fr = {
5329 .tx_rates = priv->rates_mask
5330 };
5331 u32 reg;
5332 u16 mask = 0;
5333
Jeff Garzikbf794512005-07-31 13:07:26 -04005334 /* Identify 'current FW band' and match it with the fixed
James Ketrenos43f66a62005-03-25 12:31:53 -06005335 * Tx rates */
Jeff Garzikbf794512005-07-31 13:07:26 -04005336
James Ketrenos43f66a62005-03-25 12:31:53 -06005337 switch (priv->ieee->freq_band) {
Jeff Garzik0edd5b42005-09-07 00:48:31 -04005338 case IEEE80211_52GHZ_BAND: /* A only */
James Ketrenos43f66a62005-03-25 12:31:53 -06005339 /* IEEE_A */
5340 if (priv->rates_mask & ~IEEE80211_OFDM_RATES_MASK) {
5341 /* Invalid fixed rate mask */
James Ketrenosea2b26e2005-08-24 21:25:16 -05005342 IPW_DEBUG_WX
5343 ("invalid fixed rate mask in ipw_set_fixed_rate\n");
James Ketrenos43f66a62005-03-25 12:31:53 -06005344 fr.tx_rates = 0;
5345 break;
5346 }
Jeff Garzikbf794512005-07-31 13:07:26 -04005347
James Ketrenos43f66a62005-03-25 12:31:53 -06005348 fr.tx_rates >>= IEEE80211_OFDM_SHIFT_MASK_A;
5349 break;
5350
Jeff Garzik0edd5b42005-09-07 00:48:31 -04005351 default: /* 2.4Ghz or Mixed */
James Ketrenos43f66a62005-03-25 12:31:53 -06005352 /* IEEE_B */
5353 if (network->mode == IEEE_B) {
5354 if (fr.tx_rates & ~IEEE80211_CCK_RATES_MASK) {
5355 /* Invalid fixed rate mask */
James Ketrenosea2b26e2005-08-24 21:25:16 -05005356 IPW_DEBUG_WX
5357 ("invalid fixed rate mask in ipw_set_fixed_rate\n");
James Ketrenos43f66a62005-03-25 12:31:53 -06005358 fr.tx_rates = 0;
5359 }
5360 break;
Jeff Garzikbf794512005-07-31 13:07:26 -04005361 }
James Ketrenos43f66a62005-03-25 12:31:53 -06005362
5363 /* IEEE_G */
5364 if (fr.tx_rates & ~(IEEE80211_CCK_RATES_MASK |
5365 IEEE80211_OFDM_RATES_MASK)) {
5366 /* Invalid fixed rate mask */
James Ketrenosea2b26e2005-08-24 21:25:16 -05005367 IPW_DEBUG_WX
5368 ("invalid fixed rate mask in ipw_set_fixed_rate\n");
James Ketrenos43f66a62005-03-25 12:31:53 -06005369 fr.tx_rates = 0;
5370 break;
5371 }
5372
5373 if (IEEE80211_OFDM_RATE_6MB_MASK & fr.tx_rates) {
5374 mask |= (IEEE80211_OFDM_RATE_6MB_MASK >> 1);
5375 fr.tx_rates &= ~IEEE80211_OFDM_RATE_6MB_MASK;
5376 }
Jeff Garzikbf794512005-07-31 13:07:26 -04005377
James Ketrenos43f66a62005-03-25 12:31:53 -06005378 if (IEEE80211_OFDM_RATE_9MB_MASK & fr.tx_rates) {
5379 mask |= (IEEE80211_OFDM_RATE_9MB_MASK >> 1);
5380 fr.tx_rates &= ~IEEE80211_OFDM_RATE_9MB_MASK;
5381 }
Jeff Garzikbf794512005-07-31 13:07:26 -04005382
James Ketrenos43f66a62005-03-25 12:31:53 -06005383 if (IEEE80211_OFDM_RATE_12MB_MASK & fr.tx_rates) {
5384 mask |= (IEEE80211_OFDM_RATE_12MB_MASK >> 1);
5385 fr.tx_rates &= ~IEEE80211_OFDM_RATE_12MB_MASK;
5386 }
Jeff Garzikbf794512005-07-31 13:07:26 -04005387
James Ketrenos43f66a62005-03-25 12:31:53 -06005388 fr.tx_rates |= mask;
5389 break;
5390 }
5391
5392 reg = ipw_read32(priv, IPW_MEM_FIXED_OVERRIDE);
Jeff Garzik0edd5b42005-09-07 00:48:31 -04005393 ipw_write_reg32(priv, reg, *(u32 *) & fr);
James Ketrenos43f66a62005-03-25 12:31:53 -06005394}
5395
James Ketrenosea2b26e2005-08-24 21:25:16 -05005396static void ipw_abort_scan(struct ipw_priv *priv)
5397{
5398 int err;
5399
5400 if (priv->status & STATUS_SCAN_ABORTING) {
5401 IPW_DEBUG_HC("Ignoring concurrent scan abort request.\n");
5402 return;
5403 }
5404 priv->status |= STATUS_SCAN_ABORTING;
5405
5406 err = ipw_send_scan_abort(priv);
5407 if (err)
5408 IPW_DEBUG_HC("Request to abort scan failed.\n");
5409}
5410
5411static int ipw_request_scan(struct ipw_priv *priv)
5412{
5413 struct ipw_scan_request_ext scan;
5414 int channel_index = 0;
5415 int i, err, scan_type;
5416
5417 if (priv->status & STATUS_EXIT_PENDING) {
5418 IPW_DEBUG_SCAN("Aborting scan due to device shutdown\n");
5419 priv->status |= STATUS_SCAN_PENDING;
5420 return 0;
5421 }
5422
5423 if (priv->status & STATUS_SCANNING) {
James Ketrenosa613bff2005-08-24 21:43:11 -05005424 IPW_DEBUG_HC("Concurrent scan requested. Ignoring.\n");
5425// IPW_DEBUG_HC("Concurrent scan requested. Aborting first.\n");
James Ketrenosea2b26e2005-08-24 21:25:16 -05005426 priv->status |= STATUS_SCAN_PENDING;
James Ketrenosa613bff2005-08-24 21:43:11 -05005427// ipw_abort_scan(priv);
James Ketrenosea2b26e2005-08-24 21:25:16 -05005428 return 0;
5429 }
5430
5431 if (priv->status & STATUS_SCAN_ABORTING) {
5432 IPW_DEBUG_HC("Scan request while abort pending. Queuing.\n");
5433 priv->status |= STATUS_SCAN_PENDING;
5434 return 0;
5435 }
5436
5437 if (priv->status & STATUS_RF_KILL_MASK) {
5438 IPW_DEBUG_HC("Aborting scan due to RF Kill activation\n");
5439 priv->status |= STATUS_SCAN_PENDING;
5440 return 0;
5441 }
5442
5443 memset(&scan, 0, sizeof(scan));
5444
James Ketrenosa613bff2005-08-24 21:43:11 -05005445 scan.dwell_time[IPW_SCAN_ACTIVE_BROADCAST_SCAN] = cpu_to_le16(20);
5446 scan.dwell_time[IPW_SCAN_ACTIVE_BROADCAST_AND_DIRECT_SCAN] =
5447 cpu_to_le16(20);
5448 scan.dwell_time[IPW_SCAN_PASSIVE_FULL_DWELL_SCAN] = cpu_to_le16(20);
James Ketrenosea2b26e2005-08-24 21:25:16 -05005449
James Ketrenosa613bff2005-08-24 21:43:11 -05005450 scan.full_scan_index = cpu_to_le32(ieee80211_get_scans(priv->ieee));
James Ketrenosea2b26e2005-08-24 21:25:16 -05005451
5452#ifdef CONFIG_IPW_MONITOR
5453 if (priv->ieee->iw_mode == IW_MODE_MONITOR) {
5454 u8 band = 0, channel = priv->channel;
5455
5456 if (is_valid_channel(IEEE_A, channel))
5457 band = (u8) (IPW_A_MODE << 6) | 1;
5458
5459 if (is_valid_channel(IEEE_B | IEEE_G, channel))
5460 band = (u8) (IPW_B_MODE << 6) | 1;
5461
5462 if (band == 0) {
5463 band = (u8) (IPW_B_MODE << 6) | 1;
5464 channel = 9;
5465 }
5466
5467 scan.channels_list[channel_index++] = band;
5468 scan.channels_list[channel_index] = channel;
5469 ipw_set_scan_type(&scan, channel_index,
5470 IPW_SCAN_PASSIVE_FULL_DWELL_SCAN);
5471
James Ketrenosa613bff2005-08-24 21:43:11 -05005472 scan.dwell_time[IPW_SCAN_PASSIVE_FULL_DWELL_SCAN] =
5473 cpu_to_le16(2000);
James Ketrenosea2b26e2005-08-24 21:25:16 -05005474 } else {
5475#endif /* CONFIG_IPW_MONITOR */
5476 /* If we are roaming, then make this a directed scan for the current
5477 * network. Otherwise, ensure that every other scan is a fast
5478 * channel hop scan */
James Ketrenosc848d0a2005-08-24 21:56:24 -05005479 if ((priv->status & STATUS_ROAMING) || (!(priv->status & STATUS_ASSOCIATED) && (priv->config & CFG_STATIC_ESSID) && (le32_to_cpu(scan.full_scan_index) % 2))) { /* || (
5480 (priv->status & STATUS_ASSOCIATED) &&
5481 (priv->ieee->iw_mode == IW_MODE_ADHOC))) { */
James Ketrenosea2b26e2005-08-24 21:25:16 -05005482 err = ipw_send_ssid(priv, priv->essid, priv->essid_len);
5483 if (err) {
5484 IPW_DEBUG_HC
5485 ("Attempt to send SSID command failed.\n");
5486 return err;
5487 }
5488
5489 scan_type = IPW_SCAN_ACTIVE_BROADCAST_AND_DIRECT_SCAN;
5490 } else {
5491 scan_type = IPW_SCAN_ACTIVE_BROADCAST_SCAN;
5492 }
5493
5494 if (priv->ieee->freq_band & IEEE80211_52GHZ_BAND) {
5495 int start = channel_index;
5496 for (i = 0; i < MAX_A_CHANNELS; i++) {
5497 if (band_a_active_channel[i] == 0)
5498 break;
5499 if ((priv->status & STATUS_ASSOCIATED) &&
5500 band_a_active_channel[i] == priv->channel)
5501 continue;
5502 channel_index++;
5503 scan.channels_list[channel_index] =
5504 band_a_active_channel[i];
5505 ipw_set_scan_type(&scan, channel_index,
5506 scan_type);
5507 }
5508
5509 if (start != channel_index) {
5510 scan.channels_list[start] =
5511 (u8) (IPW_A_MODE << 6) | (channel_index -
5512 start);
5513 channel_index++;
5514 }
5515 }
5516
5517 if (priv->ieee->freq_band & IEEE80211_24GHZ_BAND) {
5518 int start = channel_index;
5519 for (i = 0; i < MAX_B_CHANNELS; i++) {
5520 if (band_b_active_channel[i] == 0)
5521 break;
5522 if ((priv->status & STATUS_ASSOCIATED) &&
5523 band_b_active_channel[i] == priv->channel)
5524 continue;
5525 channel_index++;
5526 scan.channels_list[channel_index] =
5527 band_b_active_channel[i];
5528 ipw_set_scan_type(&scan, channel_index,
5529 scan_type);
5530 }
5531
5532 if (start != channel_index) {
5533 scan.channels_list[start] =
5534 (u8) (IPW_B_MODE << 6) | (channel_index -
5535 start);
5536 }
5537 }
5538#ifdef CONFIG_IPW_MONITOR
5539 }
5540#endif
5541
5542 err = ipw_send_scan_request_ext(priv, &scan);
5543 if (err) {
5544 IPW_DEBUG_HC("Sending scan command failed: %08X\n", err);
5545 return -EIO;
5546 }
5547
5548 priv->status |= STATUS_SCANNING;
5549 priv->status &= ~STATUS_SCAN_PENDING;
5550
5551 return 0;
5552}
5553
James Ketrenosc848d0a2005-08-24 21:56:24 -05005554static void ipw_bg_request_scan(void *data)
5555{
5556 struct ipw_priv *priv = data;
5557 down(&priv->sem);
5558 ipw_request_scan(data);
5559 up(&priv->sem);
5560}
5561
5562static void ipw_bg_abort_scan(void *data)
5563{
5564 struct ipw_priv *priv = data;
5565 down(&priv->sem);
5566 ipw_abort_scan(data);
5567 up(&priv->sem);
5568}
5569
James Ketrenosea2b26e2005-08-24 21:25:16 -05005570/* Support for wpa_supplicant. Will be replaced with WEXT once
5571 * they get WPA support. */
James Ketrenosea2b26e2005-08-24 21:25:16 -05005572
5573/* following definitions must match definitions in driver_ipw.c */
5574
5575#define IPW_IOCTL_WPA_SUPPLICANT SIOCIWFIRSTPRIV+30
5576
5577#define IPW_CMD_SET_WPA_PARAM 1
5578#define IPW_CMD_SET_WPA_IE 2
5579#define IPW_CMD_SET_ENCRYPTION 3
5580#define IPW_CMD_MLME 4
5581
5582#define IPW_PARAM_WPA_ENABLED 1
5583#define IPW_PARAM_TKIP_COUNTERMEASURES 2
5584#define IPW_PARAM_DROP_UNENCRYPTED 3
5585#define IPW_PARAM_PRIVACY_INVOKED 4
5586#define IPW_PARAM_AUTH_ALGS 5
5587#define IPW_PARAM_IEEE_802_1X 6
5588
5589#define IPW_MLME_STA_DEAUTH 1
5590#define IPW_MLME_STA_DISASSOC 2
5591
5592#define IPW_CRYPT_ERR_UNKNOWN_ALG 2
5593#define IPW_CRYPT_ERR_UNKNOWN_ADDR 3
5594#define IPW_CRYPT_ERR_CRYPT_INIT_FAILED 4
5595#define IPW_CRYPT_ERR_KEY_SET_FAILED 5
5596#define IPW_CRYPT_ERR_TX_KEY_SET_FAILED 6
5597#define IPW_CRYPT_ERR_CARD_CONF_FAILED 7
5598
5599#define IPW_CRYPT_ALG_NAME_LEN 16
5600
5601struct ipw_param {
5602 u32 cmd;
5603 u8 sta_addr[ETH_ALEN];
5604 union {
5605 struct {
5606 u8 name;
5607 u32 value;
5608 } wpa_param;
5609 struct {
5610 u32 len;
5611 u8 *data;
5612 } wpa_ie;
5613 struct {
5614 int command;
5615 int reason_code;
5616 } mlme;
5617 struct {
5618 u8 alg[IPW_CRYPT_ALG_NAME_LEN];
5619 u8 set_tx;
5620 u32 err;
5621 u8 idx;
5622 u8 seq[8]; /* sequence counter (set: RX, get: TX) */
5623 u16 key_len;
5624 u8 key[0];
5625 } crypt;
5626
5627 } u;
5628};
5629
5630/* end of driver_ipw.c code */
5631
5632static int ipw_wpa_enable(struct ipw_priv *priv, int value)
5633{
5634 struct ieee80211_device *ieee = priv->ieee;
5635 struct ieee80211_security sec = {
5636 .flags = SEC_LEVEL | SEC_ENABLED,
5637 };
5638 int ret = 0;
5639
5640 ieee->wpa_enabled = value;
5641
5642 if (value) {
5643 sec.level = SEC_LEVEL_3;
5644 sec.enabled = 1;
5645 } else {
5646 sec.level = SEC_LEVEL_0;
5647 sec.enabled = 0;
James Ketrenosa613bff2005-08-24 21:43:11 -05005648 ieee->wpa_ie_len = 0;
James Ketrenosea2b26e2005-08-24 21:25:16 -05005649 }
5650
5651 if (ieee->set_security)
5652 ieee->set_security(ieee->dev, &sec);
5653 else
5654 ret = -EOPNOTSUPP;
5655
5656 return ret;
5657}
5658
5659#define AUTH_ALG_OPEN_SYSTEM 0x1
5660#define AUTH_ALG_SHARED_KEY 0x2
5661
5662static int ipw_wpa_set_auth_algs(struct ipw_priv *priv, int value)
5663{
5664 struct ieee80211_device *ieee = priv->ieee;
5665 struct ieee80211_security sec = {
5666 .flags = SEC_AUTH_MODE,
5667 };
5668 int ret = 0;
5669
5670 if (value & AUTH_ALG_SHARED_KEY) {
5671 sec.auth_mode = WLAN_AUTH_SHARED_KEY;
5672 ieee->open_wep = 0;
5673 } else {
5674 sec.auth_mode = WLAN_AUTH_OPEN;
5675 ieee->open_wep = 1;
5676 }
5677
5678 if (ieee->set_security)
5679 ieee->set_security(ieee->dev, &sec);
5680 else
5681 ret = -EOPNOTSUPP;
5682
5683 return ret;
5684}
5685
5686static int ipw_wpa_set_param(struct net_device *dev, u8 name, u32 value)
5687{
5688 struct ipw_priv *priv = ieee80211_priv(dev);
James Ketrenosa613bff2005-08-24 21:43:11 -05005689 struct ieee80211_crypt_data *crypt;
5690 unsigned long flags;
James Ketrenosea2b26e2005-08-24 21:25:16 -05005691 int ret = 0;
5692
5693 switch (name) {
5694 case IPW_PARAM_WPA_ENABLED:
5695 ret = ipw_wpa_enable(priv, value);
5696 break;
5697
5698 case IPW_PARAM_TKIP_COUNTERMEASURES:
James Ketrenosa613bff2005-08-24 21:43:11 -05005699 crypt = priv->ieee->crypt[priv->ieee->tx_keyidx];
5700 if (!crypt || !crypt->ops->set_flags || !crypt->ops->get_flags) {
5701 IPW_WARNING("Can't set TKIP countermeasures: "
5702 "crypt not set!\n");
5703 break;
5704 }
5705
5706 flags = crypt->ops->get_flags(crypt->priv);
5707
5708 if (value)
5709 flags |= IEEE80211_CRYPTO_TKIP_COUNTERMEASURES;
5710 else
5711 flags &= ~IEEE80211_CRYPTO_TKIP_COUNTERMEASURES;
5712
5713 crypt->ops->set_flags(flags, crypt->priv);
5714
James Ketrenosea2b26e2005-08-24 21:25:16 -05005715 break;
5716
5717 case IPW_PARAM_DROP_UNENCRYPTED:
5718 priv->ieee->drop_unencrypted = value;
5719 break;
5720
5721 case IPW_PARAM_PRIVACY_INVOKED:
5722 priv->ieee->privacy_invoked = value;
5723 break;
5724
5725 case IPW_PARAM_AUTH_ALGS:
5726 ret = ipw_wpa_set_auth_algs(priv, value);
5727 break;
5728
5729 case IPW_PARAM_IEEE_802_1X:
5730 priv->ieee->ieee802_1x = value;
5731 break;
5732
5733 default:
5734 IPW_ERROR("%s: Unknown WPA param: %d\n", dev->name, name);
5735 ret = -EOPNOTSUPP;
5736 }
5737
5738 return ret;
5739}
5740
5741static int ipw_wpa_mlme(struct net_device *dev, int command, int reason)
5742{
5743 struct ipw_priv *priv = ieee80211_priv(dev);
5744 int ret = 0;
5745
5746 switch (command) {
5747 case IPW_MLME_STA_DEAUTH:
5748 // silently ignore
5749 break;
5750
5751 case IPW_MLME_STA_DISASSOC:
5752 ipw_disassociate(priv);
5753 break;
5754
5755 default:
5756 IPW_ERROR("%s: Unknown MLME request: %d\n", dev->name, command);
5757 ret = -EOPNOTSUPP;
5758 }
5759
5760 return ret;
5761}
5762
5763static int ipw_set_rsn_capa(struct ipw_priv *priv,
5764 char *capabilities, int length)
5765{
5766 struct host_cmd cmd = {
5767 .cmd = IPW_CMD_RSN_CAPABILITIES,
5768 .len = length,
5769 };
5770
5771 IPW_DEBUG_HC("HOST_CMD_RSN_CAPABILITIES\n");
5772
5773 memcpy(&cmd.param, capabilities, length);
5774 if (ipw_send_cmd(priv, &cmd)) {
5775 IPW_ERROR("failed to send HOST_CMD_RSN_CAPABILITIES command\n");
5776 return -1;
5777 }
5778 return 0;
5779}
5780
5781void ipw_wpa_assoc_frame(struct ipw_priv *priv, char *wpa_ie, int wpa_ie_len)
5782{
5783 /* make sure WPA is enabled */
5784 ipw_wpa_enable(priv, 1);
5785
James Ketrenosc848d0a2005-08-24 21:56:24 -05005786 ipw_disassociate(priv);
James Ketrenosea2b26e2005-08-24 21:25:16 -05005787}
5788
5789static int ipw_wpa_set_wpa_ie(struct net_device *dev,
5790 struct ipw_param *param, int plen)
5791{
5792 struct ipw_priv *priv = ieee80211_priv(dev);
5793 struct ieee80211_device *ieee = priv->ieee;
5794 u8 *buf;
5795
5796 if (!ieee->wpa_enabled)
5797 return -EOPNOTSUPP;
5798
5799 if (param->u.wpa_ie.len > MAX_WPA_IE_LEN ||
5800 (param->u.wpa_ie.len && param->u.wpa_ie.data == NULL))
5801 return -EINVAL;
5802
5803 if (param->u.wpa_ie.len) {
5804 buf = kmalloc(param->u.wpa_ie.len, GFP_KERNEL);
5805 if (buf == NULL)
5806 return -ENOMEM;
5807
5808 memcpy(buf, param->u.wpa_ie.data, param->u.wpa_ie.len);
5809 kfree(ieee->wpa_ie);
5810 ieee->wpa_ie = buf;
5811 ieee->wpa_ie_len = param->u.wpa_ie.len;
5812 } else {
5813 kfree(ieee->wpa_ie);
5814 ieee->wpa_ie = NULL;
5815 ieee->wpa_ie_len = 0;
5816 }
5817
5818 ipw_wpa_assoc_frame(priv, ieee->wpa_ie, ieee->wpa_ie_len);
5819 return 0;
5820}
5821
5822/* implementation borrowed from hostap driver */
5823
5824static int ipw_wpa_set_encryption(struct net_device *dev,
5825 struct ipw_param *param, int param_len)
5826{
5827 int ret = 0;
5828 struct ipw_priv *priv = ieee80211_priv(dev);
5829 struct ieee80211_device *ieee = priv->ieee;
5830 struct ieee80211_crypto_ops *ops;
5831 struct ieee80211_crypt_data **crypt;
5832
5833 struct ieee80211_security sec = {
5834 .flags = 0,
5835 };
5836
5837 param->u.crypt.err = 0;
5838 param->u.crypt.alg[IPW_CRYPT_ALG_NAME_LEN - 1] = '\0';
5839
5840 if (param_len !=
5841 (int)((char *)param->u.crypt.key - (char *)param) +
5842 param->u.crypt.key_len) {
5843 IPW_DEBUG_INFO("Len mismatch %d, %d\n", param_len,
5844 param->u.crypt.key_len);
5845 return -EINVAL;
5846 }
5847 if (param->sta_addr[0] == 0xff && param->sta_addr[1] == 0xff &&
5848 param->sta_addr[2] == 0xff && param->sta_addr[3] == 0xff &&
5849 param->sta_addr[4] == 0xff && param->sta_addr[5] == 0xff) {
5850 if (param->u.crypt.idx >= WEP_KEYS)
5851 return -EINVAL;
5852 crypt = &ieee->crypt[param->u.crypt.idx];
5853 } else {
5854 return -EINVAL;
5855 }
5856
5857 if (strcmp(param->u.crypt.alg, "none") == 0) {
5858 if (crypt) {
5859 sec.enabled = 0;
5860 sec.level = SEC_LEVEL_0;
5861 sec.flags |= SEC_ENABLED | SEC_LEVEL;
5862 ieee80211_crypt_delayed_deinit(ieee, crypt);
5863 }
5864 goto done;
5865 }
5866 sec.enabled = 1;
5867 sec.flags |= SEC_ENABLED;
5868
5869 ops = ieee80211_get_crypto_ops(param->u.crypt.alg);
5870 if (ops == NULL && strcmp(param->u.crypt.alg, "WEP") == 0) {
5871 request_module("ieee80211_crypt_wep");
5872 ops = ieee80211_get_crypto_ops(param->u.crypt.alg);
5873 } else if (ops == NULL && strcmp(param->u.crypt.alg, "TKIP") == 0) {
5874 request_module("ieee80211_crypt_tkip");
5875 ops = ieee80211_get_crypto_ops(param->u.crypt.alg);
5876 } else if (ops == NULL && strcmp(param->u.crypt.alg, "CCMP") == 0) {
5877 request_module("ieee80211_crypt_ccmp");
5878 ops = ieee80211_get_crypto_ops(param->u.crypt.alg);
5879 }
5880 if (ops == NULL) {
5881 IPW_DEBUG_INFO("%s: unknown crypto alg '%s'\n",
5882 dev->name, param->u.crypt.alg);
5883 param->u.crypt.err = IPW_CRYPT_ERR_UNKNOWN_ALG;
5884 ret = -EINVAL;
5885 goto done;
5886 }
5887
5888 if (*crypt == NULL || (*crypt)->ops != ops) {
5889 struct ieee80211_crypt_data *new_crypt;
5890
5891 ieee80211_crypt_delayed_deinit(ieee, crypt);
5892
5893 new_crypt = (struct ieee80211_crypt_data *)
5894 kmalloc(sizeof(*new_crypt), GFP_KERNEL);
5895 if (new_crypt == NULL) {
5896 ret = -ENOMEM;
5897 goto done;
5898 }
5899 memset(new_crypt, 0, sizeof(struct ieee80211_crypt_data));
5900 new_crypt->ops = ops;
5901 if (new_crypt->ops && try_module_get(new_crypt->ops->owner))
5902 new_crypt->priv =
5903 new_crypt->ops->init(param->u.crypt.idx);
5904
5905 if (new_crypt->priv == NULL) {
5906 kfree(new_crypt);
5907 param->u.crypt.err = IPW_CRYPT_ERR_CRYPT_INIT_FAILED;
5908 ret = -EINVAL;
5909 goto done;
5910 }
5911
5912 *crypt = new_crypt;
5913 }
5914
5915 if (param->u.crypt.key_len > 0 && (*crypt)->ops->set_key &&
5916 (*crypt)->ops->set_key(param->u.crypt.key,
5917 param->u.crypt.key_len, param->u.crypt.seq,
5918 (*crypt)->priv) < 0) {
5919 IPW_DEBUG_INFO("%s: key setting failed\n", dev->name);
5920 param->u.crypt.err = IPW_CRYPT_ERR_KEY_SET_FAILED;
5921 ret = -EINVAL;
5922 goto done;
5923 }
5924
5925 if (param->u.crypt.set_tx) {
5926 ieee->tx_keyidx = param->u.crypt.idx;
5927 sec.active_key = param->u.crypt.idx;
5928 sec.flags |= SEC_ACTIVE_KEY;
5929 }
5930
5931 if (ops->name != NULL) {
5932 if (strcmp(ops->name, "WEP") == 0) {
5933 memcpy(sec.keys[param->u.crypt.idx],
5934 param->u.crypt.key, param->u.crypt.key_len);
5935 sec.key_sizes[param->u.crypt.idx] =
5936 param->u.crypt.key_len;
5937 sec.flags |= (1 << param->u.crypt.idx);
5938 sec.flags |= SEC_LEVEL;
5939 sec.level = SEC_LEVEL_1;
5940 } else if (strcmp(ops->name, "TKIP") == 0) {
5941 sec.flags |= SEC_LEVEL;
5942 sec.level = SEC_LEVEL_2;
5943 } else if (strcmp(ops->name, "CCMP") == 0) {
5944 sec.flags |= SEC_LEVEL;
5945 sec.level = SEC_LEVEL_3;
5946 }
5947 }
5948 done:
5949 if (ieee->set_security)
5950 ieee->set_security(ieee->dev, &sec);
5951
5952 /* Do not reset port if card is in Managed mode since resetting will
5953 * generate new IEEE 802.11 authentication which may end up in looping
5954 * with IEEE 802.1X. If your hardware requires a reset after WEP
5955 * configuration (for example... Prism2), implement the reset_port in
5956 * the callbacks structures used to initialize the 802.11 stack. */
5957 if (ieee->reset_on_keychange &&
5958 ieee->iw_mode != IW_MODE_INFRA &&
5959 ieee->reset_port && ieee->reset_port(dev)) {
5960 IPW_DEBUG_INFO("%s: reset_port failed\n", dev->name);
5961 param->u.crypt.err = IPW_CRYPT_ERR_CARD_CONF_FAILED;
5962 return -EINVAL;
5963 }
5964
5965 return ret;
5966}
5967
5968static int ipw_wpa_supplicant(struct net_device *dev, struct iw_point *p)
5969{
5970 struct ipw_param *param;
5971 int ret = 0;
5972
5973 IPW_DEBUG_INFO("wpa_supplicant: len=%d\n", p->length);
5974
5975 if (p->length < sizeof(struct ipw_param) || !p->pointer)
5976 return -EINVAL;
5977
5978 param = (struct ipw_param *)kmalloc(p->length, GFP_KERNEL);
5979 if (param == NULL)
5980 return -ENOMEM;
5981
5982 if (copy_from_user(param, p->pointer, p->length)) {
5983 kfree(param);
5984 return -EFAULT;
5985 }
5986
5987 switch (param->cmd) {
5988
5989 case IPW_CMD_SET_WPA_PARAM:
5990 ret = ipw_wpa_set_param(dev, param->u.wpa_param.name,
5991 param->u.wpa_param.value);
5992 break;
5993
5994 case IPW_CMD_SET_WPA_IE:
5995 ret = ipw_wpa_set_wpa_ie(dev, param, p->length);
5996 break;
5997
5998 case IPW_CMD_SET_ENCRYPTION:
5999 ret = ipw_wpa_set_encryption(dev, param, p->length);
6000 break;
6001
6002 case IPW_CMD_MLME:
6003 ret = ipw_wpa_mlme(dev, param->u.mlme.command,
6004 param->u.mlme.reason_code);
6005 break;
6006
6007 default:
6008 IPW_ERROR("%s: Unknown WPA supplicant request: %d\n",
6009 dev->name, param->cmd);
6010 ret = -EOPNOTSUPP;
6011 }
6012
6013 if (ret == 0 && copy_to_user(p->pointer, param, p->length))
6014 ret = -EFAULT;
6015
6016 kfree(param);
6017 return ret;
6018}
James Ketrenosea2b26e2005-08-24 21:25:16 -05006019
James Ketrenos43f66a62005-03-25 12:31:53 -06006020static int ipw_associate_network(struct ipw_priv *priv,
6021 struct ieee80211_network *network,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04006022 struct ipw_supported_rates *rates, int roaming)
James Ketrenos43f66a62005-03-25 12:31:53 -06006023{
6024 int err;
6025
6026 if (priv->config & CFG_FIXED_RATE)
6027 ipw_set_fixed_rate(priv, network);
6028
6029 if (!(priv->config & CFG_STATIC_ESSID)) {
Jeff Garzikbf794512005-07-31 13:07:26 -04006030 priv->essid_len = min(network->ssid_len,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04006031 (u8) IW_ESSID_MAX_SIZE);
James Ketrenos43f66a62005-03-25 12:31:53 -06006032 memcpy(priv->essid, network->ssid, priv->essid_len);
6033 }
6034
6035 network->last_associate = jiffies;
6036
6037 memset(&priv->assoc_request, 0, sizeof(priv->assoc_request));
6038 priv->assoc_request.channel = network->channel;
6039 if ((priv->capability & CAP_PRIVACY_ON) &&
6040 (priv->capability & CAP_SHARED_KEY)) {
6041 priv->assoc_request.auth_type = AUTH_SHARED_KEY;
6042 priv->assoc_request.auth_key = priv->sec.active_key;
6043 } else {
6044 priv->assoc_request.auth_type = AUTH_OPEN;
6045 priv->assoc_request.auth_key = 0;
6046 }
6047
Jeff Garzikbf794512005-07-31 13:07:26 -04006048 if (priv->capability & CAP_PRIVACY_ON)
James Ketrenos43f66a62005-03-25 12:31:53 -06006049 ipw_send_wep_keys(priv);
6050
James Ketrenosa613bff2005-08-24 21:43:11 -05006051 if (priv->ieee->wpa_ie_len) {
James Ketrenosea2b26e2005-08-24 21:25:16 -05006052 priv->assoc_request.policy_support = 0x02; /* RSN active */
6053 ipw_set_rsn_capa(priv, priv->ieee->wpa_ie,
6054 priv->ieee->wpa_ie_len);
6055 }
James Ketrenosea2b26e2005-08-24 21:25:16 -05006056
Jeff Garzikbf794512005-07-31 13:07:26 -04006057 /*
6058 * It is valid for our ieee device to support multiple modes, but
6059 * when it comes to associating to a given network we have to choose
James Ketrenos43f66a62005-03-25 12:31:53 -06006060 * just one mode.
6061 */
6062 if (network->mode & priv->ieee->mode & IEEE_A)
6063 priv->assoc_request.ieee_mode = IPW_A_MODE;
6064 else if (network->mode & priv->ieee->mode & IEEE_G)
6065 priv->assoc_request.ieee_mode = IPW_G_MODE;
6066 else if (network->mode & priv->ieee->mode & IEEE_B)
6067 priv->assoc_request.ieee_mode = IPW_B_MODE;
6068
James Ketrenosea2b26e2005-08-24 21:25:16 -05006069 priv->assoc_request.capability = network->capability;
6070 if ((network->capability & WLAN_CAPABILITY_SHORT_PREAMBLE)
6071 && !(priv->config & CFG_PREAMBLE_LONG)) {
6072 priv->assoc_request.preamble_length = DCT_FLAG_SHORT_PREAMBLE;
6073 } else {
6074 priv->assoc_request.preamble_length = DCT_FLAG_LONG_PREAMBLE;
6075
6076 /* Clear the short preamble if we won't be supporting it */
6077 priv->assoc_request.capability &=
6078 ~WLAN_CAPABILITY_SHORT_PREAMBLE;
6079 }
6080
James Ketrenos43f66a62005-03-25 12:31:53 -06006081 IPW_DEBUG_ASSOC("%sssocation attempt: '%s', channel %d, "
James Ketrenosea2b26e2005-08-24 21:25:16 -05006082 "802.11%c [%d], %s[:%s], enc=%s%s%s%c%c\n",
James Ketrenos43f66a62005-03-25 12:31:53 -06006083 roaming ? "Rea" : "A",
Jeff Garzikbf794512005-07-31 13:07:26 -04006084 escape_essid(priv->essid, priv->essid_len),
6085 network->channel,
6086 ipw_modes[priv->assoc_request.ieee_mode],
6087 rates->num_rates,
James Ketrenosea2b26e2005-08-24 21:25:16 -05006088 (priv->assoc_request.preamble_length ==
6089 DCT_FLAG_LONG_PREAMBLE) ? "long" : "short",
6090 network->capability &
6091 WLAN_CAPABILITY_SHORT_PREAMBLE ? "short" : "long",
James Ketrenos43f66a62005-03-25 12:31:53 -06006092 priv->capability & CAP_PRIVACY_ON ? "on " : "off",
Jeff Garzikbf794512005-07-31 13:07:26 -04006093 priv->capability & CAP_PRIVACY_ON ?
6094 (priv->capability & CAP_SHARED_KEY ? "(shared)" :
James Ketrenos43f66a62005-03-25 12:31:53 -06006095 "(open)") : "",
6096 priv->capability & CAP_PRIVACY_ON ? " key=" : "",
Jeff Garzikbf794512005-07-31 13:07:26 -04006097 priv->capability & CAP_PRIVACY_ON ?
James Ketrenos43f66a62005-03-25 12:31:53 -06006098 '1' + priv->sec.active_key : '.',
Jeff Garzik0edd5b42005-09-07 00:48:31 -04006099 priv->capability & CAP_PRIVACY_ON ? '.' : ' ');
James Ketrenos43f66a62005-03-25 12:31:53 -06006100
6101 priv->assoc_request.beacon_interval = network->beacon_interval;
6102 if ((priv->ieee->iw_mode == IW_MODE_ADHOC) &&
Jeff Garzik0edd5b42005-09-07 00:48:31 -04006103 (network->time_stamp[0] == 0) && (network->time_stamp[1] == 0)) {
James Ketrenos43f66a62005-03-25 12:31:53 -06006104 priv->assoc_request.assoc_type = HC_IBSS_START;
6105 priv->assoc_request.assoc_tsf_msw = 0;
6106 priv->assoc_request.assoc_tsf_lsw = 0;
6107 } else {
6108 if (unlikely(roaming))
6109 priv->assoc_request.assoc_type = HC_REASSOCIATE;
6110 else
6111 priv->assoc_request.assoc_type = HC_ASSOCIATE;
6112 priv->assoc_request.assoc_tsf_msw = network->time_stamp[1];
6113 priv->assoc_request.assoc_tsf_lsw = network->time_stamp[0];
6114 }
6115
6116 memcpy(&priv->assoc_request.bssid, network->bssid, ETH_ALEN);
6117
6118 if (priv->ieee->iw_mode == IW_MODE_ADHOC) {
6119 memset(&priv->assoc_request.dest, 0xFF, ETH_ALEN);
6120 priv->assoc_request.atim_window = network->atim_window;
6121 } else {
Jeff Garzik0edd5b42005-09-07 00:48:31 -04006122 memcpy(&priv->assoc_request.dest, network->bssid, ETH_ALEN);
James Ketrenos43f66a62005-03-25 12:31:53 -06006123 priv->assoc_request.atim_window = 0;
6124 }
6125
James Ketrenos43f66a62005-03-25 12:31:53 -06006126 priv->assoc_request.listen_interval = network->listen_interval;
Jeff Garzikbf794512005-07-31 13:07:26 -04006127
James Ketrenos43f66a62005-03-25 12:31:53 -06006128 err = ipw_send_ssid(priv, priv->essid, priv->essid_len);
6129 if (err) {
6130 IPW_DEBUG_HC("Attempt to send SSID command failed.\n");
6131 return err;
6132 }
6133
6134 rates->ieee_mode = priv->assoc_request.ieee_mode;
6135 rates->purpose = IPW_RATE_CONNECT;
6136 ipw_send_supported_rates(priv, rates);
Jeff Garzikbf794512005-07-31 13:07:26 -04006137
James Ketrenos43f66a62005-03-25 12:31:53 -06006138 if (priv->assoc_request.ieee_mode == IPW_G_MODE)
6139 priv->sys_config.dot11g_auto_detection = 1;
6140 else
6141 priv->sys_config.dot11g_auto_detection = 0;
James Ketrenosc848d0a2005-08-24 21:56:24 -05006142
6143 if (priv->ieee->iw_mode == IW_MODE_ADHOC)
6144 priv->sys_config.answer_broadcast_ssid_probe = 1;
6145 else
6146 priv->sys_config.answer_broadcast_ssid_probe = 0;
6147
James Ketrenos43f66a62005-03-25 12:31:53 -06006148 err = ipw_send_system_config(priv, &priv->sys_config);
6149 if (err) {
6150 IPW_DEBUG_HC("Attempt to send sys config command failed.\n");
6151 return err;
6152 }
Jeff Garzikbf794512005-07-31 13:07:26 -04006153
James Ketrenos43f66a62005-03-25 12:31:53 -06006154 IPW_DEBUG_ASSOC("Association sensitivity: %d\n", network->stats.rssi);
James Ketrenosea2b26e2005-08-24 21:25:16 -05006155 err = ipw_set_sensitivity(priv, network->stats.rssi + IPW_RSSI_TO_DBM);
James Ketrenos43f66a62005-03-25 12:31:53 -06006156 if (err) {
6157 IPW_DEBUG_HC("Attempt to send associate command failed.\n");
6158 return err;
6159 }
6160
6161 /*
6162 * If preemption is enabled, it is possible for the association
6163 * to complete before we return from ipw_send_associate. Therefore
6164 * we have to be sure and update our priviate data first.
6165 */
6166 priv->channel = network->channel;
6167 memcpy(priv->bssid, network->bssid, ETH_ALEN);
Jeff Garzikbf794512005-07-31 13:07:26 -04006168 priv->status |= STATUS_ASSOCIATING;
James Ketrenos43f66a62005-03-25 12:31:53 -06006169 priv->status &= ~STATUS_SECURITY_UPDATED;
6170
6171 priv->assoc_network = network;
6172
6173 err = ipw_send_associate(priv, &priv->assoc_request);
6174 if (err) {
6175 IPW_DEBUG_HC("Attempt to send associate command failed.\n");
6176 return err;
6177 }
Jeff Garzikbf794512005-07-31 13:07:26 -04006178
6179 IPW_DEBUG(IPW_DL_STATE, "associating: '%s' " MAC_FMT " \n",
James Ketrenos43f66a62005-03-25 12:31:53 -06006180 escape_essid(priv->essid, priv->essid_len),
6181 MAC_ARG(priv->bssid));
6182
6183 return 0;
6184}
6185
6186static void ipw_roam(void *data)
6187{
6188 struct ipw_priv *priv = data;
6189 struct ieee80211_network *network = NULL;
6190 struct ipw_network_match match = {
6191 .network = priv->assoc_network
6192 };
6193
6194 /* The roaming process is as follows:
Jeff Garzikbf794512005-07-31 13:07:26 -04006195 *
6196 * 1. Missed beacon threshold triggers the roaming process by
James Ketrenos43f66a62005-03-25 12:31:53 -06006197 * setting the status ROAM bit and requesting a scan.
6198 * 2. When the scan completes, it schedules the ROAM work
6199 * 3. The ROAM work looks at all of the known networks for one that
6200 * is a better network than the currently associated. If none
6201 * found, the ROAM process is over (ROAM bit cleared)
6202 * 4. If a better network is found, a disassociation request is
6203 * sent.
6204 * 5. When the disassociation completes, the roam work is again
6205 * scheduled. The second time through, the driver is no longer
6206 * associated, and the newly selected network is sent an
Jeff Garzikbf794512005-07-31 13:07:26 -04006207 * association request.
James Ketrenos43f66a62005-03-25 12:31:53 -06006208 * 6. At this point ,the roaming process is complete and the ROAM
6209 * status bit is cleared.
6210 */
6211
6212 /* If we are no longer associated, and the roaming bit is no longer
6213 * set, then we are not actively roaming, so just return */
6214 if (!(priv->status & (STATUS_ASSOCIATED | STATUS_ROAMING)))
6215 return;
Jeff Garzikbf794512005-07-31 13:07:26 -04006216
James Ketrenos43f66a62005-03-25 12:31:53 -06006217 if (priv->status & STATUS_ASSOCIATED) {
Jeff Garzikbf794512005-07-31 13:07:26 -04006218 /* First pass through ROAM process -- look for a better
James Ketrenos43f66a62005-03-25 12:31:53 -06006219 * network */
James Ketrenosa613bff2005-08-24 21:43:11 -05006220 unsigned long flags;
James Ketrenos43f66a62005-03-25 12:31:53 -06006221 u8 rssi = priv->assoc_network->stats.rssi;
6222 priv->assoc_network->stats.rssi = -128;
James Ketrenosa613bff2005-08-24 21:43:11 -05006223 spin_lock_irqsave(&priv->ieee->lock, flags);
James Ketrenos43f66a62005-03-25 12:31:53 -06006224 list_for_each_entry(network, &priv->ieee->network_list, list) {
6225 if (network != priv->assoc_network)
6226 ipw_best_network(priv, &match, network, 1);
6227 }
James Ketrenosa613bff2005-08-24 21:43:11 -05006228 spin_unlock_irqrestore(&priv->ieee->lock, flags);
James Ketrenos43f66a62005-03-25 12:31:53 -06006229 priv->assoc_network->stats.rssi = rssi;
Jeff Garzikbf794512005-07-31 13:07:26 -04006230
James Ketrenos43f66a62005-03-25 12:31:53 -06006231 if (match.network == priv->assoc_network) {
6232 IPW_DEBUG_ASSOC("No better APs in this network to "
6233 "roam to.\n");
6234 priv->status &= ~STATUS_ROAMING;
6235 ipw_debug_config(priv);
6236 return;
6237 }
Jeff Garzikbf794512005-07-31 13:07:26 -04006238
James Ketrenos43f66a62005-03-25 12:31:53 -06006239 ipw_send_disassociate(priv, 1);
6240 priv->assoc_network = match.network;
6241
6242 return;
Jeff Garzikbf794512005-07-31 13:07:26 -04006243 }
James Ketrenos43f66a62005-03-25 12:31:53 -06006244
6245 /* Second pass through ROAM process -- request association */
6246 ipw_compatible_rates(priv, priv->assoc_network, &match.rates);
6247 ipw_associate_network(priv, priv->assoc_network, &match.rates, 1);
6248 priv->status &= ~STATUS_ROAMING;
6249}
6250
James Ketrenosc848d0a2005-08-24 21:56:24 -05006251static void ipw_bg_roam(void *data)
6252{
6253 struct ipw_priv *priv = data;
6254 down(&priv->sem);
6255 ipw_roam(data);
6256 up(&priv->sem);
6257}
6258
6259static int ipw_associate(void *data)
James Ketrenos43f66a62005-03-25 12:31:53 -06006260{
6261 struct ipw_priv *priv = data;
6262
6263 struct ieee80211_network *network = NULL;
6264 struct ipw_network_match match = {
6265 .network = NULL
6266 };
6267 struct ipw_supported_rates *rates;
6268 struct list_head *element;
James Ketrenosa613bff2005-08-24 21:43:11 -05006269 unsigned long flags;
James Ketrenos43f66a62005-03-25 12:31:53 -06006270
James Ketrenosc848d0a2005-08-24 21:56:24 -05006271 if (priv->status & (STATUS_ASSOCIATED | STATUS_ASSOCIATING)) {
6272 IPW_DEBUG_ASSOC
6273 ("Not attempting association (already in progress)\n");
6274 return 0;
6275 }
6276
6277 if (!ipw_is_init(priv) || (priv->status & STATUS_SCANNING)) {
6278 IPW_DEBUG_ASSOC
6279 ("Not attempting association (scanning or not initialized)\n");
6280 return 0;
6281 }
6282
James Ketrenos43f66a62005-03-25 12:31:53 -06006283 if (!(priv->config & CFG_ASSOCIATE) &&
6284 !(priv->config & (CFG_STATIC_ESSID |
Jeff Garzik0edd5b42005-09-07 00:48:31 -04006285 CFG_STATIC_CHANNEL | CFG_STATIC_BSSID))) {
James Ketrenos43f66a62005-03-25 12:31:53 -06006286 IPW_DEBUG_ASSOC("Not attempting association (associate=0)\n");
James Ketrenosc848d0a2005-08-24 21:56:24 -05006287 return 0;
James Ketrenos43f66a62005-03-25 12:31:53 -06006288 }
6289
James Ketrenosa613bff2005-08-24 21:43:11 -05006290 /* Protect our use of the network_list */
6291 spin_lock_irqsave(&priv->ieee->lock, flags);
Jeff Garzikbf794512005-07-31 13:07:26 -04006292 list_for_each_entry(network, &priv->ieee->network_list, list)
Jeff Garzik0edd5b42005-09-07 00:48:31 -04006293 ipw_best_network(priv, &match, network, 0);
James Ketrenos43f66a62005-03-25 12:31:53 -06006294
6295 network = match.network;
6296 rates = &match.rates;
6297
6298 if (network == NULL &&
6299 priv->ieee->iw_mode == IW_MODE_ADHOC &&
6300 priv->config & CFG_ADHOC_CREATE &&
6301 priv->config & CFG_STATIC_ESSID &&
James Ketrenosa613bff2005-08-24 21:43:11 -05006302 priv->config & CFG_STATIC_CHANNEL &&
James Ketrenos43f66a62005-03-25 12:31:53 -06006303 !list_empty(&priv->ieee->network_free_list)) {
6304 element = priv->ieee->network_free_list.next;
Jeff Garzik0edd5b42005-09-07 00:48:31 -04006305 network = list_entry(element, struct ieee80211_network, list);
James Ketrenos43f66a62005-03-25 12:31:53 -06006306 ipw_adhoc_create(priv, network);
6307 rates = &priv->rates;
6308 list_del(element);
6309 list_add_tail(&network->list, &priv->ieee->network_list);
6310 }
James Ketrenosa613bff2005-08-24 21:43:11 -05006311 spin_unlock_irqrestore(&priv->ieee->lock, flags);
Jeff Garzikbf794512005-07-31 13:07:26 -04006312
James Ketrenos43f66a62005-03-25 12:31:53 -06006313 /* If we reached the end of the list, then we don't have any valid
6314 * matching APs */
6315 if (!network) {
6316 ipw_debug_config(priv);
6317
James Ketrenosa613bff2005-08-24 21:43:11 -05006318 if (!(priv->status & STATUS_SCANNING))
6319 queue_delayed_work(priv->workqueue, &priv->request_scan,
6320 SCAN_INTERVAL);
Jeff Garzikbf794512005-07-31 13:07:26 -04006321
James Ketrenosc848d0a2005-08-24 21:56:24 -05006322 return 0;
James Ketrenos43f66a62005-03-25 12:31:53 -06006323 }
6324
6325 ipw_associate_network(priv, network, rates, 0);
James Ketrenosc848d0a2005-08-24 21:56:24 -05006326
6327 return 1;
6328}
6329
6330static void ipw_bg_associate(void *data)
6331{
6332 struct ipw_priv *priv = data;
6333 down(&priv->sem);
6334 ipw_associate(data);
6335 up(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06006336}
Jeff Garzikbf794512005-07-31 13:07:26 -04006337
6338static inline void ipw_handle_data_packet(struct ipw_priv *priv,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04006339 struct ipw_rx_mem_buffer *rxb,
6340 struct ieee80211_rx_stats *stats)
James Ketrenos43f66a62005-03-25 12:31:53 -06006341{
6342 struct ipw_rx_packet *pkt = (struct ipw_rx_packet *)rxb->skb->data;
6343
6344 /* We received data from the HW, so stop the watchdog */
6345 priv->net_dev->trans_start = jiffies;
6346
Jeff Garzikbf794512005-07-31 13:07:26 -04006347 /* We only process data packets if the
James Ketrenos43f66a62005-03-25 12:31:53 -06006348 * interface is open */
James Ketrenosa613bff2005-08-24 21:43:11 -05006349 if (unlikely((le16_to_cpu(pkt->u.frame.length) + IPW_RX_FRAME_SIZE) >
James Ketrenos43f66a62005-03-25 12:31:53 -06006350 skb_tailroom(rxb->skb))) {
6351 priv->ieee->stats.rx_errors++;
6352 priv->wstats.discard.misc++;
6353 IPW_DEBUG_DROP("Corruption detected! Oh no!\n");
6354 return;
6355 } else if (unlikely(!netif_running(priv->net_dev))) {
6356 priv->ieee->stats.rx_dropped++;
6357 priv->wstats.discard.misc++;
6358 IPW_DEBUG_DROP("Dropping packet while interface is not up.\n");
6359 return;
6360 }
6361
6362 /* Advance skb->data to the start of the actual payload */
Jiri Bencaaa4d302005-06-07 14:58:41 +02006363 skb_reserve(rxb->skb, offsetof(struct ipw_rx_packet, u.frame.data));
James Ketrenos43f66a62005-03-25 12:31:53 -06006364
6365 /* Set the size of the skb to the size of the frame */
James Ketrenosa613bff2005-08-24 21:43:11 -05006366 skb_put(rxb->skb, le16_to_cpu(pkt->u.frame.length));
James Ketrenos43f66a62005-03-25 12:31:53 -06006367
6368 IPW_DEBUG_RX("Rx packet of %d bytes.\n", rxb->skb->len);
6369
Jeff Garzikbf794512005-07-31 13:07:26 -04006370 if (!ieee80211_rx(priv->ieee, rxb->skb, stats))
James Ketrenos43f66a62005-03-25 12:31:53 -06006371 priv->ieee->stats.rx_errors++;
James Ketrenosa613bff2005-08-24 21:43:11 -05006372 else { /* ieee80211_rx succeeded, so it now owns the SKB */
James Ketrenos43f66a62005-03-25 12:31:53 -06006373 rxb->skb = NULL;
James Ketrenosa613bff2005-08-24 21:43:11 -05006374 ipw_led_activity_on(priv);
6375 }
James Ketrenos43f66a62005-03-25 12:31:53 -06006376}
6377
James Ketrenosea2b26e2005-08-24 21:25:16 -05006378static inline int is_network_packet(struct ipw_priv *priv,
6379 struct ieee80211_hdr_4addr *header)
6380{
6381 /* Filter incoming packets to determine if they are targetted toward
6382 * this network, discarding packets coming from ourselves */
6383 switch (priv->ieee->iw_mode) {
James Ketrenosa613bff2005-08-24 21:43:11 -05006384 case IW_MODE_ADHOC: /* Header: Dest. | Source | BSSID */
James Ketrenosc848d0a2005-08-24 21:56:24 -05006385 /* packets from our adapter are dropped (echo) */
6386 if (!memcmp(header->addr2, priv->net_dev->dev_addr, ETH_ALEN))
6387 return 0;
6388
James Ketrenosa613bff2005-08-24 21:43:11 -05006389 /* {broad,multi}cast packets to our IBSS go through */
James Ketrenosea2b26e2005-08-24 21:25:16 -05006390 if (is_broadcast_ether_addr(header->addr1) ||
6391 is_multicast_ether_addr(header->addr1))
6392 return !memcmp(header->addr3, priv->bssid, ETH_ALEN);
James Ketrenosa613bff2005-08-24 21:43:11 -05006393
6394 /* packets to our adapter go through */
6395 return !memcmp(header->addr1, priv->net_dev->dev_addr,
6396 ETH_ALEN);
James Ketrenosa613bff2005-08-24 21:43:11 -05006397
6398 case IW_MODE_INFRA: /* Header: Dest. | AP{BSSID} | Source */
James Ketrenosc848d0a2005-08-24 21:56:24 -05006399 /* packets from our adapter are dropped (echo) */
6400 if (!memcmp(header->addr3, priv->net_dev->dev_addr, ETH_ALEN))
6401 return 0;
6402
James Ketrenosa613bff2005-08-24 21:43:11 -05006403 /* {broad,multi}cast packets to our IBSS go through */
6404 if (is_broadcast_ether_addr(header->addr1) ||
6405 is_multicast_ether_addr(header->addr1))
6406 return !memcmp(header->addr2, priv->bssid, ETH_ALEN);
6407
6408 /* packets to our adapter go through */
6409 return !memcmp(header->addr1, priv->net_dev->dev_addr,
6410 ETH_ALEN);
James Ketrenosea2b26e2005-08-24 21:25:16 -05006411 }
James Ketrenosa613bff2005-08-24 21:43:11 -05006412
James Ketrenosea2b26e2005-08-24 21:25:16 -05006413 return 1;
6414}
6415
James Ketrenos43f66a62005-03-25 12:31:53 -06006416/*
6417 * Main entry function for recieving a packet with 80211 headers. This
6418 * should be called when ever the FW has notified us that there is a new
6419 * skb in the recieve queue.
6420 */
6421static void ipw_rx(struct ipw_priv *priv)
6422{
6423 struct ipw_rx_mem_buffer *rxb;
6424 struct ipw_rx_packet *pkt;
James Ketrenos0dacca12005-09-21 12:23:41 -05006425 struct ieee80211_hdr_4addr *header;
James Ketrenos43f66a62005-03-25 12:31:53 -06006426 u32 r, w, i;
6427 u8 network_packet;
6428
6429 r = ipw_read32(priv, CX2_RX_READ_INDEX);
6430 w = ipw_read32(priv, CX2_RX_WRITE_INDEX);
6431 i = (priv->rxq->processed + 1) % RX_QUEUE_SIZE;
6432
6433 while (i != r) {
6434 rxb = priv->rxq->queue[i];
6435#ifdef CONFIG_IPW_DEBUG
6436 if (unlikely(rxb == NULL)) {
6437 printk(KERN_CRIT "Queue not allocated!\n");
6438 break;
6439 }
6440#endif
6441 priv->rxq->queue[i] = NULL;
6442
6443 pci_dma_sync_single_for_cpu(priv->pci_dev, rxb->dma_addr,
Jeff Garzikbf794512005-07-31 13:07:26 -04006444 CX2_RX_BUF_SIZE,
James Ketrenos43f66a62005-03-25 12:31:53 -06006445 PCI_DMA_FROMDEVICE);
6446
6447 pkt = (struct ipw_rx_packet *)rxb->skb->data;
6448 IPW_DEBUG_RX("Packet: type=%02X seq=%02X bits=%02X\n",
6449 pkt->header.message_type,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04006450 pkt->header.rx_seq_num, pkt->header.control_bits);
James Ketrenos43f66a62005-03-25 12:31:53 -06006451
6452 switch (pkt->header.message_type) {
Jeff Garzik0edd5b42005-09-07 00:48:31 -04006453 case RX_FRAME_TYPE: /* 802.11 frame */ {
6454 struct ieee80211_rx_stats stats = {
James Ketrenosc848d0a2005-08-24 21:56:24 -05006455 .rssi =
6456 le16_to_cpu(pkt->u.frame.rssi_dbm) -
Jeff Garzik0edd5b42005-09-07 00:48:31 -04006457 IPW_RSSI_TO_DBM,
James Ketrenosc848d0a2005-08-24 21:56:24 -05006458 .signal =
6459 le16_to_cpu(pkt->u.frame.signal),
6460 .noise =
6461 le16_to_cpu(pkt->u.frame.noise),
Jeff Garzik0edd5b42005-09-07 00:48:31 -04006462 .rate = pkt->u.frame.rate,
6463 .mac_time = jiffies,
6464 .received_channel =
6465 pkt->u.frame.received_channel,
6466 .freq =
6467 (pkt->u.frame.
6468 control & (1 << 0)) ?
6469 IEEE80211_24GHZ_BAND :
6470 IEEE80211_52GHZ_BAND,
James Ketrenosa613bff2005-08-24 21:43:11 -05006471 .len = le16_to_cpu(pkt->u.frame.length),
Jeff Garzik0edd5b42005-09-07 00:48:31 -04006472 };
James Ketrenos43f66a62005-03-25 12:31:53 -06006473
Jeff Garzik0edd5b42005-09-07 00:48:31 -04006474 if (stats.rssi != 0)
6475 stats.mask |= IEEE80211_STATMASK_RSSI;
6476 if (stats.signal != 0)
6477 stats.mask |= IEEE80211_STATMASK_SIGNAL;
James Ketrenosc848d0a2005-08-24 21:56:24 -05006478 if (stats.noise != 0)
6479 stats.mask |= IEEE80211_STATMASK_NOISE;
Jeff Garzik0edd5b42005-09-07 00:48:31 -04006480 if (stats.rate != 0)
6481 stats.mask |= IEEE80211_STATMASK_RATE;
James Ketrenos43f66a62005-03-25 12:31:53 -06006482
Jeff Garzik0edd5b42005-09-07 00:48:31 -04006483 priv->rx_packets++;
James Ketrenos43f66a62005-03-25 12:31:53 -06006484
James Ketrenosea2b26e2005-08-24 21:25:16 -05006485#ifdef CONFIG_IPW_MONITOR
Jeff Garzik0edd5b42005-09-07 00:48:31 -04006486 if (priv->ieee->iw_mode == IW_MODE_MONITOR) {
6487 ipw_handle_data_packet(priv, rxb,
6488 &stats);
6489 break;
6490 }
James Ketrenos43f66a62005-03-25 12:31:53 -06006491#endif
Jeff Garzikbf794512005-07-31 13:07:26 -04006492
Jeff Garzik0edd5b42005-09-07 00:48:31 -04006493 header =
James Ketrenos0dacca12005-09-21 12:23:41 -05006494 (struct ieee80211_hdr_4addr *)(rxb->skb->
6495 data +
6496 IPW_RX_FRAME_SIZE);
James Ketrenos43f66a62005-03-25 12:31:53 -06006497 /* TODO: Check Ad-Hoc dest/source and make sure
6498 * that we are actually parsing these packets
Jeff Garzikbf794512005-07-31 13:07:26 -04006499 * correctly -- we should probably use the
James Ketrenos43f66a62005-03-25 12:31:53 -06006500 * frame control of the packet and disregard
6501 * the current iw_mode */
James Ketrenos43f66a62005-03-25 12:31:53 -06006502
James Ketrenosea2b26e2005-08-24 21:25:16 -05006503 network_packet =
6504 is_network_packet(priv, header);
Jeff Garzik0edd5b42005-09-07 00:48:31 -04006505 if (network_packet && priv->assoc_network) {
6506 priv->assoc_network->stats.rssi =
6507 stats.rssi;
6508 average_add(&priv->average_rssi,
6509 stats.rssi);
6510 priv->last_rx_rssi = stats.rssi;
6511 }
6512
6513 IPW_DEBUG_RX("Frame: len=%u\n",
James Ketrenosa613bff2005-08-24 21:43:11 -05006514 le16_to_cpu(pkt->u.frame.length));
Jeff Garzik0edd5b42005-09-07 00:48:31 -04006515
James Ketrenosa613bff2005-08-24 21:43:11 -05006516 if (le16_to_cpu(pkt->u.frame.length) <
6517 frame_hdr_len(header)) {
Jeff Garzik0edd5b42005-09-07 00:48:31 -04006518 IPW_DEBUG_DROP
6519 ("Received packet is too small. "
6520 "Dropping.\n");
6521 priv->ieee->stats.rx_errors++;
6522 priv->wstats.discard.misc++;
6523 break;
6524 }
6525
James Ketrenosa613bff2005-08-24 21:43:11 -05006526 switch (WLAN_FC_GET_TYPE
6527 (le16_to_cpu(header->frame_ctl))) {
Jeff Garzik0edd5b42005-09-07 00:48:31 -04006528 case IEEE80211_FTYPE_MGMT:
6529 ieee80211_rx_mgt(priv->ieee, header,
6530 &stats);
6531 if (priv->ieee->iw_mode == IW_MODE_ADHOC
6532 &&
6533 ((WLAN_FC_GET_STYPE
James Ketrenosa613bff2005-08-24 21:43:11 -05006534 (le16_to_cpu(header->frame_ctl))
6535 == IEEE80211_STYPE_PROBE_RESP)
Jeff Garzik0edd5b42005-09-07 00:48:31 -04006536 ||
6537 (WLAN_FC_GET_STYPE
James Ketrenosa613bff2005-08-24 21:43:11 -05006538 (le16_to_cpu(header->frame_ctl))
James Ketrenosc848d0a2005-08-24 21:56:24 -05006539 == IEEE80211_STYPE_BEACON))) {
6540 if (!memcmp
6541 (header->addr3, priv->bssid,
6542 ETH_ALEN))
6543 ipw_add_station(priv,
6544 header->
6545 addr2);
6546 else {
6547 struct
6548 ieee80211_probe_response
6549 *beacon;
6550 beacon =
6551 (struct
6552 ieee80211_probe_response
6553 *)header;
6554 if (le16_to_cpu
6555 (beacon->
6556 capability) &
6557 WLAN_CAPABILITY_IBSS)
6558 {
6559 queue_work
6560 (priv->
6561 workqueue,
6562 &priv->
6563 merge_networks);
6564 }
6565 }
6566 }
Jeff Garzik0edd5b42005-09-07 00:48:31 -04006567 break;
6568
6569 case IEEE80211_FTYPE_CTL:
6570 break;
6571
6572 case IEEE80211_FTYPE_DATA:
6573 if (network_packet)
6574 ipw_handle_data_packet(priv,
6575 rxb,
6576 &stats);
6577 else
6578 IPW_DEBUG_DROP("Dropping: "
6579 MAC_FMT ", "
6580 MAC_FMT ", "
6581 MAC_FMT "\n",
6582 MAC_ARG(header->
6583 addr1),
6584 MAC_ARG(header->
6585 addr2),
6586 MAC_ARG(header->
6587 addr3));
6588 break;
6589 }
James Ketrenos43f66a62005-03-25 12:31:53 -06006590 break;
6591 }
Jeff Garzikbf794512005-07-31 13:07:26 -04006592
Jeff Garzik0edd5b42005-09-07 00:48:31 -04006593 case RX_HOST_NOTIFICATION_TYPE:{
6594 IPW_DEBUG_RX
6595 ("Notification: subtype=%02X flags=%02X size=%d\n",
James Ketrenos43f66a62005-03-25 12:31:53 -06006596 pkt->u.notification.subtype,
6597 pkt->u.notification.flags,
6598 pkt->u.notification.size);
Jeff Garzik0edd5b42005-09-07 00:48:31 -04006599 ipw_rx_notification(priv, &pkt->u.notification);
6600 break;
6601 }
James Ketrenos43f66a62005-03-25 12:31:53 -06006602
6603 default:
6604 IPW_DEBUG_RX("Bad Rx packet of type %d\n",
6605 pkt->header.message_type);
6606 break;
6607 }
Jeff Garzikbf794512005-07-31 13:07:26 -04006608
6609 /* For now we just don't re-use anything. We can tweak this
6610 * later to try and re-use notification packets and SKBs that
James Ketrenos43f66a62005-03-25 12:31:53 -06006611 * fail to Rx correctly */
6612 if (rxb->skb != NULL) {
6613 dev_kfree_skb_any(rxb->skb);
6614 rxb->skb = NULL;
6615 }
Jeff Garzikbf794512005-07-31 13:07:26 -04006616
James Ketrenos43f66a62005-03-25 12:31:53 -06006617 pci_unmap_single(priv->pci_dev, rxb->dma_addr,
6618 CX2_RX_BUF_SIZE, PCI_DMA_FROMDEVICE);
6619 list_add_tail(&rxb->list, &priv->rxq->rx_used);
Jeff Garzikbf794512005-07-31 13:07:26 -04006620
James Ketrenos43f66a62005-03-25 12:31:53 -06006621 i = (i + 1) % RX_QUEUE_SIZE;
6622 }
6623
6624 /* Backtrack one entry */
6625 priv->rxq->processed = (i ? i : RX_QUEUE_SIZE) - 1;
6626
6627 ipw_rx_queue_restock(priv);
6628}
6629
James Ketrenos43f66a62005-03-25 12:31:53 -06006630/*
6631 * This file defines the Wireless Extension handlers. It does not
6632 * define any methods of hardware manipulation and relies on the
6633 * functions defined in ipw_main to provide the HW interaction.
Jeff Garzikbf794512005-07-31 13:07:26 -04006634 *
6635 * The exception to this is the use of the ipw_get_ordinal()
James Ketrenos43f66a62005-03-25 12:31:53 -06006636 * function used to poll the hardware vs. making unecessary calls.
6637 *
6638 */
6639
Jeff Garzikbf794512005-07-31 13:07:26 -04006640static int ipw_wx_get_name(struct net_device *dev,
6641 struct iw_request_info *info,
James Ketrenos43f66a62005-03-25 12:31:53 -06006642 union iwreq_data *wrqu, char *extra)
6643{
6644 struct ipw_priv *priv = ieee80211_priv(dev);
James Ketrenosc848d0a2005-08-24 21:56:24 -05006645 down(&priv->sem);
6646 if (priv->status & STATUS_RF_KILL_MASK)
James Ketrenosa613bff2005-08-24 21:43:11 -05006647 strcpy(wrqu->name, "radio off");
James Ketrenosc848d0a2005-08-24 21:56:24 -05006648 else if (!(priv->status & STATUS_ASSOCIATED))
James Ketrenos43f66a62005-03-25 12:31:53 -06006649 strcpy(wrqu->name, "unassociated");
Jeff Garzikbf794512005-07-31 13:07:26 -04006650 else
James Ketrenos43f66a62005-03-25 12:31:53 -06006651 snprintf(wrqu->name, IFNAMSIZ, "IEEE 802.11%c",
6652 ipw_modes[priv->assoc_request.ieee_mode]);
6653 IPW_DEBUG_WX("Name: %s\n", wrqu->name);
James Ketrenosc848d0a2005-08-24 21:56:24 -05006654 up(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06006655 return 0;
6656}
6657
6658static int ipw_set_channel(struct ipw_priv *priv, u8 channel)
6659{
6660 if (channel == 0) {
6661 IPW_DEBUG_INFO("Setting channel to ANY (0)\n");
6662 priv->config &= ~CFG_STATIC_CHANNEL;
James Ketrenosc848d0a2005-08-24 21:56:24 -05006663 IPW_DEBUG_ASSOC("Attempting to associate with new "
6664 "parameters.\n");
6665 ipw_associate(priv);
James Ketrenos43f66a62005-03-25 12:31:53 -06006666 return 0;
6667 }
6668
6669 priv->config |= CFG_STATIC_CHANNEL;
6670
6671 if (priv->channel == channel) {
Jeff Garzik0edd5b42005-09-07 00:48:31 -04006672 IPW_DEBUG_INFO("Request to set channel to current value (%d)\n",
6673 channel);
James Ketrenos43f66a62005-03-25 12:31:53 -06006674 return 0;
6675 }
6676
6677 IPW_DEBUG_INFO("Setting channel to %i\n", (int)channel);
6678 priv->channel = channel;
6679
James Ketrenosc848d0a2005-08-24 21:56:24 -05006680 /* Network configuration changed -- force [re]association */
6681 IPW_DEBUG_ASSOC("[re]association triggered due to channel change.\n");
6682 if (!ipw_disassociate(priv))
James Ketrenos43f66a62005-03-25 12:31:53 -06006683 ipw_associate(priv);
James Ketrenos43f66a62005-03-25 12:31:53 -06006684
6685 return 0;
6686}
6687
Jeff Garzikbf794512005-07-31 13:07:26 -04006688static int ipw_wx_set_freq(struct net_device *dev,
6689 struct iw_request_info *info,
6690 union iwreq_data *wrqu, char *extra)
James Ketrenos43f66a62005-03-25 12:31:53 -06006691{
6692 struct ipw_priv *priv = ieee80211_priv(dev);
6693 struct iw_freq *fwrq = &wrqu->freq;
James Ketrenosc848d0a2005-08-24 21:56:24 -05006694 int ret = 0;
Jeff Garzikbf794512005-07-31 13:07:26 -04006695
James Ketrenos43f66a62005-03-25 12:31:53 -06006696 /* if setting by freq convert to channel */
6697 if (fwrq->e == 1) {
Jeff Garzik0edd5b42005-09-07 00:48:31 -04006698 if ((fwrq->m >= (int)2.412e8 && fwrq->m <= (int)2.487e8)) {
James Ketrenos43f66a62005-03-25 12:31:53 -06006699 int f = fwrq->m / 100000;
6700 int c = 0;
Jeff Garzikbf794512005-07-31 13:07:26 -04006701
James Ketrenos43f66a62005-03-25 12:31:53 -06006702 while ((c < REG_MAX_CHANNEL) &&
6703 (f != ipw_frequencies[c]))
6704 c++;
Jeff Garzikbf794512005-07-31 13:07:26 -04006705
James Ketrenos43f66a62005-03-25 12:31:53 -06006706 /* hack to fall through */
6707 fwrq->e = 0;
6708 fwrq->m = c + 1;
6709 }
6710 }
Jeff Garzikbf794512005-07-31 13:07:26 -04006711
6712 if (fwrq->e > 0 || fwrq->m > 1000)
James Ketrenos43f66a62005-03-25 12:31:53 -06006713 return -EOPNOTSUPP;
6714
6715 IPW_DEBUG_WX("SET Freq/Channel -> %d \n", fwrq->m);
James Ketrenosc848d0a2005-08-24 21:56:24 -05006716 down(&priv->sem);
6717 ret = ipw_set_channel(priv, (u8) fwrq->m);
6718 up(&priv->sem);
6719 return ret;
James Ketrenos43f66a62005-03-25 12:31:53 -06006720}
6721
Jeff Garzikbf794512005-07-31 13:07:26 -04006722static int ipw_wx_get_freq(struct net_device *dev,
6723 struct iw_request_info *info,
James Ketrenos43f66a62005-03-25 12:31:53 -06006724 union iwreq_data *wrqu, char *extra)
6725{
6726 struct ipw_priv *priv = ieee80211_priv(dev);
6727
6728 wrqu->freq.e = 0;
6729
6730 /* If we are associated, trying to associate, or have a statically
6731 * configured CHANNEL then return that; otherwise return ANY */
James Ketrenosc848d0a2005-08-24 21:56:24 -05006732 down(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06006733 if (priv->config & CFG_STATIC_CHANNEL ||
6734 priv->status & (STATUS_ASSOCIATING | STATUS_ASSOCIATED))
6735 wrqu->freq.m = priv->channel;
Jeff Garzikbf794512005-07-31 13:07:26 -04006736 else
James Ketrenos43f66a62005-03-25 12:31:53 -06006737 wrqu->freq.m = 0;
6738
James Ketrenosc848d0a2005-08-24 21:56:24 -05006739 up(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06006740 IPW_DEBUG_WX("GET Freq/Channel -> %d \n", priv->channel);
6741 return 0;
6742}
6743
Jeff Garzikbf794512005-07-31 13:07:26 -04006744static int ipw_wx_set_mode(struct net_device *dev,
6745 struct iw_request_info *info,
James Ketrenos43f66a62005-03-25 12:31:53 -06006746 union iwreq_data *wrqu, char *extra)
6747{
6748 struct ipw_priv *priv = ieee80211_priv(dev);
6749 int err = 0;
6750
6751 IPW_DEBUG_WX("Set MODE: %d\n", wrqu->mode);
James Ketrenosc848d0a2005-08-24 21:56:24 -05006752 down(&priv->sem);
6753 if (wrqu->mode == priv->ieee->iw_mode) {
6754 up(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06006755 return 0;
James Ketrenosc848d0a2005-08-24 21:56:24 -05006756 }
James Ketrenos43f66a62005-03-25 12:31:53 -06006757
6758 switch (wrqu->mode) {
James Ketrenosea2b26e2005-08-24 21:25:16 -05006759#ifdef CONFIG_IPW_MONITOR
James Ketrenos43f66a62005-03-25 12:31:53 -06006760 case IW_MODE_MONITOR:
6761#endif
6762 case IW_MODE_ADHOC:
6763 case IW_MODE_INFRA:
6764 break;
6765 case IW_MODE_AUTO:
6766 wrqu->mode = IW_MODE_INFRA;
6767 break;
6768 default:
James Ketrenosc848d0a2005-08-24 21:56:24 -05006769 up(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06006770 return -EINVAL;
6771 }
6772
James Ketrenosea2b26e2005-08-24 21:25:16 -05006773#ifdef CONFIG_IPW_MONITOR
Jeff Garzikbf794512005-07-31 13:07:26 -04006774 if (priv->ieee->iw_mode == IW_MODE_MONITOR)
James Ketrenos43f66a62005-03-25 12:31:53 -06006775 priv->net_dev->type = ARPHRD_ETHER;
Jeff Garzikbf794512005-07-31 13:07:26 -04006776
6777 if (wrqu->mode == IW_MODE_MONITOR)
James Ketrenos43f66a62005-03-25 12:31:53 -06006778 priv->net_dev->type = ARPHRD_IEEE80211;
James Ketrenosea2b26e2005-08-24 21:25:16 -05006779#endif /* CONFIG_IPW_MONITOR */
Jeff Garzikbf794512005-07-31 13:07:26 -04006780
James Ketrenos43f66a62005-03-25 12:31:53 -06006781#ifdef CONFIG_PM
Jeff Garzikbf794512005-07-31 13:07:26 -04006782 /* Free the existing firmware and reset the fw_loaded
James Ketrenos43f66a62005-03-25 12:31:53 -06006783 * flag so ipw_load() will bring in the new firmawre */
James Ketrenosa613bff2005-08-24 21:43:11 -05006784 if (fw_loaded)
James Ketrenos43f66a62005-03-25 12:31:53 -06006785 fw_loaded = 0;
James Ketrenos43f66a62005-03-25 12:31:53 -06006786
6787 release_firmware(bootfw);
6788 release_firmware(ucode);
6789 release_firmware(firmware);
6790 bootfw = ucode = firmware = NULL;
6791#endif
6792
6793 priv->ieee->iw_mode = wrqu->mode;
Jeff Garzikbf794512005-07-31 13:07:26 -04006794
James Ketrenosc848d0a2005-08-24 21:56:24 -05006795 queue_work(priv->workqueue, &priv->adapter_restart);
6796 up(&priv->sem);
Jeff Garzik0edd5b42005-09-07 00:48:31 -04006797 return err;
James Ketrenos43f66a62005-03-25 12:31:53 -06006798}
6799
Jeff Garzikbf794512005-07-31 13:07:26 -04006800static int ipw_wx_get_mode(struct net_device *dev,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04006801 struct iw_request_info *info,
6802 union iwreq_data *wrqu, char *extra)
James Ketrenos43f66a62005-03-25 12:31:53 -06006803{
6804 struct ipw_priv *priv = ieee80211_priv(dev);
James Ketrenosc848d0a2005-08-24 21:56:24 -05006805 down(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06006806 wrqu->mode = priv->ieee->iw_mode;
6807 IPW_DEBUG_WX("Get MODE -> %d\n", wrqu->mode);
James Ketrenosc848d0a2005-08-24 21:56:24 -05006808 up(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06006809 return 0;
6810}
6811
James Ketrenos43f66a62005-03-25 12:31:53 -06006812#define DEFAULT_RTS_THRESHOLD 2304U
6813#define MIN_RTS_THRESHOLD 1U
6814#define MAX_RTS_THRESHOLD 2304U
6815#define DEFAULT_BEACON_INTERVAL 100U
6816#define DEFAULT_SHORT_RETRY_LIMIT 7U
6817#define DEFAULT_LONG_RETRY_LIMIT 4U
6818
6819/* Values are in microsecond */
6820static const s32 timeout_duration[] = {
6821 350000,
6822 250000,
6823 75000,
6824 37000,
6825 25000,
6826};
6827
6828static const s32 period_duration[] = {
6829 400000,
6830 700000,
6831 1000000,
6832 1000000,
6833 1000000
6834};
6835
Jeff Garzikbf794512005-07-31 13:07:26 -04006836static int ipw_wx_get_range(struct net_device *dev,
6837 struct iw_request_info *info,
James Ketrenos43f66a62005-03-25 12:31:53 -06006838 union iwreq_data *wrqu, char *extra)
6839{
6840 struct ipw_priv *priv = ieee80211_priv(dev);
6841 struct iw_range *range = (struct iw_range *)extra;
6842 u16 val;
6843 int i;
6844
6845 wrqu->data.length = sizeof(*range);
6846 memset(range, 0, sizeof(*range));
6847
6848 /* 54Mbs == ~27 Mb/s real (802.11g) */
Jeff Garzikbf794512005-07-31 13:07:26 -04006849 range->throughput = 27 * 1000 * 1000;
James Ketrenos43f66a62005-03-25 12:31:53 -06006850
6851 range->max_qual.qual = 100;
6852 /* TODO: Find real max RSSI and stick here */
6853 range->max_qual.level = 0;
James Ketrenosc848d0a2005-08-24 21:56:24 -05006854 range->max_qual.noise = priv->ieee->worst_rssi + 0x100;
Jeff Garzik0edd5b42005-09-07 00:48:31 -04006855 range->max_qual.updated = 7; /* Updated all three */
James Ketrenos43f66a62005-03-25 12:31:53 -06006856
6857 range->avg_qual.qual = 70;
6858 /* TODO: Find real 'good' to 'bad' threshol value for RSSI */
Jeff Garzik0edd5b42005-09-07 00:48:31 -04006859 range->avg_qual.level = 0; /* FIXME to real average level */
James Ketrenos43f66a62005-03-25 12:31:53 -06006860 range->avg_qual.noise = 0;
Jeff Garzik0edd5b42005-09-07 00:48:31 -04006861 range->avg_qual.updated = 7; /* Updated all three */
James Ketrenosc848d0a2005-08-24 21:56:24 -05006862 down(&priv->sem);
Jeff Garzik0edd5b42005-09-07 00:48:31 -04006863 range->num_bitrates = min(priv->rates.num_rates, (u8) IW_MAX_BITRATES);
James Ketrenos43f66a62005-03-25 12:31:53 -06006864
Jeff Garzikbf794512005-07-31 13:07:26 -04006865 for (i = 0; i < range->num_bitrates; i++)
6866 range->bitrate[i] = (priv->rates.supported_rates[i] & 0x7F) *
Jeff Garzik0edd5b42005-09-07 00:48:31 -04006867 500000;
Jeff Garzikbf794512005-07-31 13:07:26 -04006868
James Ketrenos43f66a62005-03-25 12:31:53 -06006869 range->max_rts = DEFAULT_RTS_THRESHOLD;
6870 range->min_frag = MIN_FRAG_THRESHOLD;
6871 range->max_frag = MAX_FRAG_THRESHOLD;
6872
6873 range->encoding_size[0] = 5;
Jeff Garzikbf794512005-07-31 13:07:26 -04006874 range->encoding_size[1] = 13;
James Ketrenos43f66a62005-03-25 12:31:53 -06006875 range->num_encoding_sizes = 2;
6876 range->max_encoding_tokens = WEP_KEYS;
6877
6878 /* Set the Wireless Extension versions */
6879 range->we_version_compiled = WIRELESS_EXT;
6880 range->we_version_source = 16;
6881
Jeff Garzik0edd5b42005-09-07 00:48:31 -04006882 range->num_channels = FREQ_COUNT;
James Ketrenos43f66a62005-03-25 12:31:53 -06006883
6884 val = 0;
6885 for (i = 0; i < FREQ_COUNT; i++) {
6886 range->freq[val].i = i + 1;
6887 range->freq[val].m = ipw_frequencies[i] * 100000;
6888 range->freq[val].e = 1;
6889 val++;
6890
6891 if (val == IW_MAX_FREQUENCIES)
6892 break;
6893 }
6894 range->num_frequency = val;
James Ketrenosc848d0a2005-08-24 21:56:24 -05006895 up(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06006896 IPW_DEBUG_WX("GET Range\n");
6897 return 0;
6898}
6899
Jeff Garzikbf794512005-07-31 13:07:26 -04006900static int ipw_wx_set_wap(struct net_device *dev,
6901 struct iw_request_info *info,
James Ketrenos43f66a62005-03-25 12:31:53 -06006902 union iwreq_data *wrqu, char *extra)
6903{
6904 struct ipw_priv *priv = ieee80211_priv(dev);
6905
6906 static const unsigned char any[] = {
6907 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
6908 };
6909 static const unsigned char off[] = {
6910 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
6911 };
6912
Jeff Garzikbf794512005-07-31 13:07:26 -04006913 if (wrqu->ap_addr.sa_family != ARPHRD_ETHER)
James Ketrenos43f66a62005-03-25 12:31:53 -06006914 return -EINVAL;
James Ketrenosc848d0a2005-08-24 21:56:24 -05006915 down(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06006916 if (!memcmp(any, wrqu->ap_addr.sa_data, ETH_ALEN) ||
6917 !memcmp(off, wrqu->ap_addr.sa_data, ETH_ALEN)) {
6918 /* we disable mandatory BSSID association */
6919 IPW_DEBUG_WX("Setting AP BSSID to ANY\n");
6920 priv->config &= ~CFG_STATIC_BSSID;
James Ketrenosc848d0a2005-08-24 21:56:24 -05006921 IPW_DEBUG_ASSOC("Attempting to associate with new "
6922 "parameters.\n");
6923 ipw_associate(priv);
6924 up(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06006925 return 0;
6926 }
6927
6928 priv->config |= CFG_STATIC_BSSID;
6929 if (!memcmp(priv->bssid, wrqu->ap_addr.sa_data, ETH_ALEN)) {
6930 IPW_DEBUG_WX("BSSID set to current BSSID.\n");
James Ketrenosc848d0a2005-08-24 21:56:24 -05006931 up(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06006932 return 0;
6933 }
6934
6935 IPW_DEBUG_WX("Setting mandatory BSSID to " MAC_FMT "\n",
6936 MAC_ARG(wrqu->ap_addr.sa_data));
6937
6938 memcpy(priv->bssid, wrqu->ap_addr.sa_data, ETH_ALEN);
6939
James Ketrenosc848d0a2005-08-24 21:56:24 -05006940 /* Network configuration changed -- force [re]association */
6941 IPW_DEBUG_ASSOC("[re]association triggered due to BSSID change.\n");
6942 if (!ipw_disassociate(priv))
James Ketrenos43f66a62005-03-25 12:31:53 -06006943 ipw_associate(priv);
James Ketrenos43f66a62005-03-25 12:31:53 -06006944
James Ketrenosc848d0a2005-08-24 21:56:24 -05006945 up(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06006946 return 0;
6947}
6948
Jeff Garzikbf794512005-07-31 13:07:26 -04006949static int ipw_wx_get_wap(struct net_device *dev,
6950 struct iw_request_info *info,
James Ketrenos43f66a62005-03-25 12:31:53 -06006951 union iwreq_data *wrqu, char *extra)
6952{
6953 struct ipw_priv *priv = ieee80211_priv(dev);
6954 /* If we are associated, trying to associate, or have a statically
6955 * configured BSSID then return that; otherwise return ANY */
James Ketrenosc848d0a2005-08-24 21:56:24 -05006956 down(&priv->sem);
Jeff Garzikbf794512005-07-31 13:07:26 -04006957 if (priv->config & CFG_STATIC_BSSID ||
James Ketrenos43f66a62005-03-25 12:31:53 -06006958 priv->status & (STATUS_ASSOCIATED | STATUS_ASSOCIATING)) {
6959 wrqu->ap_addr.sa_family = ARPHRD_ETHER;
6960 memcpy(wrqu->ap_addr.sa_data, &priv->bssid, ETH_ALEN);
6961 } else
6962 memset(wrqu->ap_addr.sa_data, 0, ETH_ALEN);
6963
6964 IPW_DEBUG_WX("Getting WAP BSSID: " MAC_FMT "\n",
6965 MAC_ARG(wrqu->ap_addr.sa_data));
James Ketrenosc848d0a2005-08-24 21:56:24 -05006966 up(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06006967 return 0;
6968}
6969
Jeff Garzikbf794512005-07-31 13:07:26 -04006970static int ipw_wx_set_essid(struct net_device *dev,
6971 struct iw_request_info *info,
James Ketrenos43f66a62005-03-25 12:31:53 -06006972 union iwreq_data *wrqu, char *extra)
6973{
6974 struct ipw_priv *priv = ieee80211_priv(dev);
Jeff Garzik0edd5b42005-09-07 00:48:31 -04006975 char *essid = ""; /* ANY */
James Ketrenos43f66a62005-03-25 12:31:53 -06006976 int length = 0;
James Ketrenosc848d0a2005-08-24 21:56:24 -05006977 down(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06006978 if (wrqu->essid.flags && wrqu->essid.length) {
6979 length = wrqu->essid.length - 1;
6980 essid = extra;
6981 }
6982 if (length == 0) {
6983 IPW_DEBUG_WX("Setting ESSID to ANY\n");
6984 priv->config &= ~CFG_STATIC_ESSID;
James Ketrenosc848d0a2005-08-24 21:56:24 -05006985 if (!(priv->status & (STATUS_ASSOCIATED | STATUS_ASSOCIATING))) {
James Ketrenos43f66a62005-03-25 12:31:53 -06006986 IPW_DEBUG_ASSOC("Attempting to associate with new "
6987 "parameters.\n");
6988 ipw_associate(priv);
6989 }
James Ketrenosc848d0a2005-08-24 21:56:24 -05006990 up(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06006991 return 0;
6992 }
6993
6994 length = min(length, IW_ESSID_MAX_SIZE);
6995
6996 priv->config |= CFG_STATIC_ESSID;
6997
6998 if (priv->essid_len == length && !memcmp(priv->essid, extra, length)) {
6999 IPW_DEBUG_WX("ESSID set to current ESSID.\n");
James Ketrenosc848d0a2005-08-24 21:56:24 -05007000 up(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06007001 return 0;
7002 }
7003
7004 IPW_DEBUG_WX("Setting ESSID: '%s' (%d)\n", escape_essid(essid, length),
7005 length);
7006
7007 priv->essid_len = length;
7008 memcpy(priv->essid, essid, priv->essid_len);
Jeff Garzikbf794512005-07-31 13:07:26 -04007009
James Ketrenosc848d0a2005-08-24 21:56:24 -05007010 /* Network configuration changed -- force [re]association */
7011 IPW_DEBUG_ASSOC("[re]association triggered due to ESSID change.\n");
7012 if (!ipw_disassociate(priv))
James Ketrenos43f66a62005-03-25 12:31:53 -06007013 ipw_associate(priv);
James Ketrenos43f66a62005-03-25 12:31:53 -06007014
James Ketrenosc848d0a2005-08-24 21:56:24 -05007015 up(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06007016 return 0;
7017}
7018
Jeff Garzikbf794512005-07-31 13:07:26 -04007019static int ipw_wx_get_essid(struct net_device *dev,
7020 struct iw_request_info *info,
James Ketrenos43f66a62005-03-25 12:31:53 -06007021 union iwreq_data *wrqu, char *extra)
7022{
7023 struct ipw_priv *priv = ieee80211_priv(dev);
7024
7025 /* If we are associated, trying to associate, or have a statically
7026 * configured ESSID then return that; otherwise return ANY */
James Ketrenosc848d0a2005-08-24 21:56:24 -05007027 down(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06007028 if (priv->config & CFG_STATIC_ESSID ||
Jeff Garzikbf794512005-07-31 13:07:26 -04007029 priv->status & (STATUS_ASSOCIATED | STATUS_ASSOCIATING)) {
7030 IPW_DEBUG_WX("Getting essid: '%s'\n",
James Ketrenos43f66a62005-03-25 12:31:53 -06007031 escape_essid(priv->essid, priv->essid_len));
Jeff Garzikbf794512005-07-31 13:07:26 -04007032 memcpy(extra, priv->essid, priv->essid_len);
James Ketrenos43f66a62005-03-25 12:31:53 -06007033 wrqu->essid.length = priv->essid_len;
Jeff Garzik0edd5b42005-09-07 00:48:31 -04007034 wrqu->essid.flags = 1; /* active */
James Ketrenos43f66a62005-03-25 12:31:53 -06007035 } else {
7036 IPW_DEBUG_WX("Getting essid: ANY\n");
7037 wrqu->essid.length = 0;
Jeff Garzik0edd5b42005-09-07 00:48:31 -04007038 wrqu->essid.flags = 0; /* active */
James Ketrenos43f66a62005-03-25 12:31:53 -06007039 }
James Ketrenosc848d0a2005-08-24 21:56:24 -05007040 up(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06007041 return 0;
7042}
7043
Jeff Garzikbf794512005-07-31 13:07:26 -04007044static int ipw_wx_set_nick(struct net_device *dev,
7045 struct iw_request_info *info,
James Ketrenos43f66a62005-03-25 12:31:53 -06007046 union iwreq_data *wrqu, char *extra)
Jeff Garzikbf794512005-07-31 13:07:26 -04007047{
James Ketrenos43f66a62005-03-25 12:31:53 -06007048 struct ipw_priv *priv = ieee80211_priv(dev);
7049
7050 IPW_DEBUG_WX("Setting nick to '%s'\n", extra);
7051 if (wrqu->data.length > IW_ESSID_MAX_SIZE)
7052 return -E2BIG;
James Ketrenosc848d0a2005-08-24 21:56:24 -05007053 down(&priv->sem);
Jeff Garzik0edd5b42005-09-07 00:48:31 -04007054 wrqu->data.length = min((size_t) wrqu->data.length, sizeof(priv->nick));
James Ketrenos43f66a62005-03-25 12:31:53 -06007055 memset(priv->nick, 0, sizeof(priv->nick));
Jeff Garzik0edd5b42005-09-07 00:48:31 -04007056 memcpy(priv->nick, extra, wrqu->data.length);
James Ketrenos43f66a62005-03-25 12:31:53 -06007057 IPW_DEBUG_TRACE("<<\n");
James Ketrenosc848d0a2005-08-24 21:56:24 -05007058 up(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06007059 return 0;
7060
7061}
7062
Jeff Garzikbf794512005-07-31 13:07:26 -04007063static int ipw_wx_get_nick(struct net_device *dev,
7064 struct iw_request_info *info,
James Ketrenos43f66a62005-03-25 12:31:53 -06007065 union iwreq_data *wrqu, char *extra)
Jeff Garzikbf794512005-07-31 13:07:26 -04007066{
James Ketrenos43f66a62005-03-25 12:31:53 -06007067 struct ipw_priv *priv = ieee80211_priv(dev);
7068 IPW_DEBUG_WX("Getting nick\n");
James Ketrenosc848d0a2005-08-24 21:56:24 -05007069 down(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06007070 wrqu->data.length = strlen(priv->nick) + 1;
7071 memcpy(extra, priv->nick, wrqu->data.length);
Jeff Garzik0edd5b42005-09-07 00:48:31 -04007072 wrqu->data.flags = 1; /* active */
James Ketrenosc848d0a2005-08-24 21:56:24 -05007073 up(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06007074 return 0;
7075}
7076
James Ketrenos43f66a62005-03-25 12:31:53 -06007077static int ipw_wx_set_rate(struct net_device *dev,
7078 struct iw_request_info *info,
7079 union iwreq_data *wrqu, char *extra)
Jeff Garzikbf794512005-07-31 13:07:26 -04007080{
James Ketrenosea2b26e2005-08-24 21:25:16 -05007081 /* TODO: We should use semaphores or locks for access to priv */
7082 struct ipw_priv *priv = ieee80211_priv(dev);
7083 u32 target_rate = wrqu->bitrate.value;
7084 u32 fixed, mask;
7085
7086 /* value = -1, fixed = 0 means auto only, so we should use all rates offered by AP */
7087 /* value = X, fixed = 1 means only rate X */
7088 /* value = X, fixed = 0 means all rates lower equal X */
7089
7090 if (target_rate == -1) {
7091 fixed = 0;
7092 mask = IEEE80211_DEFAULT_RATES_MASK;
7093 /* Now we should reassociate */
7094 goto apply;
7095 }
7096
7097 mask = 0;
7098 fixed = wrqu->bitrate.fixed;
7099
7100 if (target_rate == 1000000 || !fixed)
7101 mask |= IEEE80211_CCK_RATE_1MB_MASK;
7102 if (target_rate == 1000000)
7103 goto apply;
7104
7105 if (target_rate == 2000000 || !fixed)
7106 mask |= IEEE80211_CCK_RATE_2MB_MASK;
7107 if (target_rate == 2000000)
7108 goto apply;
7109
7110 if (target_rate == 5500000 || !fixed)
7111 mask |= IEEE80211_CCK_RATE_5MB_MASK;
7112 if (target_rate == 5500000)
7113 goto apply;
7114
7115 if (target_rate == 6000000 || !fixed)
7116 mask |= IEEE80211_OFDM_RATE_6MB_MASK;
7117 if (target_rate == 6000000)
7118 goto apply;
7119
7120 if (target_rate == 9000000 || !fixed)
7121 mask |= IEEE80211_OFDM_RATE_9MB_MASK;
7122 if (target_rate == 9000000)
7123 goto apply;
7124
7125 if (target_rate == 11000000 || !fixed)
7126 mask |= IEEE80211_CCK_RATE_11MB_MASK;
7127 if (target_rate == 11000000)
7128 goto apply;
7129
7130 if (target_rate == 12000000 || !fixed)
7131 mask |= IEEE80211_OFDM_RATE_12MB_MASK;
7132 if (target_rate == 12000000)
7133 goto apply;
7134
7135 if (target_rate == 18000000 || !fixed)
7136 mask |= IEEE80211_OFDM_RATE_18MB_MASK;
7137 if (target_rate == 18000000)
7138 goto apply;
7139
7140 if (target_rate == 24000000 || !fixed)
7141 mask |= IEEE80211_OFDM_RATE_24MB_MASK;
7142 if (target_rate == 24000000)
7143 goto apply;
7144
7145 if (target_rate == 36000000 || !fixed)
7146 mask |= IEEE80211_OFDM_RATE_36MB_MASK;
7147 if (target_rate == 36000000)
7148 goto apply;
7149
7150 if (target_rate == 48000000 || !fixed)
7151 mask |= IEEE80211_OFDM_RATE_48MB_MASK;
7152 if (target_rate == 48000000)
7153 goto apply;
7154
7155 if (target_rate == 54000000 || !fixed)
7156 mask |= IEEE80211_OFDM_RATE_54MB_MASK;
7157 if (target_rate == 54000000)
7158 goto apply;
7159
7160 IPW_DEBUG_WX("invalid rate specified, returning error\n");
7161 return -EINVAL;
7162
7163 apply:
7164 IPW_DEBUG_WX("Setting rate mask to 0x%08X [%s]\n",
7165 mask, fixed ? "fixed" : "sub-rates");
James Ketrenosc848d0a2005-08-24 21:56:24 -05007166 down(&priv->sem);
James Ketrenosea2b26e2005-08-24 21:25:16 -05007167 if (mask == IEEE80211_DEFAULT_RATES_MASK)
7168 priv->config &= ~CFG_FIXED_RATE;
7169 else
7170 priv->config |= CFG_FIXED_RATE;
7171
James Ketrenosc848d0a2005-08-24 21:56:24 -05007172 if (priv->rates_mask == mask) {
7173 IPW_DEBUG_WX("Mask set to current mask.\n");
7174 up(&priv->sem);
7175 return 0;
James Ketrenosea2b26e2005-08-24 21:25:16 -05007176 }
7177
James Ketrenosc848d0a2005-08-24 21:56:24 -05007178 priv->rates_mask = mask;
7179
7180 /* Network configuration changed -- force [re]association */
7181 IPW_DEBUG_ASSOC("[re]association triggered due to rates change.\n");
7182 if (!ipw_disassociate(priv))
7183 ipw_associate(priv);
7184
7185 up(&priv->sem);
James Ketrenosea2b26e2005-08-24 21:25:16 -05007186 return 0;
James Ketrenos43f66a62005-03-25 12:31:53 -06007187}
7188
Jeff Garzikbf794512005-07-31 13:07:26 -04007189static int ipw_wx_get_rate(struct net_device *dev,
7190 struct iw_request_info *info,
James Ketrenos43f66a62005-03-25 12:31:53 -06007191 union iwreq_data *wrqu, char *extra)
Jeff Garzikbf794512005-07-31 13:07:26 -04007192{
Jeff Garzik0edd5b42005-09-07 00:48:31 -04007193 struct ipw_priv *priv = ieee80211_priv(dev);
James Ketrenosc848d0a2005-08-24 21:56:24 -05007194 down(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06007195 wrqu->bitrate.value = priv->last_rate;
James Ketrenosc848d0a2005-08-24 21:56:24 -05007196 up(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06007197 IPW_DEBUG_WX("GET Rate -> %d \n", wrqu->bitrate.value);
7198 return 0;
7199}
7200
Jeff Garzikbf794512005-07-31 13:07:26 -04007201static int ipw_wx_set_rts(struct net_device *dev,
7202 struct iw_request_info *info,
James Ketrenos43f66a62005-03-25 12:31:53 -06007203 union iwreq_data *wrqu, char *extra)
Jeff Garzikbf794512005-07-31 13:07:26 -04007204{
James Ketrenos43f66a62005-03-25 12:31:53 -06007205 struct ipw_priv *priv = ieee80211_priv(dev);
James Ketrenosc848d0a2005-08-24 21:56:24 -05007206 down(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06007207 if (wrqu->rts.disabled)
7208 priv->rts_threshold = DEFAULT_RTS_THRESHOLD;
7209 else {
7210 if (wrqu->rts.value < MIN_RTS_THRESHOLD ||
James Ketrenosc848d0a2005-08-24 21:56:24 -05007211 wrqu->rts.value > MAX_RTS_THRESHOLD) {
7212 up(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06007213 return -EINVAL;
James Ketrenosc848d0a2005-08-24 21:56:24 -05007214 }
James Ketrenos43f66a62005-03-25 12:31:53 -06007215 priv->rts_threshold = wrqu->rts.value;
7216 }
7217
7218 ipw_send_rts_threshold(priv, priv->rts_threshold);
James Ketrenosc848d0a2005-08-24 21:56:24 -05007219 up(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06007220 IPW_DEBUG_WX("SET RTS Threshold -> %d \n", priv->rts_threshold);
7221 return 0;
7222}
7223
Jeff Garzikbf794512005-07-31 13:07:26 -04007224static int ipw_wx_get_rts(struct net_device *dev,
7225 struct iw_request_info *info,
James Ketrenos43f66a62005-03-25 12:31:53 -06007226 union iwreq_data *wrqu, char *extra)
Jeff Garzikbf794512005-07-31 13:07:26 -04007227{
James Ketrenos43f66a62005-03-25 12:31:53 -06007228 struct ipw_priv *priv = ieee80211_priv(dev);
James Ketrenosc848d0a2005-08-24 21:56:24 -05007229 down(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06007230 wrqu->rts.value = priv->rts_threshold;
7231 wrqu->rts.fixed = 0; /* no auto select */
Jeff Garzik0edd5b42005-09-07 00:48:31 -04007232 wrqu->rts.disabled = (wrqu->rts.value == DEFAULT_RTS_THRESHOLD);
James Ketrenosc848d0a2005-08-24 21:56:24 -05007233 up(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06007234 IPW_DEBUG_WX("GET RTS Threshold -> %d \n", wrqu->rts.value);
7235 return 0;
7236}
7237
Jeff Garzikbf794512005-07-31 13:07:26 -04007238static int ipw_wx_set_txpow(struct net_device *dev,
7239 struct iw_request_info *info,
James Ketrenos43f66a62005-03-25 12:31:53 -06007240 union iwreq_data *wrqu, char *extra)
Jeff Garzikbf794512005-07-31 13:07:26 -04007241{
James Ketrenos43f66a62005-03-25 12:31:53 -06007242 struct ipw_priv *priv = ieee80211_priv(dev);
7243 struct ipw_tx_power tx_power;
7244 int i;
James Ketrenosc848d0a2005-08-24 21:56:24 -05007245 down(&priv->sem);
7246 if (ipw_radio_kill_sw(priv, wrqu->power.disabled)) {
7247 up(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06007248 return -EINPROGRESS;
James Ketrenosc848d0a2005-08-24 21:56:24 -05007249 }
James Ketrenos43f66a62005-03-25 12:31:53 -06007250
James Ketrenosc848d0a2005-08-24 21:56:24 -05007251 if (wrqu->power.flags != IW_TXPOW_DBM) {
7252 up(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06007253 return -EINVAL;
James Ketrenosc848d0a2005-08-24 21:56:24 -05007254 }
James Ketrenos43f66a62005-03-25 12:31:53 -06007255
James Ketrenosc848d0a2005-08-24 21:56:24 -05007256 if ((wrqu->power.value > 20) || (wrqu->power.value < -12)) {
7257 up(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06007258 return -EINVAL;
James Ketrenosc848d0a2005-08-24 21:56:24 -05007259 }
James Ketrenos43f66a62005-03-25 12:31:53 -06007260
7261 priv->tx_power = wrqu->power.value;
7262
7263 memset(&tx_power, 0, sizeof(tx_power));
7264
7265 /* configure device for 'G' band */
7266 tx_power.ieee_mode = IPW_G_MODE;
7267 tx_power.num_channels = 11;
7268 for (i = 0; i < 11; i++) {
7269 tx_power.channels_tx_power[i].channel_number = i + 1;
7270 tx_power.channels_tx_power[i].tx_power = priv->tx_power;
7271 }
7272 if (ipw_send_tx_power(priv, &tx_power))
7273 goto error;
7274
7275 /* configure device to also handle 'B' band */
7276 tx_power.ieee_mode = IPW_B_MODE;
7277 if (ipw_send_tx_power(priv, &tx_power))
7278 goto error;
7279
James Ketrenosc848d0a2005-08-24 21:56:24 -05007280 up(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06007281 return 0;
7282
Jeff Garzik0edd5b42005-09-07 00:48:31 -04007283 error:
James Ketrenosc848d0a2005-08-24 21:56:24 -05007284 up(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06007285 return -EIO;
7286}
7287
Jeff Garzikbf794512005-07-31 13:07:26 -04007288static int ipw_wx_get_txpow(struct net_device *dev,
7289 struct iw_request_info *info,
James Ketrenos43f66a62005-03-25 12:31:53 -06007290 union iwreq_data *wrqu, char *extra)
Jeff Garzikbf794512005-07-31 13:07:26 -04007291{
James Ketrenos43f66a62005-03-25 12:31:53 -06007292 struct ipw_priv *priv = ieee80211_priv(dev);
James Ketrenosc848d0a2005-08-24 21:56:24 -05007293 down(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06007294 wrqu->power.value = priv->tx_power;
7295 wrqu->power.fixed = 1;
7296 wrqu->power.flags = IW_TXPOW_DBM;
7297 wrqu->power.disabled = (priv->status & STATUS_RF_KILL_MASK) ? 1 : 0;
James Ketrenosc848d0a2005-08-24 21:56:24 -05007298 up(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06007299
Jeff Garzikbf794512005-07-31 13:07:26 -04007300 IPW_DEBUG_WX("GET TX Power -> %s %d \n",
Jeff Garzik0edd5b42005-09-07 00:48:31 -04007301 wrqu->power.disabled ? "ON" : "OFF", wrqu->power.value);
James Ketrenos43f66a62005-03-25 12:31:53 -06007302
7303 return 0;
7304}
7305
Jeff Garzikbf794512005-07-31 13:07:26 -04007306static int ipw_wx_set_frag(struct net_device *dev,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04007307 struct iw_request_info *info,
7308 union iwreq_data *wrqu, char *extra)
James Ketrenos43f66a62005-03-25 12:31:53 -06007309{
7310 struct ipw_priv *priv = ieee80211_priv(dev);
James Ketrenosc848d0a2005-08-24 21:56:24 -05007311 down(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06007312 if (wrqu->frag.disabled)
7313 priv->ieee->fts = DEFAULT_FTS;
7314 else {
7315 if (wrqu->frag.value < MIN_FRAG_THRESHOLD ||
7316 wrqu->frag.value > MAX_FRAG_THRESHOLD)
7317 return -EINVAL;
Jeff Garzikbf794512005-07-31 13:07:26 -04007318
James Ketrenos43f66a62005-03-25 12:31:53 -06007319 priv->ieee->fts = wrqu->frag.value & ~0x1;
7320 }
7321
7322 ipw_send_frag_threshold(priv, wrqu->frag.value);
James Ketrenosc848d0a2005-08-24 21:56:24 -05007323 up(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06007324 IPW_DEBUG_WX("SET Frag Threshold -> %d \n", wrqu->frag.value);
7325 return 0;
7326}
7327
Jeff Garzikbf794512005-07-31 13:07:26 -04007328static int ipw_wx_get_frag(struct net_device *dev,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04007329 struct iw_request_info *info,
7330 union iwreq_data *wrqu, char *extra)
James Ketrenos43f66a62005-03-25 12:31:53 -06007331{
7332 struct ipw_priv *priv = ieee80211_priv(dev);
James Ketrenosc848d0a2005-08-24 21:56:24 -05007333 down(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06007334 wrqu->frag.value = priv->ieee->fts;
7335 wrqu->frag.fixed = 0; /* no auto select */
Jeff Garzik0edd5b42005-09-07 00:48:31 -04007336 wrqu->frag.disabled = (wrqu->frag.value == DEFAULT_FTS);
James Ketrenosc848d0a2005-08-24 21:56:24 -05007337 up(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06007338 IPW_DEBUG_WX("GET Frag Threshold -> %d \n", wrqu->frag.value);
7339
7340 return 0;
7341}
7342
Jeff Garzikbf794512005-07-31 13:07:26 -04007343static int ipw_wx_set_retry(struct net_device *dev,
7344 struct iw_request_info *info,
James Ketrenos43f66a62005-03-25 12:31:53 -06007345 union iwreq_data *wrqu, char *extra)
Jeff Garzikbf794512005-07-31 13:07:26 -04007346{
James Ketrenos43f66a62005-03-25 12:31:53 -06007347 IPW_DEBUG_WX("0x%p, 0x%p, 0x%p\n", dev, info, wrqu);
Jeff Garzikbf794512005-07-31 13:07:26 -04007348 return -EOPNOTSUPP;
James Ketrenos43f66a62005-03-25 12:31:53 -06007349}
7350
Jeff Garzikbf794512005-07-31 13:07:26 -04007351static int ipw_wx_get_retry(struct net_device *dev,
7352 struct iw_request_info *info,
James Ketrenos43f66a62005-03-25 12:31:53 -06007353 union iwreq_data *wrqu, char *extra)
Jeff Garzikbf794512005-07-31 13:07:26 -04007354{
James Ketrenos43f66a62005-03-25 12:31:53 -06007355 IPW_DEBUG_WX("0x%p, 0x%p, 0x%p\n", dev, info, wrqu);
Jeff Garzikbf794512005-07-31 13:07:26 -04007356 return -EOPNOTSUPP;
James Ketrenos43f66a62005-03-25 12:31:53 -06007357}
7358
Jeff Garzikbf794512005-07-31 13:07:26 -04007359static int ipw_wx_set_scan(struct net_device *dev,
7360 struct iw_request_info *info,
James Ketrenos43f66a62005-03-25 12:31:53 -06007361 union iwreq_data *wrqu, char *extra)
7362{
7363 struct ipw_priv *priv = ieee80211_priv(dev);
7364 IPW_DEBUG_WX("Start scan\n");
James Ketrenosc848d0a2005-08-24 21:56:24 -05007365 down(&priv->sem);
7366 if (ipw_request_scan(priv)) {
7367 up(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06007368 return -EIO;
James Ketrenosc848d0a2005-08-24 21:56:24 -05007369 }
7370 up(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06007371 return 0;
7372}
7373
Jeff Garzikbf794512005-07-31 13:07:26 -04007374static int ipw_wx_get_scan(struct net_device *dev,
7375 struct iw_request_info *info,
James Ketrenos43f66a62005-03-25 12:31:53 -06007376 union iwreq_data *wrqu, char *extra)
Jeff Garzikbf794512005-07-31 13:07:26 -04007377{
James Ketrenos43f66a62005-03-25 12:31:53 -06007378 struct ipw_priv *priv = ieee80211_priv(dev);
7379 return ieee80211_wx_get_scan(priv->ieee, info, wrqu, extra);
7380}
7381
Jeff Garzikbf794512005-07-31 13:07:26 -04007382static int ipw_wx_set_encode(struct net_device *dev,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04007383 struct iw_request_info *info,
7384 union iwreq_data *wrqu, char *key)
James Ketrenos43f66a62005-03-25 12:31:53 -06007385{
7386 struct ipw_priv *priv = ieee80211_priv(dev);
7387 return ieee80211_wx_set_encode(priv->ieee, info, wrqu, key);
7388}
7389
Jeff Garzikbf794512005-07-31 13:07:26 -04007390static int ipw_wx_get_encode(struct net_device *dev,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04007391 struct iw_request_info *info,
7392 union iwreq_data *wrqu, char *key)
James Ketrenos43f66a62005-03-25 12:31:53 -06007393{
7394 struct ipw_priv *priv = ieee80211_priv(dev);
7395 return ieee80211_wx_get_encode(priv->ieee, info, wrqu, key);
7396}
7397
Jeff Garzikbf794512005-07-31 13:07:26 -04007398static int ipw_wx_set_power(struct net_device *dev,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04007399 struct iw_request_info *info,
7400 union iwreq_data *wrqu, char *extra)
James Ketrenos43f66a62005-03-25 12:31:53 -06007401{
7402 struct ipw_priv *priv = ieee80211_priv(dev);
7403 int err;
James Ketrenosc848d0a2005-08-24 21:56:24 -05007404 down(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06007405 if (wrqu->power.disabled) {
7406 priv->power_mode = IPW_POWER_LEVEL(priv->power_mode);
7407 err = ipw_send_power_mode(priv, IPW_POWER_MODE_CAM);
7408 if (err) {
7409 IPW_DEBUG_WX("failed setting power mode.\n");
James Ketrenosc848d0a2005-08-24 21:56:24 -05007410 up(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06007411 return err;
7412 }
James Ketrenos43f66a62005-03-25 12:31:53 -06007413 IPW_DEBUG_WX("SET Power Management Mode -> off\n");
James Ketrenosc848d0a2005-08-24 21:56:24 -05007414 up(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06007415 return 0;
Jeff Garzikbf794512005-07-31 13:07:26 -04007416 }
James Ketrenos43f66a62005-03-25 12:31:53 -06007417
7418 switch (wrqu->power.flags & IW_POWER_MODE) {
Jeff Garzik0edd5b42005-09-07 00:48:31 -04007419 case IW_POWER_ON: /* If not specified */
7420 case IW_POWER_MODE: /* If set all mask */
7421 case IW_POWER_ALL_R: /* If explicitely state all */
James Ketrenos43f66a62005-03-25 12:31:53 -06007422 break;
Jeff Garzik0edd5b42005-09-07 00:48:31 -04007423 default: /* Otherwise we don't support it */
James Ketrenos43f66a62005-03-25 12:31:53 -06007424 IPW_DEBUG_WX("SET PM Mode: %X not supported.\n",
7425 wrqu->power.flags);
James Ketrenosc848d0a2005-08-24 21:56:24 -05007426 up(&priv->sem);
Jeff Garzikbf794512005-07-31 13:07:26 -04007427 return -EOPNOTSUPP;
James Ketrenos43f66a62005-03-25 12:31:53 -06007428 }
Jeff Garzikbf794512005-07-31 13:07:26 -04007429
James Ketrenos43f66a62005-03-25 12:31:53 -06007430 /* If the user hasn't specified a power management mode yet, default
7431 * to BATTERY */
Jeff Garzik0edd5b42005-09-07 00:48:31 -04007432 if (IPW_POWER_LEVEL(priv->power_mode) == IPW_POWER_AC)
James Ketrenos43f66a62005-03-25 12:31:53 -06007433 priv->power_mode = IPW_POWER_ENABLED | IPW_POWER_BATTERY;
Jeff Garzikbf794512005-07-31 13:07:26 -04007434 else
James Ketrenos43f66a62005-03-25 12:31:53 -06007435 priv->power_mode = IPW_POWER_ENABLED | priv->power_mode;
7436 err = ipw_send_power_mode(priv, IPW_POWER_LEVEL(priv->power_mode));
7437 if (err) {
7438 IPW_DEBUG_WX("failed setting power mode.\n");
James Ketrenosc848d0a2005-08-24 21:56:24 -05007439 up(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06007440 return err;
7441 }
7442
Jeff Garzik0edd5b42005-09-07 00:48:31 -04007443 IPW_DEBUG_WX("SET Power Management Mode -> 0x%02X\n", priv->power_mode);
James Ketrenosc848d0a2005-08-24 21:56:24 -05007444 up(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06007445 return 0;
7446}
7447
Jeff Garzikbf794512005-07-31 13:07:26 -04007448static int ipw_wx_get_power(struct net_device *dev,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04007449 struct iw_request_info *info,
7450 union iwreq_data *wrqu, char *extra)
James Ketrenos43f66a62005-03-25 12:31:53 -06007451{
7452 struct ipw_priv *priv = ieee80211_priv(dev);
James Ketrenosc848d0a2005-08-24 21:56:24 -05007453 down(&priv->sem);
James Ketrenosa613bff2005-08-24 21:43:11 -05007454 if (!(priv->power_mode & IPW_POWER_ENABLED))
James Ketrenos43f66a62005-03-25 12:31:53 -06007455 wrqu->power.disabled = 1;
James Ketrenosa613bff2005-08-24 21:43:11 -05007456 else
James Ketrenos43f66a62005-03-25 12:31:53 -06007457 wrqu->power.disabled = 0;
James Ketrenos43f66a62005-03-25 12:31:53 -06007458
James Ketrenosc848d0a2005-08-24 21:56:24 -05007459 up(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06007460 IPW_DEBUG_WX("GET Power Management Mode -> %02X\n", priv->power_mode);
Jeff Garzikbf794512005-07-31 13:07:26 -04007461
James Ketrenos43f66a62005-03-25 12:31:53 -06007462 return 0;
7463}
7464
Jeff Garzikbf794512005-07-31 13:07:26 -04007465static int ipw_wx_set_powermode(struct net_device *dev,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04007466 struct iw_request_info *info,
7467 union iwreq_data *wrqu, char *extra)
James Ketrenos43f66a62005-03-25 12:31:53 -06007468{
7469 struct ipw_priv *priv = ieee80211_priv(dev);
7470 int mode = *(int *)extra;
7471 int err;
James Ketrenosc848d0a2005-08-24 21:56:24 -05007472 down(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06007473 if ((mode < 1) || (mode > IPW_POWER_LIMIT)) {
7474 mode = IPW_POWER_AC;
7475 priv->power_mode = mode;
7476 } else {
7477 priv->power_mode = IPW_POWER_ENABLED | mode;
7478 }
Jeff Garzikbf794512005-07-31 13:07:26 -04007479
James Ketrenos43f66a62005-03-25 12:31:53 -06007480 if (priv->power_mode != mode) {
7481 err = ipw_send_power_mode(priv, mode);
Jeff Garzikbf794512005-07-31 13:07:26 -04007482
James Ketrenos43f66a62005-03-25 12:31:53 -06007483 if (err) {
7484 IPW_DEBUG_WX("failed setting power mode.\n");
James Ketrenosc848d0a2005-08-24 21:56:24 -05007485 up(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06007486 return err;
7487 }
7488 }
James Ketrenosc848d0a2005-08-24 21:56:24 -05007489 up(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06007490 return 0;
7491}
7492
7493#define MAX_WX_STRING 80
Jeff Garzikbf794512005-07-31 13:07:26 -04007494static int ipw_wx_get_powermode(struct net_device *dev,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04007495 struct iw_request_info *info,
7496 union iwreq_data *wrqu, char *extra)
James Ketrenos43f66a62005-03-25 12:31:53 -06007497{
7498 struct ipw_priv *priv = ieee80211_priv(dev);
7499 int level = IPW_POWER_LEVEL(priv->power_mode);
7500 char *p = extra;
7501
7502 p += snprintf(p, MAX_WX_STRING, "Power save level: %d ", level);
7503
7504 switch (level) {
7505 case IPW_POWER_AC:
7506 p += snprintf(p, MAX_WX_STRING - (p - extra), "(AC)");
7507 break;
7508 case IPW_POWER_BATTERY:
7509 p += snprintf(p, MAX_WX_STRING - (p - extra), "(BATTERY)");
7510 break;
7511 default:
7512 p += snprintf(p, MAX_WX_STRING - (p - extra),
Jeff Garzikbf794512005-07-31 13:07:26 -04007513 "(Timeout %dms, Period %dms)",
James Ketrenos43f66a62005-03-25 12:31:53 -06007514 timeout_duration[level - 1] / 1000,
7515 period_duration[level - 1] / 1000);
7516 }
7517
7518 if (!(priv->power_mode & IPW_POWER_ENABLED))
Jeff Garzik0edd5b42005-09-07 00:48:31 -04007519 p += snprintf(p, MAX_WX_STRING - (p - extra), " OFF");
James Ketrenos43f66a62005-03-25 12:31:53 -06007520
7521 wrqu->data.length = p - extra + 1;
7522
7523 return 0;
7524}
7525
7526static int ipw_wx_set_wireless_mode(struct net_device *dev,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04007527 struct iw_request_info *info,
7528 union iwreq_data *wrqu, char *extra)
James Ketrenos43f66a62005-03-25 12:31:53 -06007529{
Jeff Garzik0edd5b42005-09-07 00:48:31 -04007530 struct ipw_priv *priv = ieee80211_priv(dev);
James Ketrenos43f66a62005-03-25 12:31:53 -06007531 int mode = *(int *)extra;
7532 u8 band = 0, modulation = 0;
7533
7534 if (mode == 0 || mode & ~IEEE_MODE_MASK) {
Jeff Garzik0edd5b42005-09-07 00:48:31 -04007535 IPW_WARNING("Attempt to set invalid wireless mode: %d\n", mode);
James Ketrenos43f66a62005-03-25 12:31:53 -06007536 return -EINVAL;
7537 }
James Ketrenosc848d0a2005-08-24 21:56:24 -05007538 down(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06007539 if (priv->adapter == IPW_2915ABG) {
James Ketrenosa33a1982005-09-14 14:28:59 -05007540 priv->ieee->abg_true = 1;
James Ketrenos43f66a62005-03-25 12:31:53 -06007541 if (mode & IEEE_A) {
7542 band |= IEEE80211_52GHZ_BAND;
7543 modulation |= IEEE80211_OFDM_MODULATION;
7544 } else
James Ketrenosa33a1982005-09-14 14:28:59 -05007545 priv->ieee->abg_true = 0;
James Ketrenos43f66a62005-03-25 12:31:53 -06007546 } else {
7547 if (mode & IEEE_A) {
7548 IPW_WARNING("Attempt to set 2200BG into "
7549 "802.11a mode\n");
James Ketrenosc848d0a2005-08-24 21:56:24 -05007550 up(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06007551 return -EINVAL;
7552 }
7553
James Ketrenosa33a1982005-09-14 14:28:59 -05007554 priv->ieee->abg_true = 0;
James Ketrenos43f66a62005-03-25 12:31:53 -06007555 }
7556
7557 if (mode & IEEE_B) {
7558 band |= IEEE80211_24GHZ_BAND;
7559 modulation |= IEEE80211_CCK_MODULATION;
7560 } else
James Ketrenosa33a1982005-09-14 14:28:59 -05007561 priv->ieee->abg_true = 0;
Jeff Garzikbf794512005-07-31 13:07:26 -04007562
James Ketrenos43f66a62005-03-25 12:31:53 -06007563 if (mode & IEEE_G) {
7564 band |= IEEE80211_24GHZ_BAND;
7565 modulation |= IEEE80211_OFDM_MODULATION;
7566 } else
James Ketrenosa33a1982005-09-14 14:28:59 -05007567 priv->ieee->abg_true = 0;
James Ketrenos43f66a62005-03-25 12:31:53 -06007568
7569 priv->ieee->mode = mode;
7570 priv->ieee->freq_band = band;
7571 priv->ieee->modulation = modulation;
Jeff Garzik0edd5b42005-09-07 00:48:31 -04007572 init_supported_rates(priv, &priv->rates);
James Ketrenos43f66a62005-03-25 12:31:53 -06007573
James Ketrenosc848d0a2005-08-24 21:56:24 -05007574 /* Network configuration changed -- force [re]association */
7575 IPW_DEBUG_ASSOC("[re]association triggered due to mode change.\n");
7576 if (!ipw_disassociate(priv)) {
James Ketrenos43f66a62005-03-25 12:31:53 -06007577 ipw_send_supported_rates(priv, &priv->rates);
James Ketrenosc848d0a2005-08-24 21:56:24 -05007578 ipw_associate(priv);
7579 }
James Ketrenos43f66a62005-03-25 12:31:53 -06007580
James Ketrenosa613bff2005-08-24 21:43:11 -05007581 /* Update the band LEDs */
7582 ipw_led_band_on(priv);
7583
Jeff Garzikbf794512005-07-31 13:07:26 -04007584 IPW_DEBUG_WX("PRIV SET MODE: %c%c%c\n",
James Ketrenos43f66a62005-03-25 12:31:53 -06007585 mode & IEEE_A ? 'a' : '.',
Jeff Garzik0edd5b42005-09-07 00:48:31 -04007586 mode & IEEE_B ? 'b' : '.', mode & IEEE_G ? 'g' : '.');
James Ketrenosc848d0a2005-08-24 21:56:24 -05007587 up(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06007588 return 0;
7589}
7590
7591static int ipw_wx_get_wireless_mode(struct net_device *dev,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04007592 struct iw_request_info *info,
7593 union iwreq_data *wrqu, char *extra)
James Ketrenos43f66a62005-03-25 12:31:53 -06007594{
Jeff Garzik0edd5b42005-09-07 00:48:31 -04007595 struct ipw_priv *priv = ieee80211_priv(dev);
James Ketrenosc848d0a2005-08-24 21:56:24 -05007596 down(&priv->sem);
James Ketrenosea2b26e2005-08-24 21:25:16 -05007597 switch (priv->ieee->mode) {
7598 case IEEE_A:
James Ketrenos43f66a62005-03-25 12:31:53 -06007599 strncpy(extra, "802.11a (1)", MAX_WX_STRING);
7600 break;
James Ketrenosea2b26e2005-08-24 21:25:16 -05007601 case IEEE_B:
7602 strncpy(extra, "802.11b (2)", MAX_WX_STRING);
7603 break;
7604 case IEEE_A | IEEE_B:
7605 strncpy(extra, "802.11ab (3)", MAX_WX_STRING);
7606 break;
7607 case IEEE_G:
7608 strncpy(extra, "802.11g (4)", MAX_WX_STRING);
7609 break;
7610 case IEEE_A | IEEE_G:
7611 strncpy(extra, "802.11ag (5)", MAX_WX_STRING);
7612 break;
7613 case IEEE_B | IEEE_G:
7614 strncpy(extra, "802.11bg (6)", MAX_WX_STRING);
7615 break;
7616 case IEEE_A | IEEE_B | IEEE_G:
7617 strncpy(extra, "802.11abg (7)", MAX_WX_STRING);
7618 break;
7619 default:
7620 strncpy(extra, "unknown", MAX_WX_STRING);
James Ketrenos43f66a62005-03-25 12:31:53 -06007621 break;
Jeff Garzikbf794512005-07-31 13:07:26 -04007622 }
7623
James Ketrenos43f66a62005-03-25 12:31:53 -06007624 IPW_DEBUG_WX("PRIV GET MODE: %s\n", extra);
7625
Jeff Garzik0edd5b42005-09-07 00:48:31 -04007626 wrqu->data.length = strlen(extra) + 1;
James Ketrenosc848d0a2005-08-24 21:56:24 -05007627 up(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06007628
Jeff Garzik0edd5b42005-09-07 00:48:31 -04007629 return 0;
James Ketrenos43f66a62005-03-25 12:31:53 -06007630}
7631
James Ketrenosea2b26e2005-08-24 21:25:16 -05007632static int ipw_wx_set_preamble(struct net_device *dev,
7633 struct iw_request_info *info,
7634 union iwreq_data *wrqu, char *extra)
7635{
7636 struct ipw_priv *priv = ieee80211_priv(dev);
7637 int mode = *(int *)extra;
James Ketrenosc848d0a2005-08-24 21:56:24 -05007638 down(&priv->sem);
James Ketrenosea2b26e2005-08-24 21:25:16 -05007639 /* Switching from SHORT -> LONG requires a disassociation */
7640 if (mode == 1) {
7641 if (!(priv->config & CFG_PREAMBLE_LONG)) {
7642 priv->config |= CFG_PREAMBLE_LONG;
James Ketrenosc848d0a2005-08-24 21:56:24 -05007643
7644 /* Network configuration changed -- force [re]association */
7645 IPW_DEBUG_ASSOC
7646 ("[re]association triggered due to preamble change.\n");
7647 if (!ipw_disassociate(priv))
7648 ipw_associate(priv);
James Ketrenosea2b26e2005-08-24 21:25:16 -05007649 }
7650 goto done;
7651 }
7652
7653 if (mode == 0) {
7654 priv->config &= ~CFG_PREAMBLE_LONG;
7655 goto done;
7656 }
James Ketrenosc848d0a2005-08-24 21:56:24 -05007657 up(&priv->sem);
James Ketrenosea2b26e2005-08-24 21:25:16 -05007658 return -EINVAL;
7659
7660 done:
James Ketrenosc848d0a2005-08-24 21:56:24 -05007661 up(&priv->sem);
James Ketrenosea2b26e2005-08-24 21:25:16 -05007662 return 0;
7663}
7664
7665static int ipw_wx_get_preamble(struct net_device *dev,
7666 struct iw_request_info *info,
7667 union iwreq_data *wrqu, char *extra)
7668{
7669 struct ipw_priv *priv = ieee80211_priv(dev);
James Ketrenosc848d0a2005-08-24 21:56:24 -05007670 down(&priv->sem);
James Ketrenosea2b26e2005-08-24 21:25:16 -05007671 if (priv->config & CFG_PREAMBLE_LONG)
7672 snprintf(wrqu->name, IFNAMSIZ, "long (1)");
7673 else
7674 snprintf(wrqu->name, IFNAMSIZ, "auto (0)");
James Ketrenosc848d0a2005-08-24 21:56:24 -05007675 up(&priv->sem);
James Ketrenosea2b26e2005-08-24 21:25:16 -05007676 return 0;
7677}
7678
7679#ifdef CONFIG_IPW_MONITOR
7680static int ipw_wx_set_monitor(struct net_device *dev,
Jeff Garzikbf794512005-07-31 13:07:26 -04007681 struct iw_request_info *info,
James Ketrenos43f66a62005-03-25 12:31:53 -06007682 union iwreq_data *wrqu, char *extra)
Jeff Garzikbf794512005-07-31 13:07:26 -04007683{
James Ketrenos43f66a62005-03-25 12:31:53 -06007684 struct ipw_priv *priv = ieee80211_priv(dev);
7685 int *parms = (int *)extra;
7686 int enable = (parms[0] > 0);
James Ketrenosc848d0a2005-08-24 21:56:24 -05007687 down(&priv->sem);
James Ketrenosea2b26e2005-08-24 21:25:16 -05007688 IPW_DEBUG_WX("SET MONITOR: %d %d\n", enable, parms[1]);
James Ketrenos43f66a62005-03-25 12:31:53 -06007689 if (enable) {
7690 if (priv->ieee->iw_mode != IW_MODE_MONITOR) {
7691 priv->net_dev->type = ARPHRD_IEEE80211;
James Ketrenosa613bff2005-08-24 21:43:11 -05007692 queue_work(priv->workqueue, &priv->adapter_restart);
James Ketrenos43f66a62005-03-25 12:31:53 -06007693 }
Jeff Garzikbf794512005-07-31 13:07:26 -04007694
James Ketrenos43f66a62005-03-25 12:31:53 -06007695 ipw_set_channel(priv, parms[1]);
7696 } else {
James Ketrenosc848d0a2005-08-24 21:56:24 -05007697 if (priv->ieee->iw_mode != IW_MODE_MONITOR) {
7698 up(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06007699 return 0;
James Ketrenosc848d0a2005-08-24 21:56:24 -05007700 }
James Ketrenos43f66a62005-03-25 12:31:53 -06007701 priv->net_dev->type = ARPHRD_ETHER;
James Ketrenosa613bff2005-08-24 21:43:11 -05007702 queue_work(priv->workqueue, &priv->adapter_restart);
James Ketrenos43f66a62005-03-25 12:31:53 -06007703 }
James Ketrenosc848d0a2005-08-24 21:56:24 -05007704 up(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06007705 return 0;
7706}
7707
Jeff Garzikbf794512005-07-31 13:07:26 -04007708static int ipw_wx_reset(struct net_device *dev,
7709 struct iw_request_info *info,
James Ketrenos43f66a62005-03-25 12:31:53 -06007710 union iwreq_data *wrqu, char *extra)
Jeff Garzikbf794512005-07-31 13:07:26 -04007711{
James Ketrenos43f66a62005-03-25 12:31:53 -06007712 struct ipw_priv *priv = ieee80211_priv(dev);
7713 IPW_DEBUG_WX("RESET\n");
James Ketrenosa613bff2005-08-24 21:43:11 -05007714 queue_work(priv->workqueue, &priv->adapter_restart);
James Ketrenos43f66a62005-03-25 12:31:53 -06007715 return 0;
7716}
James Ketrenosea2b26e2005-08-24 21:25:16 -05007717#endif // CONFIG_IPW_MONITOR
James Ketrenos43f66a62005-03-25 12:31:53 -06007718
7719/* Rebase the WE IOCTLs to zero for the handler array */
7720#define IW_IOCTL(x) [(x)-SIOCSIWCOMMIT]
Jeff Garzik0edd5b42005-09-07 00:48:31 -04007721static iw_handler ipw_wx_handlers[] = {
James Ketrenosea2b26e2005-08-24 21:25:16 -05007722 IW_IOCTL(SIOCGIWNAME) = ipw_wx_get_name,
7723 IW_IOCTL(SIOCSIWFREQ) = ipw_wx_set_freq,
7724 IW_IOCTL(SIOCGIWFREQ) = ipw_wx_get_freq,
7725 IW_IOCTL(SIOCSIWMODE) = ipw_wx_set_mode,
7726 IW_IOCTL(SIOCGIWMODE) = ipw_wx_get_mode,
7727 IW_IOCTL(SIOCGIWRANGE) = ipw_wx_get_range,
7728 IW_IOCTL(SIOCSIWAP) = ipw_wx_set_wap,
7729 IW_IOCTL(SIOCGIWAP) = ipw_wx_get_wap,
7730 IW_IOCTL(SIOCSIWSCAN) = ipw_wx_set_scan,
7731 IW_IOCTL(SIOCGIWSCAN) = ipw_wx_get_scan,
7732 IW_IOCTL(SIOCSIWESSID) = ipw_wx_set_essid,
7733 IW_IOCTL(SIOCGIWESSID) = ipw_wx_get_essid,
7734 IW_IOCTL(SIOCSIWNICKN) = ipw_wx_set_nick,
7735 IW_IOCTL(SIOCGIWNICKN) = ipw_wx_get_nick,
7736 IW_IOCTL(SIOCSIWRATE) = ipw_wx_set_rate,
7737 IW_IOCTL(SIOCGIWRATE) = ipw_wx_get_rate,
7738 IW_IOCTL(SIOCSIWRTS) = ipw_wx_set_rts,
7739 IW_IOCTL(SIOCGIWRTS) = ipw_wx_get_rts,
7740 IW_IOCTL(SIOCSIWFRAG) = ipw_wx_set_frag,
7741 IW_IOCTL(SIOCGIWFRAG) = ipw_wx_get_frag,
7742 IW_IOCTL(SIOCSIWTXPOW) = ipw_wx_set_txpow,
7743 IW_IOCTL(SIOCGIWTXPOW) = ipw_wx_get_txpow,
7744 IW_IOCTL(SIOCSIWRETRY) = ipw_wx_set_retry,
7745 IW_IOCTL(SIOCGIWRETRY) = ipw_wx_get_retry,
7746 IW_IOCTL(SIOCSIWENCODE) = ipw_wx_set_encode,
7747 IW_IOCTL(SIOCGIWENCODE) = ipw_wx_get_encode,
7748 IW_IOCTL(SIOCSIWPOWER) = ipw_wx_set_power,
7749 IW_IOCTL(SIOCGIWPOWER) = ipw_wx_get_power,
James Ketrenosa613bff2005-08-24 21:43:11 -05007750 IW_IOCTL(SIOCSIWSPY) = iw_handler_set_spy,
7751 IW_IOCTL(SIOCGIWSPY) = iw_handler_get_spy,
7752 IW_IOCTL(SIOCSIWTHRSPY) = iw_handler_set_thrspy,
7753 IW_IOCTL(SIOCGIWTHRSPY) = iw_handler_get_thrspy,
James Ketrenos43f66a62005-03-25 12:31:53 -06007754};
7755
7756#define IPW_PRIV_SET_POWER SIOCIWFIRSTPRIV
7757#define IPW_PRIV_GET_POWER SIOCIWFIRSTPRIV+1
7758#define IPW_PRIV_SET_MODE SIOCIWFIRSTPRIV+2
7759#define IPW_PRIV_GET_MODE SIOCIWFIRSTPRIV+3
James Ketrenosea2b26e2005-08-24 21:25:16 -05007760#define IPW_PRIV_SET_PREAMBLE SIOCIWFIRSTPRIV+4
7761#define IPW_PRIV_GET_PREAMBLE SIOCIWFIRSTPRIV+5
7762#define IPW_PRIV_SET_MONITOR SIOCIWFIRSTPRIV+6
7763#define IPW_PRIV_RESET SIOCIWFIRSTPRIV+7
James Ketrenos43f66a62005-03-25 12:31:53 -06007764
Jeff Garzikbf794512005-07-31 13:07:26 -04007765static struct iw_priv_args ipw_priv_args[] = {
James Ketrenos43f66a62005-03-25 12:31:53 -06007766 {
Jeff Garzik0edd5b42005-09-07 00:48:31 -04007767 .cmd = IPW_PRIV_SET_POWER,
7768 .set_args = IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
7769 .name = "set_power"},
James Ketrenos43f66a62005-03-25 12:31:53 -06007770 {
Jeff Garzik0edd5b42005-09-07 00:48:31 -04007771 .cmd = IPW_PRIV_GET_POWER,
7772 .get_args = IW_PRIV_TYPE_CHAR | IW_PRIV_SIZE_FIXED | MAX_WX_STRING,
7773 .name = "get_power"},
James Ketrenos43f66a62005-03-25 12:31:53 -06007774 {
Jeff Garzik0edd5b42005-09-07 00:48:31 -04007775 .cmd = IPW_PRIV_SET_MODE,
7776 .set_args = IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
7777 .name = "set_mode"},
James Ketrenos43f66a62005-03-25 12:31:53 -06007778 {
Jeff Garzik0edd5b42005-09-07 00:48:31 -04007779 .cmd = IPW_PRIV_GET_MODE,
7780 .get_args = IW_PRIV_TYPE_CHAR | IW_PRIV_SIZE_FIXED | MAX_WX_STRING,
7781 .name = "get_mode"},
James Ketrenos43f66a62005-03-25 12:31:53 -06007782 {
James Ketrenosea2b26e2005-08-24 21:25:16 -05007783 .cmd = IPW_PRIV_SET_PREAMBLE,
7784 .set_args = IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
7785 .name = "set_preamble"},
7786 {
7787 .cmd = IPW_PRIV_GET_PREAMBLE,
7788 .get_args = IW_PRIV_TYPE_CHAR | IW_PRIV_SIZE_FIXED | IFNAMSIZ,
7789 .name = "get_preamble"},
7790#ifdef CONFIG_IPW_MONITOR
7791 {
7792 IPW_PRIV_SET_MONITOR,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04007793 IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 2, 0, "monitor"},
James Ketrenos43f66a62005-03-25 12:31:53 -06007794 {
Jeff Garzik0edd5b42005-09-07 00:48:31 -04007795 IPW_PRIV_RESET,
7796 IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 0, 0, "reset"},
James Ketrenosea2b26e2005-08-24 21:25:16 -05007797#endif /* CONFIG_IPW_MONITOR */
James Ketrenos43f66a62005-03-25 12:31:53 -06007798};
7799
7800static iw_handler ipw_priv_handler[] = {
7801 ipw_wx_set_powermode,
7802 ipw_wx_get_powermode,
7803 ipw_wx_set_wireless_mode,
7804 ipw_wx_get_wireless_mode,
James Ketrenosea2b26e2005-08-24 21:25:16 -05007805 ipw_wx_set_preamble,
7806 ipw_wx_get_preamble,
7807#ifdef CONFIG_IPW_MONITOR
7808 ipw_wx_set_monitor,
Jeff Garzikbf794512005-07-31 13:07:26 -04007809 ipw_wx_reset,
James Ketrenos43f66a62005-03-25 12:31:53 -06007810#endif
7811};
7812
Jeff Garzik0edd5b42005-09-07 00:48:31 -04007813static struct iw_handler_def ipw_wx_handler_def = {
James Ketrenosea2b26e2005-08-24 21:25:16 -05007814 .standard = ipw_wx_handlers,
7815 .num_standard = ARRAY_SIZE(ipw_wx_handlers),
7816 .num_private = ARRAY_SIZE(ipw_priv_handler),
7817 .num_private_args = ARRAY_SIZE(ipw_priv_args),
7818 .private = ipw_priv_handler,
7819 .private_args = ipw_priv_args,
James Ketrenos43f66a62005-03-25 12:31:53 -06007820};
7821
James Ketrenosa613bff2005-08-24 21:43:11 -05007822static struct iw_public_data ipw_wx_data;
7823
James Ketrenos43f66a62005-03-25 12:31:53 -06007824/*
7825 * Get wireless statistics.
7826 * Called by /proc/net/wireless
7827 * Also called by SIOCGIWSTATS
7828 */
Jeff Garzik0edd5b42005-09-07 00:48:31 -04007829static struct iw_statistics *ipw_get_wireless_stats(struct net_device *dev)
James Ketrenos43f66a62005-03-25 12:31:53 -06007830{
7831 struct ipw_priv *priv = ieee80211_priv(dev);
7832 struct iw_statistics *wstats;
Jeff Garzikbf794512005-07-31 13:07:26 -04007833
James Ketrenos43f66a62005-03-25 12:31:53 -06007834 wstats = &priv->wstats;
7835
James Ketrenosea2b26e2005-08-24 21:25:16 -05007836 /* if hw is disabled, then ipw_get_ordinal() can't be called.
Jeff Garzikbf794512005-07-31 13:07:26 -04007837 * ipw2100_wx_wireless_stats seems to be called before fw is
James Ketrenos43f66a62005-03-25 12:31:53 -06007838 * initialized. STATUS_ASSOCIATED will only be set if the hw is up
7839 * and associated; if not associcated, the values are all meaningless
7840 * anyway, so set them all to NULL and INVALID */
7841 if (!(priv->status & STATUS_ASSOCIATED)) {
7842 wstats->miss.beacon = 0;
7843 wstats->discard.retries = 0;
7844 wstats->qual.qual = 0;
7845 wstats->qual.level = 0;
7846 wstats->qual.noise = 0;
7847 wstats->qual.updated = 7;
7848 wstats->qual.updated |= IW_QUAL_NOISE_INVALID |
Jeff Garzik0edd5b42005-09-07 00:48:31 -04007849 IW_QUAL_QUAL_INVALID | IW_QUAL_LEVEL_INVALID;
James Ketrenos43f66a62005-03-25 12:31:53 -06007850 return wstats;
Jeff Garzikbf794512005-07-31 13:07:26 -04007851 }
James Ketrenos43f66a62005-03-25 12:31:53 -06007852
7853 wstats->qual.qual = priv->quality;
7854 wstats->qual.level = average_value(&priv->average_rssi);
7855 wstats->qual.noise = average_value(&priv->average_noise);
7856 wstats->qual.updated = IW_QUAL_QUAL_UPDATED | IW_QUAL_LEVEL_UPDATED |
Jeff Garzik0edd5b42005-09-07 00:48:31 -04007857 IW_QUAL_NOISE_UPDATED;
James Ketrenos43f66a62005-03-25 12:31:53 -06007858
7859 wstats->miss.beacon = average_value(&priv->average_missed_beacons);
7860 wstats->discard.retries = priv->last_tx_failures;
7861 wstats->discard.code = priv->ieee->ieee_stats.rx_discards_undecryptable;
Jeff Garzikbf794512005-07-31 13:07:26 -04007862
James Ketrenos43f66a62005-03-25 12:31:53 -06007863/* if (ipw_get_ordinal(priv, IPW_ORD_STAT_TX_RETRY, &tx_retry, &len))
7864 goto fail_get_ordinal;
7865 wstats->discard.retries += tx_retry; */
Jeff Garzikbf794512005-07-31 13:07:26 -04007866
James Ketrenos43f66a62005-03-25 12:31:53 -06007867 return wstats;
7868}
7869
James Ketrenos43f66a62005-03-25 12:31:53 -06007870/* net device stuff */
7871
7872static inline void init_sys_config(struct ipw_sys_config *sys_config)
7873{
Jeff Garzik0edd5b42005-09-07 00:48:31 -04007874 memset(sys_config, 0, sizeof(struct ipw_sys_config));
7875 sys_config->bt_coexistence = 1; /* We may need to look into prvStaBtConfig */
James Ketrenos43f66a62005-03-25 12:31:53 -06007876 sys_config->answer_broadcast_ssid_probe = 0;
7877 sys_config->accept_all_data_frames = 0;
7878 sys_config->accept_non_directed_frames = 1;
7879 sys_config->exclude_unicast_unencrypted = 0;
7880 sys_config->disable_unicast_decryption = 1;
7881 sys_config->exclude_multicast_unencrypted = 0;
7882 sys_config->disable_multicast_decryption = 1;
7883 sys_config->antenna_diversity = CFG_SYS_ANTENNA_BOTH;
Jeff Garzik0edd5b42005-09-07 00:48:31 -04007884 sys_config->pass_crc_to_host = 0; /* TODO: See if 1 gives us FCS */
James Ketrenos43f66a62005-03-25 12:31:53 -06007885 sys_config->dot11g_auto_detection = 0;
Jeff Garzikbf794512005-07-31 13:07:26 -04007886 sys_config->enable_cts_to_self = 0;
James Ketrenos43f66a62005-03-25 12:31:53 -06007887 sys_config->bt_coexist_collision_thr = 0;
James Ketrenosc848d0a2005-08-24 21:56:24 -05007888 sys_config->pass_noise_stats_to_host = 1; //1 -- fix for 256
James Ketrenos43f66a62005-03-25 12:31:53 -06007889}
7890
7891static int ipw_net_open(struct net_device *dev)
7892{
7893 struct ipw_priv *priv = ieee80211_priv(dev);
7894 IPW_DEBUG_INFO("dev->open\n");
7895 /* we should be verifying the device is ready to be opened */
James Ketrenosc848d0a2005-08-24 21:56:24 -05007896 down(&priv->sem);
Jeff Garzikbf794512005-07-31 13:07:26 -04007897 if (!(priv->status & STATUS_RF_KILL_MASK) &&
7898 (priv->status & STATUS_ASSOCIATED))
James Ketrenos43f66a62005-03-25 12:31:53 -06007899 netif_start_queue(dev);
James Ketrenosc848d0a2005-08-24 21:56:24 -05007900 up(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06007901 return 0;
7902}
7903
7904static int ipw_net_stop(struct net_device *dev)
7905{
7906 IPW_DEBUG_INFO("dev->close\n");
7907 netif_stop_queue(dev);
7908 return 0;
7909}
7910
7911/*
7912todo:
7913
7914modify to send one tfd per fragment instead of using chunking. otherwise
7915we need to heavily modify the ieee80211_skb_to_txb.
7916*/
7917
7918static inline void ipw_tx_skb(struct ipw_priv *priv, struct ieee80211_txb *txb)
7919{
James Ketrenos0dacca12005-09-21 12:23:41 -05007920 struct ieee80211_hdr_3addr *hdr = (struct ieee80211_hdr_3addr *)
Jeff Garzik0edd5b42005-09-07 00:48:31 -04007921 txb->fragments[0]->data;
James Ketrenos43f66a62005-03-25 12:31:53 -06007922 int i = 0;
7923 struct tfd_frame *tfd;
7924 struct clx2_tx_queue *txq = &priv->txq[0];
7925 struct clx2_queue *q = &txq->q;
7926 u8 id, hdr_len, unicast;
7927 u16 remaining_bytes;
James Ketrenosc848d0a2005-08-24 21:56:24 -05007928 int fc;
James Ketrenos43f66a62005-03-25 12:31:53 -06007929
7930 switch (priv->ieee->iw_mode) {
7931 case IW_MODE_ADHOC:
7932 hdr_len = IEEE80211_3ADDR_LEN;
7933 unicast = !is_broadcast_ether_addr(hdr->addr1) &&
Jeff Garzik0edd5b42005-09-07 00:48:31 -04007934 !is_multicast_ether_addr(hdr->addr1);
James Ketrenos43f66a62005-03-25 12:31:53 -06007935 id = ipw_find_station(priv, hdr->addr1);
7936 if (id == IPW_INVALID_STATION) {
7937 id = ipw_add_station(priv, hdr->addr1);
7938 if (id == IPW_INVALID_STATION) {
7939 IPW_WARNING("Attempt to send data to "
Jeff Garzikbf794512005-07-31 13:07:26 -04007940 "invalid cell: " MAC_FMT "\n",
James Ketrenos43f66a62005-03-25 12:31:53 -06007941 MAC_ARG(hdr->addr1));
7942 goto drop;
7943 }
7944 }
7945 break;
7946
7947 case IW_MODE_INFRA:
7948 default:
7949 unicast = !is_broadcast_ether_addr(hdr->addr3) &&
Jeff Garzik0edd5b42005-09-07 00:48:31 -04007950 !is_multicast_ether_addr(hdr->addr3);
James Ketrenos43f66a62005-03-25 12:31:53 -06007951 hdr_len = IEEE80211_3ADDR_LEN;
7952 id = 0;
7953 break;
7954 }
7955
7956 tfd = &txq->bd[q->first_empty];
7957 txq->txb[q->first_empty] = txb;
7958 memset(tfd, 0, sizeof(*tfd));
7959 tfd->u.data.station_number = id;
7960
7961 tfd->control_flags.message_type = TX_FRAME_TYPE;
7962 tfd->control_flags.control_bits = TFD_NEED_IRQ_MASK;
7963
7964 tfd->u.data.cmd_id = DINO_CMD_TX;
James Ketrenosa613bff2005-08-24 21:43:11 -05007965 tfd->u.data.len = cpu_to_le16(txb->payload_size);
James Ketrenos43f66a62005-03-25 12:31:53 -06007966 remaining_bytes = txb->payload_size;
7967 if (unlikely(!unicast))
7968 tfd->u.data.tx_flags = DCT_FLAG_NO_WEP;
7969 else
7970 tfd->u.data.tx_flags = DCT_FLAG_NO_WEP | DCT_FLAG_ACK_REQD;
Jeff Garzikbf794512005-07-31 13:07:26 -04007971
James Ketrenos43f66a62005-03-25 12:31:53 -06007972 if (priv->assoc_request.ieee_mode == IPW_B_MODE)
7973 tfd->u.data.tx_flags_ext = DCT_FLAG_EXT_MODE_CCK;
7974 else
7975 tfd->u.data.tx_flags_ext = DCT_FLAG_EXT_MODE_OFDM;
7976
James Ketrenosea2b26e2005-08-24 21:25:16 -05007977 if (priv->assoc_request.preamble_length == DCT_FLAG_SHORT_PREAMBLE)
7978 tfd->u.data.tx_flags |= DCT_FLAG_SHORT_PREAMBLE;
James Ketrenos43f66a62005-03-25 12:31:53 -06007979
James Ketrenosc848d0a2005-08-24 21:56:24 -05007980 fc = le16_to_cpu(hdr->frame_ctl);
7981 hdr->frame_ctl = cpu_to_le16(fc & ~IEEE80211_FCTL_MOREFRAGS);
7982
James Ketrenos43f66a62005-03-25 12:31:53 -06007983 memcpy(&tfd->u.data.tfd.tfd_24.mchdr, hdr, hdr_len);
7984
7985 /* payload */
James Ketrenosa613bff2005-08-24 21:43:11 -05007986 tfd->u.data.num_chunks = cpu_to_le32(min((u8) (NUM_TFD_CHUNKS - 2),
7987 txb->nr_frags));
7988 IPW_DEBUG_FRAG("%i fragments being sent as %i chunks.\n",
7989 txb->nr_frags, le32_to_cpu(tfd->u.data.num_chunks));
7990 for (i = 0; i < le32_to_cpu(tfd->u.data.num_chunks); i++) {
7991 IPW_DEBUG_FRAG("Adding fragment %i of %i (%d bytes).\n",
7992 i, le32_to_cpu(tfd->u.data.num_chunks),
7993 txb->fragments[i]->len - hdr_len);
Jeff Garzikbf794512005-07-31 13:07:26 -04007994 IPW_DEBUG_TX("Dumping TX packet frag %i of %i (%d bytes):\n",
James Ketrenos43f66a62005-03-25 12:31:53 -06007995 i, tfd->u.data.num_chunks,
7996 txb->fragments[i]->len - hdr_len);
Jeff Garzikbf794512005-07-31 13:07:26 -04007997 printk_buf(IPW_DL_TX, txb->fragments[i]->data + hdr_len,
James Ketrenos43f66a62005-03-25 12:31:53 -06007998 txb->fragments[i]->len - hdr_len);
7999
Jeff Garzik0edd5b42005-09-07 00:48:31 -04008000 tfd->u.data.chunk_ptr[i] =
James Ketrenosa613bff2005-08-24 21:43:11 -05008001 cpu_to_le32(pci_map_single
8002 (priv->pci_dev,
8003 txb->fragments[i]->data + hdr_len,
8004 txb->fragments[i]->len - hdr_len,
8005 PCI_DMA_TODEVICE));
8006 tfd->u.data.chunk_len[i] =
8007 cpu_to_le16(txb->fragments[i]->len - hdr_len);
James Ketrenos43f66a62005-03-25 12:31:53 -06008008 }
8009
8010 if (i != txb->nr_frags) {
8011 struct sk_buff *skb;
8012 u16 remaining_bytes = 0;
8013 int j;
8014
8015 for (j = i; j < txb->nr_frags; j++)
8016 remaining_bytes += txb->fragments[j]->len - hdr_len;
8017
8018 printk(KERN_INFO "Trying to reallocate for %d bytes\n",
8019 remaining_bytes);
8020 skb = alloc_skb(remaining_bytes, GFP_ATOMIC);
8021 if (skb != NULL) {
James Ketrenosa613bff2005-08-24 21:43:11 -05008022 tfd->u.data.chunk_len[i] = cpu_to_le16(remaining_bytes);
James Ketrenos43f66a62005-03-25 12:31:53 -06008023 for (j = i; j < txb->nr_frags; j++) {
8024 int size = txb->fragments[j]->len - hdr_len;
8025 printk(KERN_INFO "Adding frag %d %d...\n",
Jeff Garzik0edd5b42005-09-07 00:48:31 -04008026 j, size);
James Ketrenos43f66a62005-03-25 12:31:53 -06008027 memcpy(skb_put(skb, size),
Jeff Garzik0edd5b42005-09-07 00:48:31 -04008028 txb->fragments[j]->data + hdr_len, size);
James Ketrenos43f66a62005-03-25 12:31:53 -06008029 }
8030 dev_kfree_skb_any(txb->fragments[i]);
8031 txb->fragments[i] = skb;
Jeff Garzik0edd5b42005-09-07 00:48:31 -04008032 tfd->u.data.chunk_ptr[i] =
James Ketrenosa613bff2005-08-24 21:43:11 -05008033 cpu_to_le32(pci_map_single
8034 (priv->pci_dev, skb->data,
8035 tfd->u.data.chunk_len[i],
8036 PCI_DMA_TODEVICE));
8037
8038 tfd->u.data.num_chunks =
8039 cpu_to_le32(le32_to_cpu(tfd->u.data.num_chunks) +
8040 1);
Jeff Garzikbf794512005-07-31 13:07:26 -04008041 }
James Ketrenos43f66a62005-03-25 12:31:53 -06008042 }
8043
8044 /* kick DMA */
8045 q->first_empty = ipw_queue_inc_wrap(q->first_empty, q->n_bd);
8046 ipw_write32(priv, q->reg_w, q->first_empty);
8047
Jeff Garzikbf794512005-07-31 13:07:26 -04008048 if (ipw_queue_space(q) < q->high_mark)
James Ketrenos43f66a62005-03-25 12:31:53 -06008049 netif_stop_queue(priv->net_dev);
8050
8051 return;
8052
Jeff Garzik0edd5b42005-09-07 00:48:31 -04008053 drop:
James Ketrenos43f66a62005-03-25 12:31:53 -06008054 IPW_DEBUG_DROP("Silently dropping Tx packet.\n");
8055 ieee80211_txb_free(txb);
8056}
8057
8058static int ipw_net_hard_start_xmit(struct ieee80211_txb *txb,
James Ketrenosc8d42d12005-09-21 12:23:43 -05008059 struct net_device *dev, int pri)
James Ketrenos43f66a62005-03-25 12:31:53 -06008060{
8061 struct ipw_priv *priv = ieee80211_priv(dev);
8062 unsigned long flags;
8063
8064 IPW_DEBUG_TX("dev->xmit(%d bytes)\n", txb->payload_size);
James Ketrenos43f66a62005-03-25 12:31:53 -06008065 spin_lock_irqsave(&priv->lock, flags);
8066
8067 if (!(priv->status & STATUS_ASSOCIATED)) {
8068 IPW_DEBUG_INFO("Tx attempt while not associated.\n");
8069 priv->ieee->stats.tx_carrier_errors++;
8070 netif_stop_queue(dev);
8071 goto fail_unlock;
8072 }
8073
8074 ipw_tx_skb(priv, txb);
James Ketrenosc848d0a2005-08-24 21:56:24 -05008075 spin_unlock_irqrestore(&priv->lock, flags);
James Ketrenosa613bff2005-08-24 21:43:11 -05008076 ipw_led_activity_on(priv);
James Ketrenos43f66a62005-03-25 12:31:53 -06008077
James Ketrenosc848d0a2005-08-24 21:56:24 -05008078// up(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06008079 return 0;
8080
Jeff Garzik0edd5b42005-09-07 00:48:31 -04008081 fail_unlock:
James Ketrenos43f66a62005-03-25 12:31:53 -06008082 spin_unlock_irqrestore(&priv->lock, flags);
James Ketrenosc848d0a2005-08-24 21:56:24 -05008083// up(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06008084 return 1;
8085}
8086
8087static struct net_device_stats *ipw_net_get_stats(struct net_device *dev)
8088{
8089 struct ipw_priv *priv = ieee80211_priv(dev);
Jeff Garzikbf794512005-07-31 13:07:26 -04008090
James Ketrenos43f66a62005-03-25 12:31:53 -06008091 priv->ieee->stats.tx_packets = priv->tx_packets;
8092 priv->ieee->stats.rx_packets = priv->rx_packets;
8093 return &priv->ieee->stats;
8094}
8095
8096static void ipw_net_set_multicast_list(struct net_device *dev)
8097{
8098
8099}
8100
8101static int ipw_net_set_mac_address(struct net_device *dev, void *p)
8102{
8103 struct ipw_priv *priv = ieee80211_priv(dev);
8104 struct sockaddr *addr = p;
8105 if (!is_valid_ether_addr(addr->sa_data))
8106 return -EADDRNOTAVAIL;
James Ketrenosc848d0a2005-08-24 21:56:24 -05008107 down(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06008108 priv->config |= CFG_CUSTOM_MAC;
8109 memcpy(priv->mac_addr, addr->sa_data, ETH_ALEN);
8110 printk(KERN_INFO "%s: Setting MAC to " MAC_FMT "\n",
8111 priv->net_dev->name, MAC_ARG(priv->mac_addr));
James Ketrenosa613bff2005-08-24 21:43:11 -05008112 queue_work(priv->workqueue, &priv->adapter_restart);
James Ketrenosc848d0a2005-08-24 21:56:24 -05008113 up(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06008114 return 0;
8115}
8116
Jeff Garzikbf794512005-07-31 13:07:26 -04008117static void ipw_ethtool_get_drvinfo(struct net_device *dev,
James Ketrenos43f66a62005-03-25 12:31:53 -06008118 struct ethtool_drvinfo *info)
8119{
8120 struct ipw_priv *p = ieee80211_priv(dev);
8121 char vers[64];
8122 char date[32];
8123 u32 len;
8124
8125 strcpy(info->driver, DRV_NAME);
8126 strcpy(info->version, DRV_VERSION);
8127
8128 len = sizeof(vers);
8129 ipw_get_ordinal(p, IPW_ORD_STAT_FW_VERSION, vers, &len);
8130 len = sizeof(date);
8131 ipw_get_ordinal(p, IPW_ORD_STAT_FW_DATE, date, &len);
8132
Jeff Garzik0edd5b42005-09-07 00:48:31 -04008133 snprintf(info->fw_version, sizeof(info->fw_version), "%s (%s)",
James Ketrenos43f66a62005-03-25 12:31:53 -06008134 vers, date);
8135 strcpy(info->bus_info, pci_name(p->pci_dev));
8136 info->eedump_len = CX2_EEPROM_IMAGE_SIZE;
8137}
8138
8139static u32 ipw_ethtool_get_link(struct net_device *dev)
8140{
8141 struct ipw_priv *priv = ieee80211_priv(dev);
8142 return (priv->status & STATUS_ASSOCIATED) != 0;
8143}
8144
8145static int ipw_ethtool_get_eeprom_len(struct net_device *dev)
8146{
8147 return CX2_EEPROM_IMAGE_SIZE;
8148}
8149
8150static int ipw_ethtool_get_eeprom(struct net_device *dev,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04008151 struct ethtool_eeprom *eeprom, u8 * bytes)
James Ketrenos43f66a62005-03-25 12:31:53 -06008152{
8153 struct ipw_priv *p = ieee80211_priv(dev);
8154
8155 if (eeprom->offset + eeprom->len > CX2_EEPROM_IMAGE_SIZE)
8156 return -EINVAL;
James Ketrenosc848d0a2005-08-24 21:56:24 -05008157 down(&p->sem);
Jeff Garzik0edd5b42005-09-07 00:48:31 -04008158 memcpy(bytes, &((u8 *) p->eeprom)[eeprom->offset], eeprom->len);
James Ketrenosc848d0a2005-08-24 21:56:24 -05008159 up(&p->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06008160 return 0;
8161}
8162
8163static int ipw_ethtool_set_eeprom(struct net_device *dev,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04008164 struct ethtool_eeprom *eeprom, u8 * bytes)
James Ketrenos43f66a62005-03-25 12:31:53 -06008165{
8166 struct ipw_priv *p = ieee80211_priv(dev);
8167 int i;
8168
8169 if (eeprom->offset + eeprom->len > CX2_EEPROM_IMAGE_SIZE)
8170 return -EINVAL;
James Ketrenosc848d0a2005-08-24 21:56:24 -05008171 down(&p->sem);
Jeff Garzik0edd5b42005-09-07 00:48:31 -04008172 memcpy(&((u8 *) p->eeprom)[eeprom->offset], bytes, eeprom->len);
Jeff Garzikbf794512005-07-31 13:07:26 -04008173 for (i = IPW_EEPROM_DATA;
Jeff Garzik0edd5b42005-09-07 00:48:31 -04008174 i < IPW_EEPROM_DATA + CX2_EEPROM_IMAGE_SIZE; i++)
James Ketrenos43f66a62005-03-25 12:31:53 -06008175 ipw_write8(p, i, p->eeprom[i]);
James Ketrenosc848d0a2005-08-24 21:56:24 -05008176 up(&p->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06008177 return 0;
8178}
8179
8180static struct ethtool_ops ipw_ethtool_ops = {
James Ketrenosea2b26e2005-08-24 21:25:16 -05008181 .get_link = ipw_ethtool_get_link,
8182 .get_drvinfo = ipw_ethtool_get_drvinfo,
8183 .get_eeprom_len = ipw_ethtool_get_eeprom_len,
8184 .get_eeprom = ipw_ethtool_get_eeprom,
8185 .set_eeprom = ipw_ethtool_set_eeprom,
James Ketrenos43f66a62005-03-25 12:31:53 -06008186};
8187
8188static irqreturn_t ipw_isr(int irq, void *data, struct pt_regs *regs)
8189{
8190 struct ipw_priv *priv = data;
8191 u32 inta, inta_mask;
Jeff Garzikbf794512005-07-31 13:07:26 -04008192
James Ketrenos43f66a62005-03-25 12:31:53 -06008193 if (!priv)
8194 return IRQ_NONE;
8195
8196 spin_lock(&priv->lock);
8197
8198 if (!(priv->status & STATUS_INT_ENABLED)) {
8199 /* Shared IRQ */
James Ketrenosc848d0a2005-08-24 21:56:24 -05008200// ipw_write32(priv, CX2_INTA_RW, CX2_INTA_MASK_ALL);
8201// return IRQ_HANDLED;
James Ketrenos43f66a62005-03-25 12:31:53 -06008202 goto none;
8203 }
8204
8205 inta = ipw_read32(priv, CX2_INTA_RW);
8206 inta_mask = ipw_read32(priv, CX2_INTA_MASK_R);
Jeff Garzikbf794512005-07-31 13:07:26 -04008207
James Ketrenos43f66a62005-03-25 12:31:53 -06008208 if (inta == 0xFFFFFFFF) {
8209 /* Hardware disappeared */
8210 IPW_WARNING("IRQ INTA == 0xFFFFFFFF\n");
8211 goto none;
8212 }
8213
8214 if (!(inta & (CX2_INTA_MASK_ALL & inta_mask))) {
8215 /* Shared interrupt */
8216 goto none;
8217 }
8218
8219 /* tell the device to stop sending interrupts */
8220 ipw_disable_interrupts(priv);
Jeff Garzikbf794512005-07-31 13:07:26 -04008221
James Ketrenos43f66a62005-03-25 12:31:53 -06008222 /* ack current interrupts */
8223 inta &= (CX2_INTA_MASK_ALL & inta_mask);
8224 ipw_write32(priv, CX2_INTA_RW, inta);
Jeff Garzikbf794512005-07-31 13:07:26 -04008225
James Ketrenos43f66a62005-03-25 12:31:53 -06008226 /* Cache INTA value for our tasklet */
8227 priv->isr_inta = inta;
8228
8229 tasklet_schedule(&priv->irq_tasklet);
8230
Jeff Garzik0edd5b42005-09-07 00:48:31 -04008231 spin_unlock(&priv->lock);
James Ketrenos43f66a62005-03-25 12:31:53 -06008232
8233 return IRQ_HANDLED;
Jeff Garzik0edd5b42005-09-07 00:48:31 -04008234 none:
James Ketrenos43f66a62005-03-25 12:31:53 -06008235 spin_unlock(&priv->lock);
8236 return IRQ_NONE;
8237}
8238
8239static void ipw_rf_kill(void *adapter)
8240{
8241 struct ipw_priv *priv = adapter;
8242 unsigned long flags;
Jeff Garzikbf794512005-07-31 13:07:26 -04008243
James Ketrenos43f66a62005-03-25 12:31:53 -06008244 spin_lock_irqsave(&priv->lock, flags);
8245
8246 if (rf_kill_active(priv)) {
8247 IPW_DEBUG_RF_KILL("RF Kill active, rescheduling GPIO check\n");
8248 if (priv->workqueue)
8249 queue_delayed_work(priv->workqueue,
8250 &priv->rf_kill, 2 * HZ);
8251 goto exit_unlock;
8252 }
8253
8254 /* RF Kill is now disabled, so bring the device back up */
8255
8256 if (!(priv->status & STATUS_RF_KILL_MASK)) {
8257 IPW_DEBUG_RF_KILL("HW RF Kill no longer active, restarting "
8258 "device\n");
8259
8260 /* we can not do an adapter restart while inside an irq lock */
8261 queue_work(priv->workqueue, &priv->adapter_restart);
Jeff Garzikbf794512005-07-31 13:07:26 -04008262 } else
James Ketrenos43f66a62005-03-25 12:31:53 -06008263 IPW_DEBUG_RF_KILL("HW RF Kill deactivated. SW RF Kill still "
8264 "enabled\n");
8265
Jeff Garzik0edd5b42005-09-07 00:48:31 -04008266 exit_unlock:
James Ketrenos43f66a62005-03-25 12:31:53 -06008267 spin_unlock_irqrestore(&priv->lock, flags);
8268}
8269
James Ketrenosc848d0a2005-08-24 21:56:24 -05008270static void ipw_bg_rf_kill(void *data)
8271{
8272 struct ipw_priv *priv = data;
8273 down(&priv->sem);
8274 ipw_rf_kill(data);
8275 up(&priv->sem);
8276}
8277
James Ketrenosa613bff2005-08-24 21:43:11 -05008278void ipw_link_up(struct ipw_priv *priv)
8279{
8280 netif_carrier_on(priv->net_dev);
8281 if (netif_queue_stopped(priv->net_dev)) {
8282 IPW_DEBUG_NOTIF("waking queue\n");
8283 netif_wake_queue(priv->net_dev);
8284 } else {
8285 IPW_DEBUG_NOTIF("starting queue\n");
8286 netif_start_queue(priv->net_dev);
8287 }
8288
James Ketrenosc848d0a2005-08-24 21:56:24 -05008289 cancel_delayed_work(&priv->request_scan);
James Ketrenosa613bff2005-08-24 21:43:11 -05008290 ipw_reset_stats(priv);
8291 /* Ensure the rate is updated immediately */
8292 priv->last_rate = ipw_get_current_rate(priv);
8293 ipw_gather_stats(priv);
8294 ipw_led_link_up(priv);
8295 notify_wx_assoc_event(priv);
8296
8297 if (priv->config & CFG_BACKGROUND_SCAN)
8298 queue_delayed_work(priv->workqueue, &priv->request_scan, HZ);
8299}
8300
James Ketrenosc848d0a2005-08-24 21:56:24 -05008301static void ipw_bg_link_up(void *data)
8302{
8303 struct ipw_priv *priv = data;
8304 down(&priv->sem);
8305 ipw_link_up(data);
8306 up(&priv->sem);
8307}
8308
James Ketrenosa613bff2005-08-24 21:43:11 -05008309void ipw_link_down(struct ipw_priv *priv)
8310{
8311 ipw_led_link_down(priv);
8312 netif_carrier_off(priv->net_dev);
8313 netif_stop_queue(priv->net_dev);
8314 notify_wx_assoc_event(priv);
8315
8316 /* Cancel any queued work ... */
8317 cancel_delayed_work(&priv->request_scan);
8318 cancel_delayed_work(&priv->adhoc_check);
8319 cancel_delayed_work(&priv->gather_stats);
8320
8321 ipw_reset_stats(priv);
8322
8323 /* Queue up another scan... */
8324 queue_work(priv->workqueue, &priv->request_scan);
8325}
8326
James Ketrenosc848d0a2005-08-24 21:56:24 -05008327static void ipw_bg_link_down(void *data)
8328{
8329 struct ipw_priv *priv = data;
8330 down(&priv->sem);
8331 ipw_link_down(data);
8332 up(&priv->sem);
8333}
8334
James Ketrenos43f66a62005-03-25 12:31:53 -06008335static int ipw_setup_deferred_work(struct ipw_priv *priv)
8336{
8337 int ret = 0;
8338
James Ketrenos43f66a62005-03-25 12:31:53 -06008339 priv->workqueue = create_workqueue(DRV_NAME);
James Ketrenos43f66a62005-03-25 12:31:53 -06008340 init_waitqueue_head(&priv->wait_command_queue);
8341
James Ketrenosc848d0a2005-08-24 21:56:24 -05008342 INIT_WORK(&priv->adhoc_check, ipw_bg_adhoc_check, priv);
8343 INIT_WORK(&priv->associate, ipw_bg_associate, priv);
8344 INIT_WORK(&priv->disassociate, ipw_bg_disassociate, priv);
8345 INIT_WORK(&priv->rx_replenish, ipw_bg_rx_queue_replenish, priv);
8346 INIT_WORK(&priv->adapter_restart, ipw_bg_adapter_restart, priv);
8347 INIT_WORK(&priv->rf_kill, ipw_bg_rf_kill, priv);
8348 INIT_WORK(&priv->up, (void (*)(void *))ipw_bg_up, priv);
8349 INIT_WORK(&priv->down, (void (*)(void *))ipw_bg_down, priv);
Jeff Garzikbf794512005-07-31 13:07:26 -04008350 INIT_WORK(&priv->request_scan,
James Ketrenosc848d0a2005-08-24 21:56:24 -05008351 (void (*)(void *))ipw_bg_request_scan, priv);
Jeff Garzikbf794512005-07-31 13:07:26 -04008352 INIT_WORK(&priv->gather_stats,
James Ketrenosc848d0a2005-08-24 21:56:24 -05008353 (void (*)(void *))ipw_bg_gather_stats, priv);
8354 INIT_WORK(&priv->abort_scan, (void (*)(void *))ipw_bg_abort_scan, priv);
8355 INIT_WORK(&priv->roam, ipw_bg_roam, priv);
8356 INIT_WORK(&priv->scan_check, ipw_bg_scan_check, priv);
8357 INIT_WORK(&priv->link_up, (void (*)(void *))ipw_bg_link_up, priv);
8358 INIT_WORK(&priv->link_down, (void (*)(void *))ipw_bg_link_down, priv);
8359 INIT_WORK(&priv->led_link_on, (void (*)(void *))ipw_bg_led_link_on,
James Ketrenosa613bff2005-08-24 21:43:11 -05008360 priv);
James Ketrenosc848d0a2005-08-24 21:56:24 -05008361 INIT_WORK(&priv->led_link_off, (void (*)(void *))ipw_bg_led_link_off,
James Ketrenosa613bff2005-08-24 21:43:11 -05008362 priv);
James Ketrenosc848d0a2005-08-24 21:56:24 -05008363 INIT_WORK(&priv->led_act_off, (void (*)(void *))ipw_bg_led_activity_off,
8364 priv);
8365 INIT_WORK(&priv->merge_networks,
8366 (void (*)(void *))ipw_merge_adhoc_network, priv);
James Ketrenos43f66a62005-03-25 12:31:53 -06008367
8368 tasklet_init(&priv->irq_tasklet, (void (*)(unsigned long))
8369 ipw_irq_tasklet, (unsigned long)priv);
8370
8371 return ret;
8372}
8373
James Ketrenos43f66a62005-03-25 12:31:53 -06008374static void shim__set_security(struct net_device *dev,
8375 struct ieee80211_security *sec)
8376{
8377 struct ipw_priv *priv = ieee80211_priv(dev);
8378 int i;
James Ketrenosc848d0a2005-08-24 21:56:24 -05008379 down(&priv->sem);
Jeff Garzikbf794512005-07-31 13:07:26 -04008380 for (i = 0; i < 4; i++) {
James Ketrenos43f66a62005-03-25 12:31:53 -06008381 if (sec->flags & (1 << i)) {
8382 priv->sec.key_sizes[i] = sec->key_sizes[i];
8383 if (sec->key_sizes[i] == 0)
8384 priv->sec.flags &= ~(1 << i);
8385 else
Jeff Garzikbf794512005-07-31 13:07:26 -04008386 memcpy(priv->sec.keys[i], sec->keys[i],
James Ketrenos43f66a62005-03-25 12:31:53 -06008387 sec->key_sizes[i]);
8388 priv->sec.flags |= (1 << i);
8389 priv->status |= STATUS_SECURITY_UPDATED;
Jeff Garzikbf794512005-07-31 13:07:26 -04008390 }
James Ketrenos43f66a62005-03-25 12:31:53 -06008391 }
8392
8393 if ((sec->flags & SEC_ACTIVE_KEY) &&
8394 priv->sec.active_key != sec->active_key) {
8395 if (sec->active_key <= 3) {
8396 priv->sec.active_key = sec->active_key;
8397 priv->sec.flags |= SEC_ACTIVE_KEY;
Jeff Garzikbf794512005-07-31 13:07:26 -04008398 } else
James Ketrenos43f66a62005-03-25 12:31:53 -06008399 priv->sec.flags &= ~SEC_ACTIVE_KEY;
8400 priv->status |= STATUS_SECURITY_UPDATED;
8401 }
8402
8403 if ((sec->flags & SEC_AUTH_MODE) &&
8404 (priv->sec.auth_mode != sec->auth_mode)) {
8405 priv->sec.auth_mode = sec->auth_mode;
8406 priv->sec.flags |= SEC_AUTH_MODE;
8407 if (sec->auth_mode == WLAN_AUTH_SHARED_KEY)
8408 priv->capability |= CAP_SHARED_KEY;
8409 else
8410 priv->capability &= ~CAP_SHARED_KEY;
8411 priv->status |= STATUS_SECURITY_UPDATED;
8412 }
Jeff Garzikbf794512005-07-31 13:07:26 -04008413
Jeff Garzik0edd5b42005-09-07 00:48:31 -04008414 if (sec->flags & SEC_ENABLED && priv->sec.enabled != sec->enabled) {
James Ketrenos43f66a62005-03-25 12:31:53 -06008415 priv->sec.flags |= SEC_ENABLED;
8416 priv->sec.enabled = sec->enabled;
8417 priv->status |= STATUS_SECURITY_UPDATED;
Jeff Garzikbf794512005-07-31 13:07:26 -04008418 if (sec->enabled)
James Ketrenos43f66a62005-03-25 12:31:53 -06008419 priv->capability |= CAP_PRIVACY_ON;
8420 else
8421 priv->capability &= ~CAP_PRIVACY_ON;
8422 }
Jeff Garzikbf794512005-07-31 13:07:26 -04008423
Jeff Garzik0edd5b42005-09-07 00:48:31 -04008424 if (sec->flags & SEC_LEVEL && priv->sec.level != sec->level) {
James Ketrenos43f66a62005-03-25 12:31:53 -06008425 priv->sec.level = sec->level;
8426 priv->sec.flags |= SEC_LEVEL;
8427 priv->status |= STATUS_SECURITY_UPDATED;
8428 }
8429
Jeff Garzikbf794512005-07-31 13:07:26 -04008430 /* To match current functionality of ipw2100 (which works well w/
8431 * various supplicants, we don't force a disassociate if the
James Ketrenos43f66a62005-03-25 12:31:53 -06008432 * privacy capability changes ... */
8433#if 0
8434 if ((priv->status & (STATUS_ASSOCIATED | STATUS_ASSOCIATING)) &&
Jeff Garzikbf794512005-07-31 13:07:26 -04008435 (((priv->assoc_request.capability &
James Ketrenos43f66a62005-03-25 12:31:53 -06008436 WLAN_CAPABILITY_PRIVACY) && !sec->enabled) ||
Jeff Garzikbf794512005-07-31 13:07:26 -04008437 (!(priv->assoc_request.capability &
Jeff Garzik0edd5b42005-09-07 00:48:31 -04008438 WLAN_CAPABILITY_PRIVACY) && sec->enabled))) {
James Ketrenos43f66a62005-03-25 12:31:53 -06008439 IPW_DEBUG_ASSOC("Disassociating due to capability "
8440 "change.\n");
8441 ipw_disassociate(priv);
8442 }
8443#endif
James Ketrenosc848d0a2005-08-24 21:56:24 -05008444 up(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06008445}
8446
Jeff Garzikbf794512005-07-31 13:07:26 -04008447static int init_supported_rates(struct ipw_priv *priv,
James Ketrenos43f66a62005-03-25 12:31:53 -06008448 struct ipw_supported_rates *rates)
8449{
8450 /* TODO: Mask out rates based on priv->rates_mask */
8451
8452 memset(rates, 0, sizeof(*rates));
Jeff Garzik0edd5b42005-09-07 00:48:31 -04008453 /* configure supported rates */
James Ketrenos43f66a62005-03-25 12:31:53 -06008454 switch (priv->ieee->freq_band) {
8455 case IEEE80211_52GHZ_BAND:
8456 rates->ieee_mode = IPW_A_MODE;
8457 rates->purpose = IPW_RATE_CAPABILITIES;
8458 ipw_add_ofdm_scan_rates(rates, IEEE80211_CCK_MODULATION,
8459 IEEE80211_OFDM_DEFAULT_RATES_MASK);
8460 break;
8461
Jeff Garzik0edd5b42005-09-07 00:48:31 -04008462 default: /* Mixed or 2.4Ghz */
James Ketrenos43f66a62005-03-25 12:31:53 -06008463 rates->ieee_mode = IPW_G_MODE;
8464 rates->purpose = IPW_RATE_CAPABILITIES;
8465 ipw_add_cck_scan_rates(rates, IEEE80211_CCK_MODULATION,
8466 IEEE80211_CCK_DEFAULT_RATES_MASK);
8467 if (priv->ieee->modulation & IEEE80211_OFDM_MODULATION) {
8468 ipw_add_ofdm_scan_rates(rates, IEEE80211_CCK_MODULATION,
8469 IEEE80211_OFDM_DEFAULT_RATES_MASK);
8470 }
8471 break;
8472 }
8473
8474 return 0;
8475}
8476
Jeff Garzikbf794512005-07-31 13:07:26 -04008477static int ipw_config(struct ipw_priv *priv)
James Ketrenos43f66a62005-03-25 12:31:53 -06008478{
8479 int i;
8480 struct ipw_tx_power tx_power;
8481
8482 memset(&priv->sys_config, 0, sizeof(priv->sys_config));
8483 memset(&tx_power, 0, sizeof(tx_power));
8484
8485 /* This is only called from ipw_up, which resets/reloads the firmware
8486 so, we don't need to first disable the card before we configure
8487 it */
8488
8489 /* configure device for 'G' band */
8490 tx_power.ieee_mode = IPW_G_MODE;
8491 tx_power.num_channels = 11;
8492 for (i = 0; i < 11; i++) {
8493 tx_power.channels_tx_power[i].channel_number = i + 1;
8494 tx_power.channels_tx_power[i].tx_power = priv->tx_power;
8495 }
8496 if (ipw_send_tx_power(priv, &tx_power))
8497 goto error;
8498
8499 /* configure device to also handle 'B' band */
8500 tx_power.ieee_mode = IPW_B_MODE;
8501 if (ipw_send_tx_power(priv, &tx_power))
8502 goto error;
8503
8504 /* initialize adapter address */
8505 if (ipw_send_adapter_address(priv, priv->net_dev->dev_addr))
8506 goto error;
8507
8508 /* set basic system config settings */
8509 init_sys_config(&priv->sys_config);
James Ketrenosc848d0a2005-08-24 21:56:24 -05008510 if (priv->ieee->iw_mode == IW_MODE_ADHOC)
8511 priv->sys_config.answer_broadcast_ssid_probe = 1;
8512 else
8513 priv->sys_config.answer_broadcast_ssid_probe = 0;
8514
James Ketrenos43f66a62005-03-25 12:31:53 -06008515 if (ipw_send_system_config(priv, &priv->sys_config))
8516 goto error;
8517
Jeff Garzik0edd5b42005-09-07 00:48:31 -04008518 init_supported_rates(priv, &priv->rates);
8519 if (ipw_send_supported_rates(priv, &priv->rates))
James Ketrenos43f66a62005-03-25 12:31:53 -06008520 goto error;
8521
8522 /* Set request-to-send threshold */
8523 if (priv->rts_threshold) {
8524 if (ipw_send_rts_threshold(priv, priv->rts_threshold))
8525 goto error;
8526 }
8527
8528 if (ipw_set_random_seed(priv))
8529 goto error;
Jeff Garzikbf794512005-07-31 13:07:26 -04008530
James Ketrenos43f66a62005-03-25 12:31:53 -06008531 /* final state transition to the RUN state */
8532 if (ipw_send_host_complete(priv))
8533 goto error;
8534
8535 /* If configured to try and auto-associate, kick off a scan */
James Ketrenosc848d0a2005-08-24 21:56:24 -05008536 if ((priv->config & CFG_ASSOCIATE) && ipw_request_scan(priv)) {
8537 IPW_WARNING("error sending scan request\n");
James Ketrenos43f66a62005-03-25 12:31:53 -06008538 goto error;
James Ketrenosc848d0a2005-08-24 21:56:24 -05008539 }
James Ketrenos43f66a62005-03-25 12:31:53 -06008540
8541 return 0;
Jeff Garzikbf794512005-07-31 13:07:26 -04008542
Jeff Garzik0edd5b42005-09-07 00:48:31 -04008543 error:
James Ketrenos43f66a62005-03-25 12:31:53 -06008544 return -EIO;
8545}
8546
8547#define MAX_HW_RESTARTS 5
8548static int ipw_up(struct ipw_priv *priv)
8549{
8550 int rc, i;
8551
8552 if (priv->status & STATUS_EXIT_PENDING)
8553 return -EIO;
8554
Jeff Garzik0edd5b42005-09-07 00:48:31 -04008555 for (i = 0; i < MAX_HW_RESTARTS; i++) {
Jeff Garzikbf794512005-07-31 13:07:26 -04008556 /* Load the microcode, firmware, and eeprom.
James Ketrenos43f66a62005-03-25 12:31:53 -06008557 * Also start the clocks. */
8558 rc = ipw_load(priv);
8559 if (rc) {
Jeff Garzik0edd5b42005-09-07 00:48:31 -04008560 IPW_ERROR("Unable to load firmware: 0x%08X\n", rc);
James Ketrenos43f66a62005-03-25 12:31:53 -06008561 return rc;
8562 }
8563
8564 ipw_init_ordinals(priv);
8565 if (!(priv->config & CFG_CUSTOM_MAC))
8566 eeprom_parse_mac(priv, priv->mac_addr);
8567 memcpy(priv->net_dev->dev_addr, priv->mac_addr, ETH_ALEN);
8568
James Ketrenosc848d0a2005-08-24 21:56:24 -05008569 if (priv->status & STATUS_RF_KILL_MASK) {
James Ketrenos43f66a62005-03-25 12:31:53 -06008570 return 0;
James Ketrenosc848d0a2005-08-24 21:56:24 -05008571 }
James Ketrenos43f66a62005-03-25 12:31:53 -06008572
8573 rc = ipw_config(priv);
8574 if (!rc) {
8575 IPW_DEBUG_INFO("Configured device on count %i\n", i);
James Ketrenosa613bff2005-08-24 21:43:11 -05008576 ipw_led_init(priv);
8577 ipw_led_radio_on(priv);
James Ketrenos43f66a62005-03-25 12:31:53 -06008578 priv->notif_missed_beacons = 0;
James Ketrenosc848d0a2005-08-24 21:56:24 -05008579 priv->status |= STATUS_INIT;
James Ketrenos43f66a62005-03-25 12:31:53 -06008580 return 0;
James Ketrenos43f66a62005-03-25 12:31:53 -06008581 }
Jeff Garzikbf794512005-07-31 13:07:26 -04008582
James Ketrenosc848d0a2005-08-24 21:56:24 -05008583 IPW_DEBUG_INFO("Device configuration failed: 0x%08X\n", rc);
James Ketrenos43f66a62005-03-25 12:31:53 -06008584 IPW_DEBUG_INFO("Failed to config device on retry %d of %d\n",
8585 i, MAX_HW_RESTARTS);
8586
8587 /* We had an error bringing up the hardware, so take it
8588 * all the way back down so we can try again */
8589 ipw_down(priv);
8590 }
8591
Jeff Garzikbf794512005-07-31 13:07:26 -04008592 /* tried to restart and config the device for as long as our
James Ketrenos43f66a62005-03-25 12:31:53 -06008593 * patience could withstand */
Jeff Garzik0edd5b42005-09-07 00:48:31 -04008594 IPW_ERROR("Unable to initialize device after %d attempts.\n", i);
James Ketrenosc848d0a2005-08-24 21:56:24 -05008595
James Ketrenos43f66a62005-03-25 12:31:53 -06008596 return -EIO;
8597}
8598
James Ketrenosc848d0a2005-08-24 21:56:24 -05008599static void ipw_bg_up(void *data)
8600{
8601 struct ipw_priv *priv = data;
8602 down(&priv->sem);
8603 ipw_up(data);
8604 up(&priv->sem);
8605}
8606
James Ketrenos43f66a62005-03-25 12:31:53 -06008607static void ipw_down(struct ipw_priv *priv)
8608{
James Ketrenos43f66a62005-03-25 12:31:53 -06008609#if 0
James Ketrenosc848d0a2005-08-24 21:56:24 -05008610 /* Attempt to disable the card */
James Ketrenos43f66a62005-03-25 12:31:53 -06008611 ipw_send_card_disable(priv, 0);
8612#endif
8613
8614 /* tell the device to stop sending interrupts */
8615 ipw_disable_interrupts(priv);
8616
8617 /* Clear all bits but the RF Kill */
8618 priv->status &= STATUS_RF_KILL_MASK;
James Ketrenos43f66a62005-03-25 12:31:53 -06008619 netif_carrier_off(priv->net_dev);
8620 netif_stop_queue(priv->net_dev);
8621
8622 ipw_stop_nic(priv);
James Ketrenosa613bff2005-08-24 21:43:11 -05008623
8624 ipw_led_radio_off(priv);
James Ketrenos43f66a62005-03-25 12:31:53 -06008625}
8626
James Ketrenosc848d0a2005-08-24 21:56:24 -05008627static void ipw_bg_down(void *data)
8628{
8629 struct ipw_priv *priv = data;
8630 down(&priv->sem);
8631 ipw_down(data);
8632 up(&priv->sem);
8633}
8634
James Ketrenosea2b26e2005-08-24 21:25:16 -05008635static int ipw_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
8636{
James Ketrenosea2b26e2005-08-24 21:25:16 -05008637 struct iwreq *wrq = (struct iwreq *)rq;
8638 int ret = -1;
8639 switch (cmd) {
8640 case IPW_IOCTL_WPA_SUPPLICANT:
8641 ret = ipw_wpa_supplicant(dev, &wrq->u.data);
8642 return ret;
8643
8644 default:
8645 return -EOPNOTSUPP;
8646 }
8647
James Ketrenosea2b26e2005-08-24 21:25:16 -05008648 return -EOPNOTSUPP;
8649}
8650
James Ketrenos43f66a62005-03-25 12:31:53 -06008651/* Called by register_netdev() */
8652static int ipw_net_init(struct net_device *dev)
8653{
8654 struct ipw_priv *priv = ieee80211_priv(dev);
James Ketrenosc848d0a2005-08-24 21:56:24 -05008655 down(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06008656 if (priv->status & STATUS_RF_KILL_SW) {
8657 IPW_WARNING("Radio disabled by module parameter.\n");
James Ketrenosc848d0a2005-08-24 21:56:24 -05008658 up(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06008659 return 0;
8660 } else if (rf_kill_active(priv)) {
8661 IPW_WARNING("Radio Frequency Kill Switch is On:\n"
8662 "Kill switch must be turned off for "
8663 "wireless networking to work.\n");
8664 queue_delayed_work(priv->workqueue, &priv->rf_kill, 2 * HZ);
James Ketrenosc848d0a2005-08-24 21:56:24 -05008665 up(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06008666 return 0;
8667 }
8668
James Ketrenosc848d0a2005-08-24 21:56:24 -05008669 if (ipw_up(priv)) {
8670 up(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06008671 return -EIO;
James Ketrenosc848d0a2005-08-24 21:56:24 -05008672 }
James Ketrenos43f66a62005-03-25 12:31:53 -06008673
James Ketrenosc848d0a2005-08-24 21:56:24 -05008674 up(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06008675 return 0;
8676}
8677
8678/* PCI driver stuff */
8679static struct pci_device_id card_ids[] = {
8680 {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2701, 0, 0, 0},
8681 {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2702, 0, 0, 0},
8682 {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2711, 0, 0, 0},
8683 {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2712, 0, 0, 0},
8684 {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2721, 0, 0, 0},
8685 {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2722, 0, 0, 0},
8686 {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2731, 0, 0, 0},
8687 {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2732, 0, 0, 0},
8688 {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2741, 0, 0, 0},
8689 {PCI_VENDOR_ID_INTEL, 0x1043, 0x103c, 0x2741, 0, 0, 0},
8690 {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2742, 0, 0, 0},
8691 {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2751, 0, 0, 0},
8692 {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2752, 0, 0, 0},
8693 {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2753, 0, 0, 0},
8694 {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2754, 0, 0, 0},
8695 {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2761, 0, 0, 0},
8696 {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2762, 0, 0, 0},
8697 {PCI_VENDOR_ID_INTEL, 0x104f, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
Jeff Garzik0edd5b42005-09-07 00:48:31 -04008698 {PCI_VENDOR_ID_INTEL, 0x4220, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, /* BG */
James Ketrenosa613bff2005-08-24 21:43:11 -05008699 {PCI_VENDOR_ID_INTEL, 0x4221, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, /* BG */
Jeff Garzik0edd5b42005-09-07 00:48:31 -04008700 {PCI_VENDOR_ID_INTEL, 0x4223, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, /* ABG */
8701 {PCI_VENDOR_ID_INTEL, 0x4224, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, /* ABG */
Jeff Garzikbf794512005-07-31 13:07:26 -04008702
James Ketrenos43f66a62005-03-25 12:31:53 -06008703 /* required last entry */
8704 {0,}
8705};
8706
8707MODULE_DEVICE_TABLE(pci, card_ids);
8708
8709static struct attribute *ipw_sysfs_entries[] = {
8710 &dev_attr_rf_kill.attr,
8711 &dev_attr_direct_dword.attr,
8712 &dev_attr_indirect_byte.attr,
8713 &dev_attr_indirect_dword.attr,
8714 &dev_attr_mem_gpio_reg.attr,
8715 &dev_attr_command_event_reg.attr,
8716 &dev_attr_nic_type.attr,
8717 &dev_attr_status.attr,
8718 &dev_attr_cfg.attr,
8719 &dev_attr_dump_errors.attr,
8720 &dev_attr_dump_events.attr,
8721 &dev_attr_eeprom_delay.attr,
8722 &dev_attr_ucode_version.attr,
8723 &dev_attr_rtc.attr,
James Ketrenosa613bff2005-08-24 21:43:11 -05008724 &dev_attr_scan_age.attr,
8725 &dev_attr_led.attr,
James Ketrenos43f66a62005-03-25 12:31:53 -06008726 NULL
8727};
8728
8729static struct attribute_group ipw_attribute_group = {
8730 .name = NULL, /* put in device directory */
Jeff Garzik0edd5b42005-09-07 00:48:31 -04008731 .attrs = ipw_sysfs_entries,
James Ketrenos43f66a62005-03-25 12:31:53 -06008732};
8733
Jeff Garzik0edd5b42005-09-07 00:48:31 -04008734static int ipw_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
James Ketrenos43f66a62005-03-25 12:31:53 -06008735{
8736 int err = 0;
8737 struct net_device *net_dev;
8738 void __iomem *base;
8739 u32 length, val;
8740 struct ipw_priv *priv;
8741 int band, modulation;
8742
8743 net_dev = alloc_ieee80211(sizeof(struct ipw_priv));
8744 if (net_dev == NULL) {
8745 err = -ENOMEM;
8746 goto out;
8747 }
8748
8749 priv = ieee80211_priv(net_dev);
8750 priv->ieee = netdev_priv(net_dev);
James Ketrenosa613bff2005-08-24 21:43:11 -05008751
James Ketrenos43f66a62005-03-25 12:31:53 -06008752 priv->net_dev = net_dev;
8753 priv->pci_dev = pdev;
8754#ifdef CONFIG_IPW_DEBUG
8755 ipw_debug_level = debug;
8756#endif
8757 spin_lock_init(&priv->lock);
8758
James Ketrenosc848d0a2005-08-24 21:56:24 -05008759 init_MUTEX(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06008760 if (pci_enable_device(pdev)) {
8761 err = -ENODEV;
8762 goto out_free_ieee80211;
8763 }
8764
8765 pci_set_master(pdev);
8766
Tobias Klauser0e08b442005-06-20 14:28:41 -07008767 err = pci_set_dma_mask(pdev, DMA_32BIT_MASK);
Jeff Garzikbf794512005-07-31 13:07:26 -04008768 if (!err)
Tobias Klauser0e08b442005-06-20 14:28:41 -07008769 err = pci_set_consistent_dma_mask(pdev, DMA_32BIT_MASK);
James Ketrenos43f66a62005-03-25 12:31:53 -06008770 if (err) {
8771 printk(KERN_WARNING DRV_NAME ": No suitable DMA available.\n");
8772 goto out_pci_disable_device;
8773 }
8774
8775 pci_set_drvdata(pdev, priv);
8776
8777 err = pci_request_regions(pdev, DRV_NAME);
Jeff Garzikbf794512005-07-31 13:07:26 -04008778 if (err)
James Ketrenos43f66a62005-03-25 12:31:53 -06008779 goto out_pci_disable_device;
8780
Jeff Garzikbf794512005-07-31 13:07:26 -04008781 /* We disable the RETRY_TIMEOUT register (0x41) to keep
James Ketrenos43f66a62005-03-25 12:31:53 -06008782 * PCI Tx retries from interfering with C3 CPU state */
Jeff Garzikbf794512005-07-31 13:07:26 -04008783 pci_read_config_dword(pdev, 0x40, &val);
8784 if ((val & 0x0000ff00) != 0)
James Ketrenos43f66a62005-03-25 12:31:53 -06008785 pci_write_config_dword(pdev, 0x40, val & 0xffff00ff);
Jeff Garzikbf794512005-07-31 13:07:26 -04008786
James Ketrenos43f66a62005-03-25 12:31:53 -06008787 length = pci_resource_len(pdev, 0);
8788 priv->hw_len = length;
Jeff Garzikbf794512005-07-31 13:07:26 -04008789
James Ketrenos43f66a62005-03-25 12:31:53 -06008790 base = ioremap_nocache(pci_resource_start(pdev, 0), length);
8791 if (!base) {
8792 err = -ENODEV;
8793 goto out_pci_release_regions;
8794 }
8795
8796 priv->hw_base = base;
8797 IPW_DEBUG_INFO("pci_resource_len = 0x%08x\n", length);
8798 IPW_DEBUG_INFO("pci_resource_base = %p\n", base);
8799
8800 err = ipw_setup_deferred_work(priv);
8801 if (err) {
8802 IPW_ERROR("Unable to setup deferred work\n");
8803 goto out_iounmap;
8804 }
8805
8806 /* Initialize module parameter values here */
James Ketrenosa613bff2005-08-24 21:43:11 -05008807
8808 /* We default to disabling the LED code as right now it causes
8809 * too many systems to lock up... */
8810 if (!led)
8811 priv->config |= CFG_NO_LED;
8812
Jeff Garzikbf794512005-07-31 13:07:26 -04008813 if (associate)
James Ketrenos43f66a62005-03-25 12:31:53 -06008814 priv->config |= CFG_ASSOCIATE;
8815 else
8816 IPW_DEBUG_INFO("Auto associate disabled.\n");
Jeff Garzikbf794512005-07-31 13:07:26 -04008817
8818 if (auto_create)
James Ketrenos43f66a62005-03-25 12:31:53 -06008819 priv->config |= CFG_ADHOC_CREATE;
8820 else
8821 IPW_DEBUG_INFO("Auto adhoc creation disabled.\n");
Jeff Garzikbf794512005-07-31 13:07:26 -04008822
James Ketrenos43f66a62005-03-25 12:31:53 -06008823 if (disable) {
8824 priv->status |= STATUS_RF_KILL_SW;
8825 IPW_DEBUG_INFO("Radio disabled.\n");
8826 }
8827
8828 if (channel != 0) {
8829 priv->config |= CFG_STATIC_CHANNEL;
8830 priv->channel = channel;
8831 IPW_DEBUG_INFO("Bind to static channel %d\n", channel);
Jeff Garzik0edd5b42005-09-07 00:48:31 -04008832 IPW_DEBUG_INFO("Bind to static channel %d\n", channel);
James Ketrenos43f66a62005-03-25 12:31:53 -06008833 /* TODO: Validate that provided channel is in range */
8834 }
8835
8836 switch (mode) {
8837 case 1:
8838 priv->ieee->iw_mode = IW_MODE_ADHOC;
8839 break;
James Ketrenosea2b26e2005-08-24 21:25:16 -05008840#ifdef CONFIG_IPW_MONITOR
James Ketrenos43f66a62005-03-25 12:31:53 -06008841 case 2:
8842 priv->ieee->iw_mode = IW_MODE_MONITOR;
8843 break;
8844#endif
8845 default:
8846 case 0:
8847 priv->ieee->iw_mode = IW_MODE_INFRA;
8848 break;
8849 }
8850
8851 if ((priv->pci_dev->device == 0x4223) ||
8852 (priv->pci_dev->device == 0x4224)) {
Jeff Garzikbf794512005-07-31 13:07:26 -04008853 printk(KERN_INFO DRV_NAME
James Ketrenos43f66a62005-03-25 12:31:53 -06008854 ": Detected Intel PRO/Wireless 2915ABG Network "
8855 "Connection\n");
James Ketrenosa33a1982005-09-14 14:28:59 -05008856 priv->ieee->abg_true = 1;
James Ketrenos43f66a62005-03-25 12:31:53 -06008857 band = IEEE80211_52GHZ_BAND | IEEE80211_24GHZ_BAND;
8858 modulation = IEEE80211_OFDM_MODULATION |
Jeff Garzik0edd5b42005-09-07 00:48:31 -04008859 IEEE80211_CCK_MODULATION;
James Ketrenos43f66a62005-03-25 12:31:53 -06008860 priv->adapter = IPW_2915ABG;
Jeff Garzik0edd5b42005-09-07 00:48:31 -04008861 priv->ieee->mode = IEEE_A | IEEE_G | IEEE_B;
James Ketrenos43f66a62005-03-25 12:31:53 -06008862 } else {
James Ketrenosa613bff2005-08-24 21:43:11 -05008863 printk(KERN_INFO DRV_NAME
8864 ": Detected Intel PRO/Wireless 2200BG Network "
8865 "Connection\n");
Jeff Garzikbf794512005-07-31 13:07:26 -04008866
James Ketrenosa33a1982005-09-14 14:28:59 -05008867 priv->ieee->abg_true = 0;
James Ketrenos43f66a62005-03-25 12:31:53 -06008868 band = IEEE80211_24GHZ_BAND;
8869 modulation = IEEE80211_OFDM_MODULATION |
Jeff Garzik0edd5b42005-09-07 00:48:31 -04008870 IEEE80211_CCK_MODULATION;
James Ketrenos43f66a62005-03-25 12:31:53 -06008871 priv->adapter = IPW_2200BG;
Jeff Garzik0edd5b42005-09-07 00:48:31 -04008872 priv->ieee->mode = IEEE_G | IEEE_B;
James Ketrenos43f66a62005-03-25 12:31:53 -06008873 }
8874
8875 priv->ieee->freq_band = band;
8876 priv->ieee->modulation = modulation;
8877
8878 priv->rates_mask = IEEE80211_DEFAULT_RATES_MASK;
8879
8880 priv->missed_beacon_threshold = IPW_MB_DISASSOCIATE_THRESHOLD_DEFAULT;
8881 priv->roaming_threshold = IPW_MB_ROAMING_THRESHOLD_DEFAULT;
8882
8883 priv->rts_threshold = DEFAULT_RTS_THRESHOLD;
8884
8885 /* If power management is turned on, default to AC mode */
Jeff Garzik0edd5b42005-09-07 00:48:31 -04008886 priv->power_mode = IPW_POWER_AC;
James Ketrenos43f66a62005-03-25 12:31:53 -06008887 priv->tx_power = IPW_DEFAULT_TX_POWER;
8888
Jeff Garzik0edd5b42005-09-07 00:48:31 -04008889 err = request_irq(pdev->irq, ipw_isr, SA_SHIRQ, DRV_NAME, priv);
James Ketrenos43f66a62005-03-25 12:31:53 -06008890 if (err) {
8891 IPW_ERROR("Error allocating IRQ %d\n", pdev->irq);
8892 goto out_destroy_workqueue;
8893 }
8894
8895 SET_MODULE_OWNER(net_dev);
8896 SET_NETDEV_DEV(net_dev, &pdev->dev);
8897
James Ketrenosa613bff2005-08-24 21:43:11 -05008898 ipw_wx_data.spy_data = &priv->ieee->spy_data;
8899 ipw_wx_data.ieee80211 = priv->ieee;
8900
James Ketrenosc848d0a2005-08-24 21:56:24 -05008901 down(&priv->sem);
8902
James Ketrenos43f66a62005-03-25 12:31:53 -06008903 priv->ieee->hard_start_xmit = ipw_net_hard_start_xmit;
8904 priv->ieee->set_security = shim__set_security;
8905
James Ketrenosc848d0a2005-08-24 21:56:24 -05008906 priv->ieee->perfect_rssi = -20;
8907 priv->ieee->worst_rssi = -85;
8908
James Ketrenos43f66a62005-03-25 12:31:53 -06008909 net_dev->open = ipw_net_open;
8910 net_dev->stop = ipw_net_stop;
8911 net_dev->init = ipw_net_init;
James Ketrenosea2b26e2005-08-24 21:25:16 -05008912 net_dev->do_ioctl = ipw_ioctl;
James Ketrenos43f66a62005-03-25 12:31:53 -06008913 net_dev->get_stats = ipw_net_get_stats;
8914 net_dev->set_multicast_list = ipw_net_set_multicast_list;
8915 net_dev->set_mac_address = ipw_net_set_mac_address;
8916 net_dev->get_wireless_stats = ipw_get_wireless_stats;
James Ketrenosa613bff2005-08-24 21:43:11 -05008917 net_dev->wireless_data = &ipw_wx_data;
James Ketrenos43f66a62005-03-25 12:31:53 -06008918 net_dev->wireless_handlers = &ipw_wx_handler_def;
8919 net_dev->ethtool_ops = &ipw_ethtool_ops;
8920 net_dev->irq = pdev->irq;
Jeff Garzik0edd5b42005-09-07 00:48:31 -04008921 net_dev->base_addr = (unsigned long)priv->hw_base;
James Ketrenos43f66a62005-03-25 12:31:53 -06008922 net_dev->mem_start = pci_resource_start(pdev, 0);
8923 net_dev->mem_end = net_dev->mem_start + pci_resource_len(pdev, 0) - 1;
8924
8925 err = sysfs_create_group(&pdev->dev.kobj, &ipw_attribute_group);
8926 if (err) {
8927 IPW_ERROR("failed to create sysfs device attributes\n");
James Ketrenosc848d0a2005-08-24 21:56:24 -05008928 up(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06008929 goto out_release_irq;
8930 }
8931
James Ketrenosc848d0a2005-08-24 21:56:24 -05008932 up(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06008933 err = register_netdev(net_dev);
8934 if (err) {
8935 IPW_ERROR("failed to register network device\n");
James Ketrenosa613bff2005-08-24 21:43:11 -05008936 goto out_remove_sysfs;
James Ketrenos43f66a62005-03-25 12:31:53 -06008937 }
James Ketrenos43f66a62005-03-25 12:31:53 -06008938 return 0;
8939
James Ketrenosa613bff2005-08-24 21:43:11 -05008940 out_remove_sysfs:
James Ketrenos43f66a62005-03-25 12:31:53 -06008941 sysfs_remove_group(&pdev->dev.kobj, &ipw_attribute_group);
Jeff Garzik0edd5b42005-09-07 00:48:31 -04008942 out_release_irq:
James Ketrenos43f66a62005-03-25 12:31:53 -06008943 free_irq(pdev->irq, priv);
Jeff Garzik0edd5b42005-09-07 00:48:31 -04008944 out_destroy_workqueue:
James Ketrenos43f66a62005-03-25 12:31:53 -06008945 destroy_workqueue(priv->workqueue);
8946 priv->workqueue = NULL;
Jeff Garzik0edd5b42005-09-07 00:48:31 -04008947 out_iounmap:
James Ketrenos43f66a62005-03-25 12:31:53 -06008948 iounmap(priv->hw_base);
Jeff Garzik0edd5b42005-09-07 00:48:31 -04008949 out_pci_release_regions:
James Ketrenos43f66a62005-03-25 12:31:53 -06008950 pci_release_regions(pdev);
Jeff Garzik0edd5b42005-09-07 00:48:31 -04008951 out_pci_disable_device:
James Ketrenos43f66a62005-03-25 12:31:53 -06008952 pci_disable_device(pdev);
8953 pci_set_drvdata(pdev, NULL);
Jeff Garzik0edd5b42005-09-07 00:48:31 -04008954 out_free_ieee80211:
James Ketrenos43f66a62005-03-25 12:31:53 -06008955 free_ieee80211(priv->net_dev);
Jeff Garzik0edd5b42005-09-07 00:48:31 -04008956 out:
James Ketrenos43f66a62005-03-25 12:31:53 -06008957 return err;
8958}
8959
8960static void ipw_pci_remove(struct pci_dev *pdev)
8961{
8962 struct ipw_priv *priv = pci_get_drvdata(pdev);
8963 if (!priv)
8964 return;
8965
8966 priv->status |= STATUS_EXIT_PENDING;
8967
8968 sysfs_remove_group(&pdev->dev.kobj, &ipw_attribute_group);
8969
8970 ipw_down(priv);
8971
8972 unregister_netdev(priv->net_dev);
8973
8974 if (priv->rxq) {
8975 ipw_rx_queue_free(priv, priv->rxq);
8976 priv->rxq = NULL;
8977 }
8978 ipw_tx_queue_free(priv);
8979
James Ketrenosa613bff2005-08-24 21:43:11 -05008980 ipw_led_shutdown(priv);
8981
James Ketrenos43f66a62005-03-25 12:31:53 -06008982 /* ipw_down will ensure that there is no more pending work
8983 * in the workqueue's, so we can safely remove them now. */
James Ketrenosa613bff2005-08-24 21:43:11 -05008984 cancel_delayed_work(&priv->adhoc_check);
8985 cancel_delayed_work(&priv->gather_stats);
8986 cancel_delayed_work(&priv->request_scan);
8987 cancel_delayed_work(&priv->rf_kill);
8988 cancel_delayed_work(&priv->scan_check);
8989 destroy_workqueue(priv->workqueue);
8990 priv->workqueue = NULL;
James Ketrenos43f66a62005-03-25 12:31:53 -06008991
8992 free_irq(pdev->irq, priv);
8993 iounmap(priv->hw_base);
8994 pci_release_regions(pdev);
8995 pci_disable_device(pdev);
8996 pci_set_drvdata(pdev, NULL);
8997 free_ieee80211(priv->net_dev);
8998
8999#ifdef CONFIG_PM
9000 if (fw_loaded) {
9001 release_firmware(bootfw);
9002 release_firmware(ucode);
9003 release_firmware(firmware);
9004 fw_loaded = 0;
9005 }
9006#endif
9007}
9008
James Ketrenos43f66a62005-03-25 12:31:53 -06009009#ifdef CONFIG_PM
Pavel Machek583a4e82005-09-03 15:56:58 -07009010static int ipw_pci_suspend(struct pci_dev *pdev, pm_message_t state)
James Ketrenos43f66a62005-03-25 12:31:53 -06009011{
9012 struct ipw_priv *priv = pci_get_drvdata(pdev);
9013 struct net_device *dev = priv->net_dev;
9014
9015 printk(KERN_INFO "%s: Going into suspend...\n", dev->name);
9016
Jeff Garzik0edd5b42005-09-07 00:48:31 -04009017 /* Take down the device; powers it off, etc. */
James Ketrenos43f66a62005-03-25 12:31:53 -06009018 ipw_down(priv);
9019
9020 /* Remove the PRESENT state of the device */
9021 netif_device_detach(dev);
9022
James Ketrenos43f66a62005-03-25 12:31:53 -06009023 pci_save_state(pdev);
James Ketrenos43f66a62005-03-25 12:31:53 -06009024 pci_disable_device(pdev);
Pavel Machek583a4e82005-09-03 15:56:58 -07009025 pci_set_power_state(pdev, pci_choose_state(pdev, state));
Jeff Garzikbf794512005-07-31 13:07:26 -04009026
James Ketrenos43f66a62005-03-25 12:31:53 -06009027 return 0;
9028}
9029
9030static int ipw_pci_resume(struct pci_dev *pdev)
9031{
9032 struct ipw_priv *priv = pci_get_drvdata(pdev);
9033 struct net_device *dev = priv->net_dev;
9034 u32 val;
Jeff Garzikbf794512005-07-31 13:07:26 -04009035
James Ketrenos43f66a62005-03-25 12:31:53 -06009036 printk(KERN_INFO "%s: Coming out of suspend...\n", dev->name);
9037
James Ketrenosea2b26e2005-08-24 21:25:16 -05009038 pci_set_power_state(pdev, PCI_D0);
James Ketrenos43f66a62005-03-25 12:31:53 -06009039 pci_enable_device(pdev);
James Ketrenos43f66a62005-03-25 12:31:53 -06009040 pci_restore_state(pdev);
James Ketrenosea2b26e2005-08-24 21:25:16 -05009041
James Ketrenos43f66a62005-03-25 12:31:53 -06009042 /*
9043 * Suspend/Resume resets the PCI configuration space, so we have to
9044 * re-disable the RETRY_TIMEOUT register (0x41) to keep PCI Tx retries
9045 * from interfering with C3 CPU state. pci_restore_state won't help
9046 * here since it only restores the first 64 bytes pci config header.
9047 */
Jeff Garzikbf794512005-07-31 13:07:26 -04009048 pci_read_config_dword(pdev, 0x40, &val);
9049 if ((val & 0x0000ff00) != 0)
James Ketrenos43f66a62005-03-25 12:31:53 -06009050 pci_write_config_dword(pdev, 0x40, val & 0xffff00ff);
9051
9052 /* Set the device back into the PRESENT state; this will also wake
9053 * the queue of needed */
9054 netif_device_attach(dev);
9055
9056 /* Bring the device back up */
9057 queue_work(priv->workqueue, &priv->up);
Jeff Garzikbf794512005-07-31 13:07:26 -04009058
James Ketrenos43f66a62005-03-25 12:31:53 -06009059 return 0;
9060}
9061#endif
9062
9063/* driver initialization stuff */
9064static struct pci_driver ipw_driver = {
9065 .name = DRV_NAME,
9066 .id_table = card_ids,
9067 .probe = ipw_pci_probe,
9068 .remove = __devexit_p(ipw_pci_remove),
9069#ifdef CONFIG_PM
9070 .suspend = ipw_pci_suspend,
9071 .resume = ipw_pci_resume,
9072#endif
9073};
9074
9075static int __init ipw_init(void)
9076{
9077 int ret;
9078
9079 printk(KERN_INFO DRV_NAME ": " DRV_DESCRIPTION ", " DRV_VERSION "\n");
9080 printk(KERN_INFO DRV_NAME ": " DRV_COPYRIGHT "\n");
9081
9082 ret = pci_module_init(&ipw_driver);
9083 if (ret) {
9084 IPW_ERROR("Unable to initialize PCI module\n");
9085 return ret;
9086 }
9087
Jeff Garzik0edd5b42005-09-07 00:48:31 -04009088 ret = driver_create_file(&ipw_driver.driver, &driver_attr_debug_level);
James Ketrenos43f66a62005-03-25 12:31:53 -06009089 if (ret) {
9090 IPW_ERROR("Unable to create driver sysfs file\n");
9091 pci_unregister_driver(&ipw_driver);
9092 return ret;
9093 }
9094
9095 return ret;
9096}
9097
9098static void __exit ipw_exit(void)
9099{
9100 driver_remove_file(&ipw_driver.driver, &driver_attr_debug_level);
9101 pci_unregister_driver(&ipw_driver);
9102}
9103
9104module_param(disable, int, 0444);
9105MODULE_PARM_DESC(disable, "manually disable the radio (default 0 [radio on])");
9106
9107module_param(associate, int, 0444);
9108MODULE_PARM_DESC(associate, "auto associate when scanning (default on)");
9109
9110module_param(auto_create, int, 0444);
9111MODULE_PARM_DESC(auto_create, "auto create adhoc network (default on)");
9112
James Ketrenosa613bff2005-08-24 21:43:11 -05009113module_param(led, int, 0444);
James Ketrenosc848d0a2005-08-24 21:56:24 -05009114MODULE_PARM_DESC(led, "enable led control on some systems (default 0 off)\n");
James Ketrenosa613bff2005-08-24 21:43:11 -05009115
James Ketrenos43f66a62005-03-25 12:31:53 -06009116module_param(debug, int, 0444);
9117MODULE_PARM_DESC(debug, "debug output mask");
9118
9119module_param(channel, int, 0444);
Jeff Garzikbf794512005-07-31 13:07:26 -04009120MODULE_PARM_DESC(channel, "channel to limit associate to (default 0 [ANY])");
James Ketrenos43f66a62005-03-25 12:31:53 -06009121
James Ketrenosea2b26e2005-08-24 21:25:16 -05009122#ifdef CONFIG_IPW_MONITOR
James Ketrenos43f66a62005-03-25 12:31:53 -06009123module_param(mode, int, 0444);
9124MODULE_PARM_DESC(mode, "network mode (0=BSS,1=IBSS,2=Monitor)");
9125#else
9126module_param(mode, int, 0444);
9127MODULE_PARM_DESC(mode, "network mode (0=BSS,1=IBSS)");
9128#endif
9129
9130module_exit(ipw_exit);
9131module_init(ipw_init);