blob: 589aef8ba4e67f766f3b8440ea553816442836c3 [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"
Olaf Hering733482e2005-11-08 21:34:55 -080034#include <linux/version.h>
James Ketrenos43f66a62005-03-25 12:31:53 -060035
36#define IPW2200_VERSION "1.0.0"
37#define DRV_DESCRIPTION "Intel(R) PRO/Wireless 2200/2915 Network Driver"
38#define DRV_COPYRIGHT "Copyright(c) 2003-2004 Intel Corporation"
39#define DRV_VERSION IPW2200_VERSION
40
41MODULE_DESCRIPTION(DRV_DESCRIPTION);
42MODULE_VERSION(DRV_VERSION);
43MODULE_AUTHOR(DRV_COPYRIGHT);
44MODULE_LICENSE("GPL");
45
46static int debug = 0;
47static int channel = 0;
48static char *ifname;
49static int mode = 0;
50
51static u32 ipw_debug_level;
52static int associate = 1;
53static int auto_create = 1;
54static int disable = 0;
55static const char ipw_modes[] = {
56 'a', 'b', 'g', '?'
57};
58
59static void ipw_rx(struct ipw_priv *priv);
Jeff Garzikbf794512005-07-31 13:07:26 -040060static int ipw_queue_tx_reclaim(struct ipw_priv *priv,
James Ketrenos43f66a62005-03-25 12:31:53 -060061 struct clx2_tx_queue *txq, int qindex);
62static int ipw_queue_reset(struct ipw_priv *priv);
63
64static int ipw_queue_tx_hcmd(struct ipw_priv *priv, int hcmd, void *buf,
65 int len, int sync);
66
67static void ipw_tx_queue_free(struct ipw_priv *);
68
69static struct ipw_rx_queue *ipw_rx_queue_alloc(struct ipw_priv *);
70static void ipw_rx_queue_free(struct ipw_priv *, struct ipw_rx_queue *);
71static void ipw_rx_queue_replenish(void *);
72
73static int ipw_up(struct ipw_priv *);
74static void ipw_down(struct ipw_priv *);
75static 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;
293 u32 aligned_len;
294 u32 i;
Jeff Garzikbf794512005-07-31 13:07:26 -0400295
James Ketrenos43f66a62005-03-25 12:31:53 -0600296 IPW_DEBUG_IO("addr = %i, buf = %p, num = %i\n", addr, buf, num);
297
298 /* Read the first nibble byte by byte */
299 if (unlikely(dif_len)) {
300 /* Start reading at aligned_addr + dif_len */
301 _ipw_write32(priv, CX2_INDIRECT_ADDR, aligned_addr);
302 for (i = dif_len; i < 4; i++, buf++)
303 *buf = _ipw_read8(priv, CX2_INDIRECT_DATA + i);
304 num -= dif_len;
305 aligned_addr += 4;
306 }
307
308 /* Read DWs through autoinc register */
309 _ipw_write32(priv, CX2_AUTOINC_ADDR, aligned_addr);
310 aligned_len = num & CX2_INDIRECT_ADDR_MASK;
311 for (i = 0; i < aligned_len; i += 4, buf += 4, aligned_addr += 4)
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400312 *(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 */
315 dif_len = num - aligned_len;
316 _ipw_write32(priv, CX2_INDIRECT_ADDR, aligned_addr);
317 for (i = 0; i < dif_len; i++, buf++)
318 *buf = ipw_read8(priv, CX2_INDIRECT_DATA + i);
319}
320
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400321static void _ipw_write_indirect(struct ipw_priv *priv, u32 addr, u8 * buf,
James Ketrenos43f66a62005-03-25 12:31:53 -0600322 int num)
323{
324 u32 aligned_addr = addr & CX2_INDIRECT_ADDR_MASK;
325 u32 dif_len = addr - aligned_addr;
326 u32 aligned_len;
327 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 Ketrenos43f66a62005-03-25 12:31:53 -0600331 /* Write the first nibble byte by byte */
332 if (unlikely(dif_len)) {
333 /* Start writing at aligned_addr + dif_len */
334 _ipw_write32(priv, CX2_INDIRECT_ADDR, aligned_addr);
335 for (i = dif_len; i < 4; i++, buf++)
336 _ipw_write8(priv, CX2_INDIRECT_DATA + i, *buf);
337 num -= dif_len;
338 aligned_addr += 4;
339 }
Jeff Garzikbf794512005-07-31 13:07:26 -0400340
James Ketrenos43f66a62005-03-25 12:31:53 -0600341 /* Write DWs through autoinc register */
342 _ipw_write32(priv, CX2_AUTOINC_ADDR, aligned_addr);
343 aligned_len = num & CX2_INDIRECT_ADDR_MASK;
344 for (i = 0; i < aligned_len; i += 4, buf += 4, aligned_addr += 4)
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400345 _ipw_write32(priv, CX2_AUTOINC_DATA, *(u32 *) buf);
Jeff Garzikbf794512005-07-31 13:07:26 -0400346
James Ketrenos43f66a62005-03-25 12:31:53 -0600347 /* Copy the last nibble */
348 dif_len = num - aligned_len;
349 _ipw_write32(priv, CX2_INDIRECT_ADDR, aligned_addr);
350 for (i = 0; i < dif_len; i++, buf++)
351 _ipw_write8(priv, CX2_INDIRECT_DATA + i, *buf);
352}
353
Jeff Garzikbf794512005-07-31 13:07:26 -0400354static void ipw_write_direct(struct ipw_priv *priv, u32 addr, void *buf,
James Ketrenos43f66a62005-03-25 12:31:53 -0600355 int num)
356{
357 memcpy_toio((priv->hw_base + addr), buf, num);
358}
359
360static inline void ipw_set_bit(struct ipw_priv *priv, u32 reg, u32 mask)
361{
362 ipw_write32(priv, reg, ipw_read32(priv, reg) | mask);
363}
364
365static inline void ipw_clear_bit(struct ipw_priv *priv, u32 reg, u32 mask)
366{
367 ipw_write32(priv, reg, ipw_read32(priv, reg) & ~mask);
368}
369
370static inline void ipw_enable_interrupts(struct ipw_priv *priv)
371{
372 if (priv->status & STATUS_INT_ENABLED)
373 return;
374 priv->status |= STATUS_INT_ENABLED;
375 ipw_write32(priv, CX2_INTA_MASK_R, CX2_INTA_MASK_ALL);
376}
377
378static inline void ipw_disable_interrupts(struct ipw_priv *priv)
379{
380 if (!(priv->status & STATUS_INT_ENABLED))
381 return;
382 priv->status &= ~STATUS_INT_ENABLED;
383 ipw_write32(priv, CX2_INTA_MASK_R, ~CX2_INTA_MASK_ALL);
384}
385
386static char *ipw_error_desc(u32 val)
387{
388 switch (val) {
Jeff Garzikbf794512005-07-31 13:07:26 -0400389 case IPW_FW_ERROR_OK:
James Ketrenos43f66a62005-03-25 12:31:53 -0600390 return "ERROR_OK";
Jeff Garzikbf794512005-07-31 13:07:26 -0400391 case IPW_FW_ERROR_FAIL:
James Ketrenos43f66a62005-03-25 12:31:53 -0600392 return "ERROR_FAIL";
Jeff Garzikbf794512005-07-31 13:07:26 -0400393 case IPW_FW_ERROR_MEMORY_UNDERFLOW:
James Ketrenos43f66a62005-03-25 12:31:53 -0600394 return "MEMORY_UNDERFLOW";
Jeff Garzikbf794512005-07-31 13:07:26 -0400395 case IPW_FW_ERROR_MEMORY_OVERFLOW:
James Ketrenos43f66a62005-03-25 12:31:53 -0600396 return "MEMORY_OVERFLOW";
Jeff Garzikbf794512005-07-31 13:07:26 -0400397 case IPW_FW_ERROR_BAD_PARAM:
James Ketrenos43f66a62005-03-25 12:31:53 -0600398 return "ERROR_BAD_PARAM";
Jeff Garzikbf794512005-07-31 13:07:26 -0400399 case IPW_FW_ERROR_BAD_CHECKSUM:
James Ketrenos43f66a62005-03-25 12:31:53 -0600400 return "ERROR_BAD_CHECKSUM";
Jeff Garzikbf794512005-07-31 13:07:26 -0400401 case IPW_FW_ERROR_NMI_INTERRUPT:
James Ketrenos43f66a62005-03-25 12:31:53 -0600402 return "ERROR_NMI_INTERRUPT";
Jeff Garzikbf794512005-07-31 13:07:26 -0400403 case IPW_FW_ERROR_BAD_DATABASE:
James Ketrenos43f66a62005-03-25 12:31:53 -0600404 return "ERROR_BAD_DATABASE";
Jeff Garzikbf794512005-07-31 13:07:26 -0400405 case IPW_FW_ERROR_ALLOC_FAIL:
James Ketrenos43f66a62005-03-25 12:31:53 -0600406 return "ERROR_ALLOC_FAIL";
Jeff Garzikbf794512005-07-31 13:07:26 -0400407 case IPW_FW_ERROR_DMA_UNDERRUN:
James Ketrenos43f66a62005-03-25 12:31:53 -0600408 return "ERROR_DMA_UNDERRUN";
Jeff Garzikbf794512005-07-31 13:07:26 -0400409 case IPW_FW_ERROR_DMA_STATUS:
James Ketrenos43f66a62005-03-25 12:31:53 -0600410 return "ERROR_DMA_STATUS";
Jeff Garzikbf794512005-07-31 13:07:26 -0400411 case IPW_FW_ERROR_DINOSTATUS_ERROR:
James Ketrenos43f66a62005-03-25 12:31:53 -0600412 return "ERROR_DINOSTATUS_ERROR";
Jeff Garzikbf794512005-07-31 13:07:26 -0400413 case IPW_FW_ERROR_EEPROMSTATUS_ERROR:
James Ketrenos43f66a62005-03-25 12:31:53 -0600414 return "ERROR_EEPROMSTATUS_ERROR";
Jeff Garzikbf794512005-07-31 13:07:26 -0400415 case IPW_FW_ERROR_SYSASSERT:
James Ketrenos43f66a62005-03-25 12:31:53 -0600416 return "ERROR_SYSASSERT";
Jeff Garzikbf794512005-07-31 13:07:26 -0400417 case IPW_FW_ERROR_FATAL_ERROR:
James Ketrenos43f66a62005-03-25 12:31:53 -0600418 return "ERROR_FATALSTATUS_ERROR";
Jeff Garzikbf794512005-07-31 13:07:26 -0400419 default:
James Ketrenos43f66a62005-03-25 12:31:53 -0600420 return "UNKNOWNSTATUS_ERROR";
421 }
422}
423
424static void ipw_dump_nic_error_log(struct ipw_priv *priv)
425{
426 u32 desc, time, blink1, blink2, ilink1, ilink2, idata, i, count, base;
427
428 base = ipw_read32(priv, IPWSTATUS_ERROR_LOG);
429 count = ipw_read_reg32(priv, base);
Jeff Garzikbf794512005-07-31 13:07:26 -0400430
James Ketrenos43f66a62005-03-25 12:31:53 -0600431 if (ERROR_START_OFFSET <= count * ERROR_ELEM_SIZE) {
432 IPW_ERROR("Start IPW Error Log Dump:\n");
433 IPW_ERROR("Status: 0x%08X, Config: %08X\n",
434 priv->status, priv->config);
435 }
436
Jeff Garzikbf794512005-07-31 13:07:26 -0400437 for (i = ERROR_START_OFFSET;
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400438 i <= count * ERROR_ELEM_SIZE; i += ERROR_ELEM_SIZE) {
439 desc = ipw_read_reg32(priv, base + i);
440 time = ipw_read_reg32(priv, base + i + 1 * sizeof(u32));
441 blink1 = ipw_read_reg32(priv, base + i + 2 * sizeof(u32));
442 blink2 = ipw_read_reg32(priv, base + i + 3 * sizeof(u32));
443 ilink1 = ipw_read_reg32(priv, base + i + 4 * sizeof(u32));
444 ilink2 = ipw_read_reg32(priv, base + i + 5 * sizeof(u32));
445 idata = ipw_read_reg32(priv, base + i + 6 * sizeof(u32));
James Ketrenos43f66a62005-03-25 12:31:53 -0600446
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400447 IPW_ERROR("%s %i 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x\n",
448 ipw_error_desc(desc), time, blink1, blink2,
449 ilink1, ilink2, idata);
James Ketrenos43f66a62005-03-25 12:31:53 -0600450 }
451}
452
453static void ipw_dump_nic_event_log(struct ipw_priv *priv)
454{
455 u32 ev, time, data, i, count, base;
456
457 base = ipw_read32(priv, IPW_EVENT_LOG);
458 count = ipw_read_reg32(priv, base);
Jeff Garzikbf794512005-07-31 13:07:26 -0400459
James Ketrenos43f66a62005-03-25 12:31:53 -0600460 if (EVENT_START_OFFSET <= count * EVENT_ELEM_SIZE)
461 IPW_ERROR("Start IPW Event Log Dump:\n");
462
Jeff Garzikbf794512005-07-31 13:07:26 -0400463 for (i = EVENT_START_OFFSET;
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400464 i <= count * EVENT_ELEM_SIZE; i += EVENT_ELEM_SIZE) {
James Ketrenos43f66a62005-03-25 12:31:53 -0600465 ev = ipw_read_reg32(priv, base + i);
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400466 time = ipw_read_reg32(priv, base + i + 1 * sizeof(u32));
467 data = ipw_read_reg32(priv, base + i + 2 * sizeof(u32));
James Ketrenos43f66a62005-03-25 12:31:53 -0600468
469#ifdef CONFIG_IPW_DEBUG
470 IPW_ERROR("%i\t0x%08x\t%i\n", time, data, ev);
471#endif
472 }
473}
474
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400475static int ipw_get_ordinal(struct ipw_priv *priv, u32 ord, void *val, u32 * len)
James Ketrenos43f66a62005-03-25 12:31:53 -0600476{
477 u32 addr, field_info, field_len, field_count, total_len;
478
479 IPW_DEBUG_ORD("ordinal = %i\n", ord);
480
481 if (!priv || !val || !len) {
482 IPW_DEBUG_ORD("Invalid argument\n");
483 return -EINVAL;
484 }
Jeff Garzikbf794512005-07-31 13:07:26 -0400485
James Ketrenos43f66a62005-03-25 12:31:53 -0600486 /* verify device ordinal tables have been initialized */
487 if (!priv->table0_addr || !priv->table1_addr || !priv->table2_addr) {
488 IPW_DEBUG_ORD("Access ordinals before initialization\n");
489 return -EINVAL;
490 }
491
492 switch (IPW_ORD_TABLE_ID_MASK & ord) {
493 case IPW_ORD_TABLE_0_MASK:
494 /*
495 * TABLE 0: Direct access to a table of 32 bit values
496 *
Jeff Garzikbf794512005-07-31 13:07:26 -0400497 * This is a very simple table with the data directly
James Ketrenos43f66a62005-03-25 12:31:53 -0600498 * read from the table
499 */
500
501 /* remove the table id from the ordinal */
502 ord &= IPW_ORD_TABLE_VALUE_MASK;
503
504 /* boundary check */
505 if (ord > priv->table0_len) {
506 IPW_DEBUG_ORD("ordinal value (%i) longer then "
507 "max (%i)\n", ord, priv->table0_len);
508 return -EINVAL;
509 }
510
511 /* verify we have enough room to store the value */
512 if (*len < sizeof(u32)) {
513 IPW_DEBUG_ORD("ordinal buffer length too small, "
Jiri Bencaaa4d302005-06-07 14:58:41 +0200514 "need %zd\n", sizeof(u32));
James Ketrenos43f66a62005-03-25 12:31:53 -0600515 return -EINVAL;
516 }
517
518 IPW_DEBUG_ORD("Reading TABLE0[%i] from offset 0x%08x\n",
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400519 ord, priv->table0_addr + (ord << 2));
James Ketrenos43f66a62005-03-25 12:31:53 -0600520
521 *len = sizeof(u32);
522 ord <<= 2;
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400523 *((u32 *) val) = ipw_read32(priv, priv->table0_addr + ord);
James Ketrenos43f66a62005-03-25 12:31:53 -0600524 break;
525
526 case IPW_ORD_TABLE_1_MASK:
527 /*
528 * TABLE 1: Indirect access to a table of 32 bit values
Jeff Garzikbf794512005-07-31 13:07:26 -0400529 *
530 * This is a fairly large table of u32 values each
James Ketrenos43f66a62005-03-25 12:31:53 -0600531 * representing starting addr for the data (which is
532 * also a u32)
533 */
534
535 /* remove the table id from the ordinal */
536 ord &= IPW_ORD_TABLE_VALUE_MASK;
Jeff Garzikbf794512005-07-31 13:07:26 -0400537
James Ketrenos43f66a62005-03-25 12:31:53 -0600538 /* boundary check */
539 if (ord > priv->table1_len) {
540 IPW_DEBUG_ORD("ordinal value too long\n");
541 return -EINVAL;
542 }
543
544 /* verify we have enough room to store the value */
545 if (*len < sizeof(u32)) {
546 IPW_DEBUG_ORD("ordinal buffer length too small, "
Jiri Bencaaa4d302005-06-07 14:58:41 +0200547 "need %zd\n", sizeof(u32));
James Ketrenos43f66a62005-03-25 12:31:53 -0600548 return -EINVAL;
549 }
550
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400551 *((u32 *) val) =
552 ipw_read_reg32(priv, (priv->table1_addr + (ord << 2)));
James Ketrenos43f66a62005-03-25 12:31:53 -0600553 *len = sizeof(u32);
554 break;
555
556 case IPW_ORD_TABLE_2_MASK:
557 /*
558 * TABLE 2: Indirect access to a table of variable sized values
559 *
560 * This table consist of six values, each containing
561 * - dword containing the starting offset of the data
562 * - dword containing the lengh in the first 16bits
563 * and the count in the second 16bits
564 */
565
566 /* remove the table id from the ordinal */
567 ord &= IPW_ORD_TABLE_VALUE_MASK;
568
569 /* boundary check */
570 if (ord > priv->table2_len) {
571 IPW_DEBUG_ORD("ordinal value too long\n");
572 return -EINVAL;
573 }
574
575 /* get the address of statistic */
576 addr = ipw_read_reg32(priv, priv->table2_addr + (ord << 3));
Jeff Garzikbf794512005-07-31 13:07:26 -0400577
578 /* get the second DW of statistics ;
James Ketrenos43f66a62005-03-25 12:31:53 -0600579 * two 16-bit words - first is length, second is count */
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400580 field_info =
581 ipw_read_reg32(priv,
582 priv->table2_addr + (ord << 3) +
583 sizeof(u32));
Jeff Garzikbf794512005-07-31 13:07:26 -0400584
James Ketrenos43f66a62005-03-25 12:31:53 -0600585 /* get each entry length */
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400586 field_len = *((u16 *) & field_info);
Jeff Garzikbf794512005-07-31 13:07:26 -0400587
James Ketrenos43f66a62005-03-25 12:31:53 -0600588 /* get number of entries */
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400589 field_count = *(((u16 *) & field_info) + 1);
Jeff Garzikbf794512005-07-31 13:07:26 -0400590
James Ketrenos43f66a62005-03-25 12:31:53 -0600591 /* abort if not enought memory */
592 total_len = field_len * field_count;
593 if (total_len > *len) {
594 *len = total_len;
595 return -EINVAL;
596 }
Jeff Garzikbf794512005-07-31 13:07:26 -0400597
James Ketrenos43f66a62005-03-25 12:31:53 -0600598 *len = total_len;
599 if (!total_len)
600 return 0;
601
602 IPW_DEBUG_ORD("addr = 0x%08x, total_len = %i, "
Jeff Garzikbf794512005-07-31 13:07:26 -0400603 "field_info = 0x%08x\n",
James Ketrenos43f66a62005-03-25 12:31:53 -0600604 addr, total_len, field_info);
605 ipw_read_indirect(priv, addr, val, total_len);
606 break;
607
608 default:
609 IPW_DEBUG_ORD("Invalid ordinal!\n");
610 return -EINVAL;
611
612 }
613
James Ketrenos43f66a62005-03-25 12:31:53 -0600614 return 0;
615}
616
617static void ipw_init_ordinals(struct ipw_priv *priv)
618{
619 priv->table0_addr = IPW_ORDINALS_TABLE_LOWER;
Jeff Garzikbf794512005-07-31 13:07:26 -0400620 priv->table0_len = ipw_read32(priv, priv->table0_addr);
James Ketrenos43f66a62005-03-25 12:31:53 -0600621
622 IPW_DEBUG_ORD("table 0 offset at 0x%08x, len = %i\n",
623 priv->table0_addr, priv->table0_len);
624
625 priv->table1_addr = ipw_read32(priv, IPW_ORDINALS_TABLE_1);
626 priv->table1_len = ipw_read_reg32(priv, priv->table1_addr);
627
628 IPW_DEBUG_ORD("table 1 offset at 0x%08x, len = %i\n",
629 priv->table1_addr, priv->table1_len);
630
631 priv->table2_addr = ipw_read32(priv, IPW_ORDINALS_TABLE_2);
632 priv->table2_len = ipw_read_reg32(priv, priv->table2_addr);
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400633 priv->table2_len &= 0x0000ffff; /* use first two bytes */
James Ketrenos43f66a62005-03-25 12:31:53 -0600634
635 IPW_DEBUG_ORD("table 2 offset at 0x%08x, len = %i\n",
636 priv->table2_addr, priv->table2_len);
637
638}
639
640/*
641 * The following adds a new attribute to the sysfs representation
642 * of this device driver (i.e. a new file in /sys/bus/pci/drivers/ipw/)
643 * used for controling the debug level.
Jeff Garzikbf794512005-07-31 13:07:26 -0400644 *
James Ketrenos43f66a62005-03-25 12:31:53 -0600645 * See the level definitions in ipw for details.
646 */
647static ssize_t show_debug_level(struct device_driver *d, char *buf)
648{
649 return sprintf(buf, "0x%08X\n", ipw_debug_level);
650}
Andrew Mortonad3fee52005-06-20 14:30:36 -0700651static ssize_t store_debug_level(struct device_driver *d,
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400652 const char *buf, size_t count)
James Ketrenos43f66a62005-03-25 12:31:53 -0600653{
654 char *p = (char *)buf;
655 u32 val;
656
657 if (p[1] == 'x' || p[1] == 'X' || p[0] == 'x' || p[0] == 'X') {
658 p++;
659 if (p[0] == 'x' || p[0] == 'X')
660 p++;
661 val = simple_strtoul(p, &p, 16);
662 } else
663 val = simple_strtoul(p, &p, 10);
Jeff Garzikbf794512005-07-31 13:07:26 -0400664 if (p == buf)
665 printk(KERN_INFO DRV_NAME
James Ketrenos43f66a62005-03-25 12:31:53 -0600666 ": %s is not in hex or decimal form.\n", buf);
667 else
668 ipw_debug_level = val;
669
670 return strnlen(buf, count);
671}
672
Jeff Garzikbf794512005-07-31 13:07:26 -0400673static DRIVER_ATTR(debug_level, S_IWUSR | S_IRUGO,
James Ketrenos43f66a62005-03-25 12:31:53 -0600674 show_debug_level, store_debug_level);
675
Andrew Mortonad3fee52005-06-20 14:30:36 -0700676static ssize_t show_status(struct device *d,
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400677 struct device_attribute *attr, char *buf)
James Ketrenos43f66a62005-03-25 12:31:53 -0600678{
Andrew Mortonad3fee52005-06-20 14:30:36 -0700679 struct ipw_priv *p = d->driver_data;
James Ketrenos43f66a62005-03-25 12:31:53 -0600680 return sprintf(buf, "0x%08x\n", (int)p->status);
681}
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400682
James Ketrenos43f66a62005-03-25 12:31:53 -0600683static DEVICE_ATTR(status, S_IRUGO, show_status, NULL);
684
Andrew Mortonad3fee52005-06-20 14:30:36 -0700685static ssize_t show_cfg(struct device *d, struct device_attribute *attr,
686 char *buf)
James Ketrenos43f66a62005-03-25 12:31:53 -0600687{
Andrew Mortonad3fee52005-06-20 14:30:36 -0700688 struct ipw_priv *p = d->driver_data;
James Ketrenos43f66a62005-03-25 12:31:53 -0600689 return sprintf(buf, "0x%08x\n", (int)p->config);
690}
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400691
James Ketrenos43f66a62005-03-25 12:31:53 -0600692static DEVICE_ATTR(cfg, S_IRUGO, show_cfg, NULL);
693
Andrew Mortonad3fee52005-06-20 14:30:36 -0700694static ssize_t show_nic_type(struct device *d,
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400695 struct device_attribute *attr, char *buf)
James Ketrenos43f66a62005-03-25 12:31:53 -0600696{
Andrew Mortonad3fee52005-06-20 14:30:36 -0700697 struct ipw_priv *p = d->driver_data;
James Ketrenos43f66a62005-03-25 12:31:53 -0600698 u8 type = p->eeprom[EEPROM_NIC_TYPE];
699
700 switch (type) {
701 case EEPROM_NIC_TYPE_STANDARD:
702 return sprintf(buf, "STANDARD\n");
703 case EEPROM_NIC_TYPE_DELL:
704 return sprintf(buf, "DELL\n");
705 case EEPROM_NIC_TYPE_FUJITSU:
706 return sprintf(buf, "FUJITSU\n");
707 case EEPROM_NIC_TYPE_IBM:
708 return sprintf(buf, "IBM\n");
709 case EEPROM_NIC_TYPE_HP:
710 return sprintf(buf, "HP\n");
711 }
Jeff Garzikbf794512005-07-31 13:07:26 -0400712
James Ketrenos43f66a62005-03-25 12:31:53 -0600713 return sprintf(buf, "UNKNOWN\n");
714}
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400715
James Ketrenos43f66a62005-03-25 12:31:53 -0600716static DEVICE_ATTR(nic_type, S_IRUGO, show_nic_type, NULL);
717
Andrew Mortonad3fee52005-06-20 14:30:36 -0700718static ssize_t dump_error_log(struct device *d,
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400719 struct device_attribute *attr, const char *buf,
720 size_t count)
James Ketrenos43f66a62005-03-25 12:31:53 -0600721{
722 char *p = (char *)buf;
723
Jeff Garzikbf794512005-07-31 13:07:26 -0400724 if (p[0] == '1')
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400725 ipw_dump_nic_error_log((struct ipw_priv *)d->driver_data);
James Ketrenos43f66a62005-03-25 12:31:53 -0600726
727 return strnlen(buf, count);
728}
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400729
James Ketrenos43f66a62005-03-25 12:31:53 -0600730static DEVICE_ATTR(dump_errors, S_IWUSR, NULL, dump_error_log);
731
Andrew Mortonad3fee52005-06-20 14:30:36 -0700732static ssize_t dump_event_log(struct device *d,
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400733 struct device_attribute *attr, const char *buf,
734 size_t count)
James Ketrenos43f66a62005-03-25 12:31:53 -0600735{
736 char *p = (char *)buf;
737
Jeff Garzikbf794512005-07-31 13:07:26 -0400738 if (p[0] == '1')
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400739 ipw_dump_nic_event_log((struct ipw_priv *)d->driver_data);
James Ketrenos43f66a62005-03-25 12:31:53 -0600740
741 return strnlen(buf, count);
742}
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400743
James Ketrenos43f66a62005-03-25 12:31:53 -0600744static DEVICE_ATTR(dump_events, S_IWUSR, NULL, dump_event_log);
745
Andrew Mortonad3fee52005-06-20 14:30:36 -0700746static ssize_t show_ucode_version(struct device *d,
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400747 struct device_attribute *attr, char *buf)
James Ketrenos43f66a62005-03-25 12:31:53 -0600748{
749 u32 len = sizeof(u32), tmp = 0;
Andrew Mortonad3fee52005-06-20 14:30:36 -0700750 struct ipw_priv *p = d->driver_data;
James Ketrenos43f66a62005-03-25 12:31:53 -0600751
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400752 if (ipw_get_ordinal(p, IPW_ORD_STAT_UCODE_VERSION, &tmp, &len))
James Ketrenos43f66a62005-03-25 12:31:53 -0600753 return 0;
754
755 return sprintf(buf, "0x%08x\n", tmp);
756}
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400757
758static DEVICE_ATTR(ucode_version, S_IWUSR | S_IRUGO, show_ucode_version, NULL);
James Ketrenos43f66a62005-03-25 12:31:53 -0600759
Andrew Mortonad3fee52005-06-20 14:30:36 -0700760static ssize_t show_rtc(struct device *d, struct device_attribute *attr,
761 char *buf)
James Ketrenos43f66a62005-03-25 12:31:53 -0600762{
763 u32 len = sizeof(u32), tmp = 0;
Andrew Mortonad3fee52005-06-20 14:30:36 -0700764 struct ipw_priv *p = d->driver_data;
James Ketrenos43f66a62005-03-25 12:31:53 -0600765
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400766 if (ipw_get_ordinal(p, IPW_ORD_STAT_RTC, &tmp, &len))
James Ketrenos43f66a62005-03-25 12:31:53 -0600767 return 0;
768
769 return sprintf(buf, "0x%08x\n", tmp);
770}
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400771
772static DEVICE_ATTR(rtc, S_IWUSR | S_IRUGO, show_rtc, NULL);
James Ketrenos43f66a62005-03-25 12:31:53 -0600773
774/*
775 * Add a device attribute to view/control the delay between eeprom
776 * operations.
777 */
Andrew Mortonad3fee52005-06-20 14:30:36 -0700778static ssize_t show_eeprom_delay(struct device *d,
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400779 struct device_attribute *attr, char *buf)
James Ketrenos43f66a62005-03-25 12:31:53 -0600780{
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400781 int n = ((struct ipw_priv *)d->driver_data)->eeprom_delay;
James Ketrenos43f66a62005-03-25 12:31:53 -0600782 return sprintf(buf, "%i\n", n);
783}
Andrew Mortonad3fee52005-06-20 14:30:36 -0700784static ssize_t store_eeprom_delay(struct device *d,
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400785 struct device_attribute *attr,
786 const char *buf, size_t count)
James Ketrenos43f66a62005-03-25 12:31:53 -0600787{
Andrew Mortonad3fee52005-06-20 14:30:36 -0700788 struct ipw_priv *p = d->driver_data;
James Ketrenos43f66a62005-03-25 12:31:53 -0600789 sscanf(buf, "%i", &p->eeprom_delay);
790 return strnlen(buf, count);
791}
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400792
793static DEVICE_ATTR(eeprom_delay, S_IWUSR | S_IRUGO,
794 show_eeprom_delay, store_eeprom_delay);
James Ketrenos43f66a62005-03-25 12:31:53 -0600795
Andrew Mortonad3fee52005-06-20 14:30:36 -0700796static ssize_t show_command_event_reg(struct device *d,
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400797 struct device_attribute *attr, char *buf)
James Ketrenos43f66a62005-03-25 12:31:53 -0600798{
799 u32 reg = 0;
Andrew Mortonad3fee52005-06-20 14:30:36 -0700800 struct ipw_priv *p = d->driver_data;
James Ketrenos43f66a62005-03-25 12:31:53 -0600801
802 reg = ipw_read_reg32(p, CX2_INTERNAL_CMD_EVENT);
803 return sprintf(buf, "0x%08x\n", reg);
804}
Andrew Mortonad3fee52005-06-20 14:30:36 -0700805static ssize_t store_command_event_reg(struct device *d,
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400806 struct device_attribute *attr,
807 const char *buf, size_t count)
James Ketrenos43f66a62005-03-25 12:31:53 -0600808{
809 u32 reg;
Andrew Mortonad3fee52005-06-20 14:30:36 -0700810 struct ipw_priv *p = d->driver_data;
James Ketrenos43f66a62005-03-25 12:31:53 -0600811
812 sscanf(buf, "%x", &reg);
813 ipw_write_reg32(p, CX2_INTERNAL_CMD_EVENT, reg);
814 return strnlen(buf, count);
815}
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400816
817static DEVICE_ATTR(command_event_reg, S_IWUSR | S_IRUGO,
818 show_command_event_reg, store_command_event_reg);
James Ketrenos43f66a62005-03-25 12:31:53 -0600819
Andrew Mortonad3fee52005-06-20 14:30:36 -0700820static ssize_t show_mem_gpio_reg(struct device *d,
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400821 struct device_attribute *attr, char *buf)
James Ketrenos43f66a62005-03-25 12:31:53 -0600822{
823 u32 reg = 0;
Andrew Mortonad3fee52005-06-20 14:30:36 -0700824 struct ipw_priv *p = d->driver_data;
James Ketrenos43f66a62005-03-25 12:31:53 -0600825
826 reg = ipw_read_reg32(p, 0x301100);
827 return sprintf(buf, "0x%08x\n", reg);
828}
Andrew Mortonad3fee52005-06-20 14:30:36 -0700829static ssize_t store_mem_gpio_reg(struct device *d,
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400830 struct device_attribute *attr,
831 const char *buf, size_t count)
James Ketrenos43f66a62005-03-25 12:31:53 -0600832{
833 u32 reg;
Andrew Mortonad3fee52005-06-20 14:30:36 -0700834 struct ipw_priv *p = d->driver_data;
James Ketrenos43f66a62005-03-25 12:31:53 -0600835
836 sscanf(buf, "%x", &reg);
837 ipw_write_reg32(p, 0x301100, reg);
838 return strnlen(buf, count);
839}
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400840
841static DEVICE_ATTR(mem_gpio_reg, S_IWUSR | S_IRUGO,
842 show_mem_gpio_reg, store_mem_gpio_reg);
James Ketrenos43f66a62005-03-25 12:31:53 -0600843
Andrew Mortonad3fee52005-06-20 14:30:36 -0700844static ssize_t show_indirect_dword(struct device *d,
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400845 struct device_attribute *attr, char *buf)
James Ketrenos43f66a62005-03-25 12:31:53 -0600846{
847 u32 reg = 0;
Andrew Mortonad3fee52005-06-20 14:30:36 -0700848 struct ipw_priv *priv = d->driver_data;
Jeff Garzikbf794512005-07-31 13:07:26 -0400849 if (priv->status & STATUS_INDIRECT_DWORD)
James Ketrenos43f66a62005-03-25 12:31:53 -0600850 reg = ipw_read_reg32(priv, priv->indirect_dword);
Jeff Garzikbf794512005-07-31 13:07:26 -0400851 else
James Ketrenos43f66a62005-03-25 12:31:53 -0600852 reg = 0;
Jeff Garzikbf794512005-07-31 13:07:26 -0400853
James Ketrenos43f66a62005-03-25 12:31:53 -0600854 return sprintf(buf, "0x%08x\n", reg);
855}
Andrew Mortonad3fee52005-06-20 14:30:36 -0700856static ssize_t store_indirect_dword(struct device *d,
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400857 struct device_attribute *attr,
858 const char *buf, size_t count)
James Ketrenos43f66a62005-03-25 12:31:53 -0600859{
Andrew Mortonad3fee52005-06-20 14:30:36 -0700860 struct ipw_priv *priv = d->driver_data;
James Ketrenos43f66a62005-03-25 12:31:53 -0600861
862 sscanf(buf, "%x", &priv->indirect_dword);
863 priv->status |= STATUS_INDIRECT_DWORD;
864 return strnlen(buf, count);
865}
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400866
867static DEVICE_ATTR(indirect_dword, S_IWUSR | S_IRUGO,
868 show_indirect_dword, store_indirect_dword);
James Ketrenos43f66a62005-03-25 12:31:53 -0600869
Andrew Mortonad3fee52005-06-20 14:30:36 -0700870static ssize_t show_indirect_byte(struct device *d,
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400871 struct device_attribute *attr, char *buf)
James Ketrenos43f66a62005-03-25 12:31:53 -0600872{
873 u8 reg = 0;
Andrew Mortonad3fee52005-06-20 14:30:36 -0700874 struct ipw_priv *priv = d->driver_data;
Jeff Garzikbf794512005-07-31 13:07:26 -0400875 if (priv->status & STATUS_INDIRECT_BYTE)
James Ketrenos43f66a62005-03-25 12:31:53 -0600876 reg = ipw_read_reg8(priv, priv->indirect_byte);
Jeff Garzikbf794512005-07-31 13:07:26 -0400877 else
James Ketrenos43f66a62005-03-25 12:31:53 -0600878 reg = 0;
879
880 return sprintf(buf, "0x%02x\n", reg);
881}
Andrew Mortonad3fee52005-06-20 14:30:36 -0700882static ssize_t store_indirect_byte(struct device *d,
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400883 struct device_attribute *attr,
884 const char *buf, size_t count)
James Ketrenos43f66a62005-03-25 12:31:53 -0600885{
Andrew Mortonad3fee52005-06-20 14:30:36 -0700886 struct ipw_priv *priv = d->driver_data;
James Ketrenos43f66a62005-03-25 12:31:53 -0600887
888 sscanf(buf, "%x", &priv->indirect_byte);
889 priv->status |= STATUS_INDIRECT_BYTE;
890 return strnlen(buf, count);
891}
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400892
893static DEVICE_ATTR(indirect_byte, S_IWUSR | S_IRUGO,
James Ketrenos43f66a62005-03-25 12:31:53 -0600894 show_indirect_byte, store_indirect_byte);
895
Andrew Mortonad3fee52005-06-20 14:30:36 -0700896static ssize_t show_direct_dword(struct device *d,
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400897 struct device_attribute *attr, char *buf)
James Ketrenos43f66a62005-03-25 12:31:53 -0600898{
899 u32 reg = 0;
Andrew Mortonad3fee52005-06-20 14:30:36 -0700900 struct ipw_priv *priv = d->driver_data;
James Ketrenos43f66a62005-03-25 12:31:53 -0600901
Jeff Garzikbf794512005-07-31 13:07:26 -0400902 if (priv->status & STATUS_DIRECT_DWORD)
James Ketrenos43f66a62005-03-25 12:31:53 -0600903 reg = ipw_read32(priv, priv->direct_dword);
Jeff Garzikbf794512005-07-31 13:07:26 -0400904 else
James Ketrenos43f66a62005-03-25 12:31:53 -0600905 reg = 0;
906
907 return sprintf(buf, "0x%08x\n", reg);
908}
Andrew Mortonad3fee52005-06-20 14:30:36 -0700909static ssize_t store_direct_dword(struct device *d,
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400910 struct device_attribute *attr,
911 const char *buf, size_t count)
James Ketrenos43f66a62005-03-25 12:31:53 -0600912{
Andrew Mortonad3fee52005-06-20 14:30:36 -0700913 struct ipw_priv *priv = d->driver_data;
James Ketrenos43f66a62005-03-25 12:31:53 -0600914
915 sscanf(buf, "%x", &priv->direct_dword);
916 priv->status |= STATUS_DIRECT_DWORD;
917 return strnlen(buf, count);
918}
James Ketrenos43f66a62005-03-25 12:31:53 -0600919
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400920static DEVICE_ATTR(direct_dword, S_IWUSR | S_IRUGO,
921 show_direct_dword, store_direct_dword);
James Ketrenos43f66a62005-03-25 12:31:53 -0600922
923static inline int rf_kill_active(struct ipw_priv *priv)
924{
925 if (0 == (ipw_read32(priv, 0x30) & 0x10000))
926 priv->status |= STATUS_RF_KILL_HW;
927 else
928 priv->status &= ~STATUS_RF_KILL_HW;
929
930 return (priv->status & STATUS_RF_KILL_HW) ? 1 : 0;
931}
932
Andrew Mortonad3fee52005-06-20 14:30:36 -0700933static ssize_t show_rf_kill(struct device *d, struct device_attribute *attr,
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400934 char *buf)
James Ketrenos43f66a62005-03-25 12:31:53 -0600935{
936 /* 0 - RF kill not enabled
Jeff Garzikbf794512005-07-31 13:07:26 -0400937 1 - SW based RF kill active (sysfs)
James Ketrenos43f66a62005-03-25 12:31:53 -0600938 2 - HW based RF kill active
939 3 - Both HW and SW baed RF kill active */
Andrew Mortonad3fee52005-06-20 14:30:36 -0700940 struct ipw_priv *priv = d->driver_data;
James Ketrenos43f66a62005-03-25 12:31:53 -0600941 int val = ((priv->status & STATUS_RF_KILL_SW) ? 0x1 : 0x0) |
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400942 (rf_kill_active(priv) ? 0x2 : 0x0);
James Ketrenos43f66a62005-03-25 12:31:53 -0600943 return sprintf(buf, "%i\n", val);
944}
945
946static int ipw_radio_kill_sw(struct ipw_priv *priv, int disable_radio)
947{
Jeff Garzikbf794512005-07-31 13:07:26 -0400948 if ((disable_radio ? 1 : 0) ==
James Ketrenos43f66a62005-03-25 12:31:53 -0600949 (priv->status & STATUS_RF_KILL_SW ? 1 : 0))
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400950 return 0;
James Ketrenos43f66a62005-03-25 12:31:53 -0600951
952 IPW_DEBUG_RF_KILL("Manual SW RF Kill set to: RADIO %s\n",
953 disable_radio ? "OFF" : "ON");
954
955 if (disable_radio) {
956 priv->status |= STATUS_RF_KILL_SW;
957
Jeff Garzikbf794512005-07-31 13:07:26 -0400958 if (priv->workqueue) {
James Ketrenos43f66a62005-03-25 12:31:53 -0600959 cancel_delayed_work(&priv->request_scan);
960 }
961 wake_up_interruptible(&priv->wait_command_queue);
962 queue_work(priv->workqueue, &priv->down);
963 } else {
964 priv->status &= ~STATUS_RF_KILL_SW;
965 if (rf_kill_active(priv)) {
966 IPW_DEBUG_RF_KILL("Can not turn radio back on - "
967 "disabled by HW switch\n");
968 /* Make sure the RF_KILL check timer is running */
969 cancel_delayed_work(&priv->rf_kill);
Jeff Garzikbf794512005-07-31 13:07:26 -0400970 queue_delayed_work(priv->workqueue, &priv->rf_kill,
James Ketrenos43f66a62005-03-25 12:31:53 -0600971 2 * HZ);
Jeff Garzikbf794512005-07-31 13:07:26 -0400972 } else
James Ketrenos43f66a62005-03-25 12:31:53 -0600973 queue_work(priv->workqueue, &priv->up);
974 }
975
976 return 1;
977}
978
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400979static ssize_t store_rf_kill(struct device *d, struct device_attribute *attr,
980 const char *buf, size_t count)
James Ketrenos43f66a62005-03-25 12:31:53 -0600981{
Andrew Mortonad3fee52005-06-20 14:30:36 -0700982 struct ipw_priv *priv = d->driver_data;
Jeff Garzikbf794512005-07-31 13:07:26 -0400983
James Ketrenos43f66a62005-03-25 12:31:53 -0600984 ipw_radio_kill_sw(priv, buf[0] == '1');
985
986 return count;
987}
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400988
989static DEVICE_ATTR(rf_kill, S_IWUSR | S_IRUGO, show_rf_kill, store_rf_kill);
James Ketrenos43f66a62005-03-25 12:31:53 -0600990
991static void ipw_irq_tasklet(struct ipw_priv *priv)
992{
993 u32 inta, inta_mask, handled = 0;
994 unsigned long flags;
995 int rc = 0;
996
997 spin_lock_irqsave(&priv->lock, flags);
998
999 inta = ipw_read32(priv, CX2_INTA_RW);
1000 inta_mask = ipw_read32(priv, CX2_INTA_MASK_R);
1001 inta &= (CX2_INTA_MASK_ALL & inta_mask);
1002
1003 /* Add any cached INTA values that need to be handled */
1004 inta |= priv->isr_inta;
1005
1006 /* handle all the justifications for the interrupt */
1007 if (inta & CX2_INTA_BIT_RX_TRANSFER) {
1008 ipw_rx(priv);
1009 handled |= CX2_INTA_BIT_RX_TRANSFER;
1010 }
1011
1012 if (inta & CX2_INTA_BIT_TX_CMD_QUEUE) {
1013 IPW_DEBUG_HC("Command completed.\n");
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001014 rc = ipw_queue_tx_reclaim(priv, &priv->txq_cmd, -1);
James Ketrenos43f66a62005-03-25 12:31:53 -06001015 priv->status &= ~STATUS_HCMD_ACTIVE;
1016 wake_up_interruptible(&priv->wait_command_queue);
1017 handled |= CX2_INTA_BIT_TX_CMD_QUEUE;
1018 }
1019
1020 if (inta & CX2_INTA_BIT_TX_QUEUE_1) {
1021 IPW_DEBUG_TX("TX_QUEUE_1\n");
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001022 rc = ipw_queue_tx_reclaim(priv, &priv->txq[0], 0);
James Ketrenos43f66a62005-03-25 12:31:53 -06001023 handled |= CX2_INTA_BIT_TX_QUEUE_1;
1024 }
1025
1026 if (inta & CX2_INTA_BIT_TX_QUEUE_2) {
1027 IPW_DEBUG_TX("TX_QUEUE_2\n");
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001028 rc = ipw_queue_tx_reclaim(priv, &priv->txq[1], 1);
James Ketrenos43f66a62005-03-25 12:31:53 -06001029 handled |= CX2_INTA_BIT_TX_QUEUE_2;
1030 }
1031
1032 if (inta & CX2_INTA_BIT_TX_QUEUE_3) {
1033 IPW_DEBUG_TX("TX_QUEUE_3\n");
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001034 rc = ipw_queue_tx_reclaim(priv, &priv->txq[2], 2);
James Ketrenos43f66a62005-03-25 12:31:53 -06001035 handled |= CX2_INTA_BIT_TX_QUEUE_3;
1036 }
1037
1038 if (inta & CX2_INTA_BIT_TX_QUEUE_4) {
1039 IPW_DEBUG_TX("TX_QUEUE_4\n");
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001040 rc = ipw_queue_tx_reclaim(priv, &priv->txq[3], 3);
James Ketrenos43f66a62005-03-25 12:31:53 -06001041 handled |= CX2_INTA_BIT_TX_QUEUE_4;
1042 }
1043
1044 if (inta & CX2_INTA_BIT_STATUS_CHANGE) {
1045 IPW_WARNING("STATUS_CHANGE\n");
1046 handled |= CX2_INTA_BIT_STATUS_CHANGE;
1047 }
1048
1049 if (inta & CX2_INTA_BIT_BEACON_PERIOD_EXPIRED) {
1050 IPW_WARNING("TX_PERIOD_EXPIRED\n");
1051 handled |= CX2_INTA_BIT_BEACON_PERIOD_EXPIRED;
1052 }
1053
1054 if (inta & CX2_INTA_BIT_SLAVE_MODE_HOST_CMD_DONE) {
1055 IPW_WARNING("HOST_CMD_DONE\n");
1056 handled |= CX2_INTA_BIT_SLAVE_MODE_HOST_CMD_DONE;
1057 }
1058
1059 if (inta & CX2_INTA_BIT_FW_INITIALIZATION_DONE) {
1060 IPW_WARNING("FW_INITIALIZATION_DONE\n");
1061 handled |= CX2_INTA_BIT_FW_INITIALIZATION_DONE;
1062 }
1063
1064 if (inta & CX2_INTA_BIT_FW_CARD_DISABLE_PHY_OFF_DONE) {
1065 IPW_WARNING("PHY_OFF_DONE\n");
1066 handled |= CX2_INTA_BIT_FW_CARD_DISABLE_PHY_OFF_DONE;
1067 }
1068
1069 if (inta & CX2_INTA_BIT_RF_KILL_DONE) {
1070 IPW_DEBUG_RF_KILL("RF_KILL_DONE\n");
1071 priv->status |= STATUS_RF_KILL_HW;
1072 wake_up_interruptible(&priv->wait_command_queue);
1073 netif_carrier_off(priv->net_dev);
1074 netif_stop_queue(priv->net_dev);
1075 cancel_delayed_work(&priv->request_scan);
1076 queue_delayed_work(priv->workqueue, &priv->rf_kill, 2 * HZ);
1077 handled |= CX2_INTA_BIT_RF_KILL_DONE;
1078 }
Jeff Garzikbf794512005-07-31 13:07:26 -04001079
James Ketrenos43f66a62005-03-25 12:31:53 -06001080 if (inta & CX2_INTA_BIT_FATAL_ERROR) {
1081 IPW_ERROR("Firmware error detected. Restarting.\n");
1082#ifdef CONFIG_IPW_DEBUG
1083 if (ipw_debug_level & IPW_DL_FW_ERRORS) {
1084 ipw_dump_nic_error_log(priv);
1085 ipw_dump_nic_event_log(priv);
1086 }
1087#endif
1088 queue_work(priv->workqueue, &priv->adapter_restart);
1089 handled |= CX2_INTA_BIT_FATAL_ERROR;
1090 }
1091
1092 if (inta & CX2_INTA_BIT_PARITY_ERROR) {
1093 IPW_ERROR("Parity error\n");
1094 handled |= CX2_INTA_BIT_PARITY_ERROR;
1095 }
1096
1097 if (handled != inta) {
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001098 IPW_ERROR("Unhandled INTA bits 0x%08x\n", inta & ~handled);
James Ketrenos43f66a62005-03-25 12:31:53 -06001099 }
1100
1101 /* enable all interrupts */
1102 ipw_enable_interrupts(priv);
1103
1104 spin_unlock_irqrestore(&priv->lock, flags);
1105}
Jeff Garzikbf794512005-07-31 13:07:26 -04001106
James Ketrenos43f66a62005-03-25 12:31:53 -06001107#ifdef CONFIG_IPW_DEBUG
1108#define IPW_CMD(x) case IPW_CMD_ ## x : return #x
1109static char *get_cmd_string(u8 cmd)
1110{
1111 switch (cmd) {
1112 IPW_CMD(HOST_COMPLETE);
Jeff Garzikbf794512005-07-31 13:07:26 -04001113 IPW_CMD(POWER_DOWN);
1114 IPW_CMD(SYSTEM_CONFIG);
1115 IPW_CMD(MULTICAST_ADDRESS);
1116 IPW_CMD(SSID);
1117 IPW_CMD(ADAPTER_ADDRESS);
1118 IPW_CMD(PORT_TYPE);
1119 IPW_CMD(RTS_THRESHOLD);
1120 IPW_CMD(FRAG_THRESHOLD);
1121 IPW_CMD(POWER_MODE);
1122 IPW_CMD(WEP_KEY);
1123 IPW_CMD(TGI_TX_KEY);
1124 IPW_CMD(SCAN_REQUEST);
1125 IPW_CMD(SCAN_REQUEST_EXT);
1126 IPW_CMD(ASSOCIATE);
1127 IPW_CMD(SUPPORTED_RATES);
1128 IPW_CMD(SCAN_ABORT);
1129 IPW_CMD(TX_FLUSH);
1130 IPW_CMD(QOS_PARAMETERS);
1131 IPW_CMD(DINO_CONFIG);
1132 IPW_CMD(RSN_CAPABILITIES);
1133 IPW_CMD(RX_KEY);
1134 IPW_CMD(CARD_DISABLE);
1135 IPW_CMD(SEED_NUMBER);
1136 IPW_CMD(TX_POWER);
1137 IPW_CMD(COUNTRY_INFO);
1138 IPW_CMD(AIRONET_INFO);
1139 IPW_CMD(AP_TX_POWER);
1140 IPW_CMD(CCKM_INFO);
1141 IPW_CMD(CCX_VER_INFO);
1142 IPW_CMD(SET_CALIBRATION);
1143 IPW_CMD(SENSITIVITY_CALIB);
1144 IPW_CMD(RETRY_LIMIT);
1145 IPW_CMD(IPW_PRE_POWER_DOWN);
1146 IPW_CMD(VAP_BEACON_TEMPLATE);
1147 IPW_CMD(VAP_DTIM_PERIOD);
1148 IPW_CMD(EXT_SUPPORTED_RATES);
1149 IPW_CMD(VAP_LOCAL_TX_PWR_CONSTRAINT);
1150 IPW_CMD(VAP_QUIET_INTERVALS);
1151 IPW_CMD(VAP_CHANNEL_SWITCH);
1152 IPW_CMD(VAP_MANDATORY_CHANNELS);
1153 IPW_CMD(VAP_CELL_PWR_LIMIT);
1154 IPW_CMD(VAP_CF_PARAM_SET);
1155 IPW_CMD(VAP_SET_BEACONING_STATE);
1156 IPW_CMD(MEASUREMENT);
1157 IPW_CMD(POWER_CAPABILITY);
1158 IPW_CMD(SUPPORTED_CHANNELS);
1159 IPW_CMD(TPC_REPORT);
1160 IPW_CMD(WME_INFO);
1161 IPW_CMD(PRODUCTION_COMMAND);
1162 default:
James Ketrenos43f66a62005-03-25 12:31:53 -06001163 return "UNKNOWN";
1164 }
1165}
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001166#endif /* CONFIG_IPW_DEBUG */
James Ketrenos43f66a62005-03-25 12:31:53 -06001167
1168#define HOST_COMPLETE_TIMEOUT HZ
1169static int ipw_send_cmd(struct ipw_priv *priv, struct host_cmd *cmd)
1170{
1171 int rc = 0;
1172
1173 if (priv->status & STATUS_HCMD_ACTIVE) {
1174 IPW_ERROR("Already sending a command\n");
1175 return -1;
1176 }
1177
1178 priv->status |= STATUS_HCMD_ACTIVE;
Jeff Garzikbf794512005-07-31 13:07:26 -04001179
1180 IPW_DEBUG_HC("Sending %s command (#%d), %d bytes\n",
James Ketrenos43f66a62005-03-25 12:31:53 -06001181 get_cmd_string(cmd->cmd), cmd->cmd, cmd->len);
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001182 printk_buf(IPW_DL_HOST_COMMAND, (u8 *) cmd->param, cmd->len);
James Ketrenos43f66a62005-03-25 12:31:53 -06001183
1184 rc = ipw_queue_tx_hcmd(priv, cmd->cmd, &cmd->param, cmd->len, 0);
1185 if (rc)
1186 return rc;
1187
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001188 rc = wait_event_interruptible_timeout(priv->wait_command_queue,
1189 !(priv->
1190 status & STATUS_HCMD_ACTIVE),
1191 HOST_COMPLETE_TIMEOUT);
James Ketrenos43f66a62005-03-25 12:31:53 -06001192 if (rc == 0) {
1193 IPW_DEBUG_INFO("Command completion failed out after %dms.\n",
Jeff Garzik9bd481f2005-06-28 01:46:35 -04001194 jiffies_to_msecs(HOST_COMPLETE_TIMEOUT));
James Ketrenos43f66a62005-03-25 12:31:53 -06001195 priv->status &= ~STATUS_HCMD_ACTIVE;
1196 return -EIO;
1197 }
1198 if (priv->status & STATUS_RF_KILL_MASK) {
1199 IPW_DEBUG_INFO("Command aborted due to RF Kill Switch\n");
1200 return -EIO;
1201 }
1202
1203 return 0;
1204}
1205
1206static int ipw_send_host_complete(struct ipw_priv *priv)
1207{
1208 struct host_cmd cmd = {
1209 .cmd = IPW_CMD_HOST_COMPLETE,
1210 .len = 0
1211 };
1212
1213 if (!priv) {
1214 IPW_ERROR("Invalid args\n");
1215 return -1;
1216 }
1217
1218 if (ipw_send_cmd(priv, &cmd)) {
1219 IPW_ERROR("failed to send HOST_COMPLETE command\n");
1220 return -1;
1221 }
Jeff Garzikbf794512005-07-31 13:07:26 -04001222
James Ketrenos43f66a62005-03-25 12:31:53 -06001223 return 0;
1224}
1225
Jeff Garzikbf794512005-07-31 13:07:26 -04001226static int ipw_send_system_config(struct ipw_priv *priv,
James Ketrenos43f66a62005-03-25 12:31:53 -06001227 struct ipw_sys_config *config)
1228{
1229 struct host_cmd cmd = {
1230 .cmd = IPW_CMD_SYSTEM_CONFIG,
1231 .len = sizeof(*config)
1232 };
1233
1234 if (!priv || !config) {
1235 IPW_ERROR("Invalid args\n");
1236 return -1;
1237 }
1238
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001239 memcpy(&cmd.param, config, sizeof(*config));
James Ketrenos43f66a62005-03-25 12:31:53 -06001240 if (ipw_send_cmd(priv, &cmd)) {
1241 IPW_ERROR("failed to send SYSTEM_CONFIG command\n");
1242 return -1;
1243 }
1244
1245 return 0;
1246}
1247
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001248static int ipw_send_ssid(struct ipw_priv *priv, u8 * ssid, int len)
James Ketrenos43f66a62005-03-25 12:31:53 -06001249{
1250 struct host_cmd cmd = {
1251 .cmd = IPW_CMD_SSID,
1252 .len = min(len, IW_ESSID_MAX_SIZE)
1253 };
1254
1255 if (!priv || !ssid) {
1256 IPW_ERROR("Invalid args\n");
1257 return -1;
1258 }
1259
1260 memcpy(&cmd.param, ssid, cmd.len);
1261 if (ipw_send_cmd(priv, &cmd)) {
1262 IPW_ERROR("failed to send SSID command\n");
1263 return -1;
1264 }
Jeff Garzikbf794512005-07-31 13:07:26 -04001265
James Ketrenos43f66a62005-03-25 12:31:53 -06001266 return 0;
1267}
1268
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001269static int ipw_send_adapter_address(struct ipw_priv *priv, u8 * mac)
James Ketrenos43f66a62005-03-25 12:31:53 -06001270{
1271 struct host_cmd cmd = {
1272 .cmd = IPW_CMD_ADAPTER_ADDRESS,
1273 .len = ETH_ALEN
1274 };
1275
1276 if (!priv || !mac) {
1277 IPW_ERROR("Invalid args\n");
1278 return -1;
1279 }
1280
1281 IPW_DEBUG_INFO("%s: Setting MAC to " MAC_FMT "\n",
1282 priv->net_dev->name, MAC_ARG(mac));
1283
1284 memcpy(&cmd.param, mac, ETH_ALEN);
1285
1286 if (ipw_send_cmd(priv, &cmd)) {
1287 IPW_ERROR("failed to send ADAPTER_ADDRESS command\n");
1288 return -1;
1289 }
Jeff Garzikbf794512005-07-31 13:07:26 -04001290
James Ketrenos43f66a62005-03-25 12:31:53 -06001291 return 0;
1292}
1293
1294static void ipw_adapter_restart(void *adapter)
1295{
1296 struct ipw_priv *priv = adapter;
1297
1298 if (priv->status & STATUS_RF_KILL_MASK)
1299 return;
1300
1301 ipw_down(priv);
1302 if (ipw_up(priv)) {
1303 IPW_ERROR("Failed to up device\n");
1304 return;
1305 }
1306}
1307
James Ketrenos43f66a62005-03-25 12:31:53 -06001308#define IPW_SCAN_CHECK_WATCHDOG (5 * HZ)
1309
1310static void ipw_scan_check(void *data)
1311{
1312 struct ipw_priv *priv = data;
1313 if (priv->status & (STATUS_SCANNING | STATUS_SCAN_ABORTING)) {
1314 IPW_DEBUG_SCAN("Scan completion watchdog resetting "
Jeff Garzikbf794512005-07-31 13:07:26 -04001315 "adapter (%dms).\n",
James Ketrenos43f66a62005-03-25 12:31:53 -06001316 IPW_SCAN_CHECK_WATCHDOG / 100);
1317 ipw_adapter_restart(priv);
1318 }
1319}
1320
1321static int ipw_send_scan_request_ext(struct ipw_priv *priv,
1322 struct ipw_scan_request_ext *request)
1323{
1324 struct host_cmd cmd = {
1325 .cmd = IPW_CMD_SCAN_REQUEST_EXT,
1326 .len = sizeof(*request)
1327 };
1328
1329 if (!priv || !request) {
1330 IPW_ERROR("Invalid args\n");
1331 return -1;
1332 }
1333
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001334 memcpy(&cmd.param, request, sizeof(*request));
James Ketrenos43f66a62005-03-25 12:31:53 -06001335 if (ipw_send_cmd(priv, &cmd)) {
1336 IPW_ERROR("failed to send SCAN_REQUEST_EXT command\n");
1337 return -1;
1338 }
Jeff Garzikbf794512005-07-31 13:07:26 -04001339
1340 queue_delayed_work(priv->workqueue, &priv->scan_check,
James Ketrenos43f66a62005-03-25 12:31:53 -06001341 IPW_SCAN_CHECK_WATCHDOG);
1342 return 0;
1343}
1344
1345static int ipw_send_scan_abort(struct ipw_priv *priv)
1346{
1347 struct host_cmd cmd = {
1348 .cmd = IPW_CMD_SCAN_ABORT,
1349 .len = 0
1350 };
1351
1352 if (!priv) {
1353 IPW_ERROR("Invalid args\n");
1354 return -1;
1355 }
1356
1357 if (ipw_send_cmd(priv, &cmd)) {
1358 IPW_ERROR("failed to send SCAN_ABORT command\n");
1359 return -1;
1360 }
Jeff Garzikbf794512005-07-31 13:07:26 -04001361
James Ketrenos43f66a62005-03-25 12:31:53 -06001362 return 0;
1363}
1364
1365static int ipw_set_sensitivity(struct ipw_priv *priv, u16 sens)
1366{
1367 struct host_cmd cmd = {
1368 .cmd = IPW_CMD_SENSITIVITY_CALIB,
1369 .len = sizeof(struct ipw_sensitivity_calib)
1370 };
1371 struct ipw_sensitivity_calib *calib = (struct ipw_sensitivity_calib *)
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001372 &cmd.param;
James Ketrenos43f66a62005-03-25 12:31:53 -06001373 calib->beacon_rssi_raw = sens;
1374 if (ipw_send_cmd(priv, &cmd)) {
1375 IPW_ERROR("failed to send SENSITIVITY CALIB command\n");
1376 return -1;
1377 }
1378
1379 return 0;
1380}
1381
1382static int ipw_send_associate(struct ipw_priv *priv,
1383 struct ipw_associate *associate)
1384{
1385 struct host_cmd cmd = {
1386 .cmd = IPW_CMD_ASSOCIATE,
1387 .len = sizeof(*associate)
1388 };
1389
1390 if (!priv || !associate) {
1391 IPW_ERROR("Invalid args\n");
1392 return -1;
1393 }
1394
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001395 memcpy(&cmd.param, associate, sizeof(*associate));
James Ketrenos43f66a62005-03-25 12:31:53 -06001396 if (ipw_send_cmd(priv, &cmd)) {
1397 IPW_ERROR("failed to send ASSOCIATE command\n");
1398 return -1;
1399 }
Jeff Garzikbf794512005-07-31 13:07:26 -04001400
James Ketrenos43f66a62005-03-25 12:31:53 -06001401 return 0;
1402}
1403
1404static int ipw_send_supported_rates(struct ipw_priv *priv,
1405 struct ipw_supported_rates *rates)
1406{
1407 struct host_cmd cmd = {
1408 .cmd = IPW_CMD_SUPPORTED_RATES,
1409 .len = sizeof(*rates)
1410 };
1411
1412 if (!priv || !rates) {
1413 IPW_ERROR("Invalid args\n");
1414 return -1;
1415 }
1416
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001417 memcpy(&cmd.param, rates, sizeof(*rates));
James Ketrenos43f66a62005-03-25 12:31:53 -06001418 if (ipw_send_cmd(priv, &cmd)) {
1419 IPW_ERROR("failed to send SUPPORTED_RATES command\n");
1420 return -1;
1421 }
Jeff Garzikbf794512005-07-31 13:07:26 -04001422
James Ketrenos43f66a62005-03-25 12:31:53 -06001423 return 0;
1424}
1425
1426static int ipw_set_random_seed(struct ipw_priv *priv)
1427{
1428 struct host_cmd cmd = {
1429 .cmd = IPW_CMD_SEED_NUMBER,
1430 .len = sizeof(u32)
1431 };
1432
1433 if (!priv) {
1434 IPW_ERROR("Invalid args\n");
1435 return -1;
1436 }
1437
1438 get_random_bytes(&cmd.param, sizeof(u32));
1439
1440 if (ipw_send_cmd(priv, &cmd)) {
1441 IPW_ERROR("failed to send SEED_NUMBER command\n");
1442 return -1;
1443 }
Jeff Garzikbf794512005-07-31 13:07:26 -04001444
James Ketrenos43f66a62005-03-25 12:31:53 -06001445 return 0;
1446}
1447
1448#if 0
1449static int ipw_send_card_disable(struct ipw_priv *priv, u32 phy_off)
1450{
1451 struct host_cmd cmd = {
1452 .cmd = IPW_CMD_CARD_DISABLE,
1453 .len = sizeof(u32)
1454 };
1455
1456 if (!priv) {
1457 IPW_ERROR("Invalid args\n");
1458 return -1;
1459 }
1460
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001461 *((u32 *) & cmd.param) = phy_off;
James Ketrenos43f66a62005-03-25 12:31:53 -06001462
1463 if (ipw_send_cmd(priv, &cmd)) {
1464 IPW_ERROR("failed to send CARD_DISABLE command\n");
1465 return -1;
1466 }
Jeff Garzikbf794512005-07-31 13:07:26 -04001467
James Ketrenos43f66a62005-03-25 12:31:53 -06001468 return 0;
1469}
1470#endif
1471
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001472static int ipw_send_tx_power(struct ipw_priv *priv, struct ipw_tx_power *power)
James Ketrenos43f66a62005-03-25 12:31:53 -06001473{
1474 struct host_cmd cmd = {
1475 .cmd = IPW_CMD_TX_POWER,
1476 .len = sizeof(*power)
1477 };
1478
1479 if (!priv || !power) {
1480 IPW_ERROR("Invalid args\n");
1481 return -1;
1482 }
1483
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001484 memcpy(&cmd.param, power, sizeof(*power));
James Ketrenos43f66a62005-03-25 12:31:53 -06001485 if (ipw_send_cmd(priv, &cmd)) {
1486 IPW_ERROR("failed to send TX_POWER command\n");
1487 return -1;
1488 }
Jeff Garzikbf794512005-07-31 13:07:26 -04001489
James Ketrenos43f66a62005-03-25 12:31:53 -06001490 return 0;
1491}
1492
1493static int ipw_send_rts_threshold(struct ipw_priv *priv, u16 rts)
1494{
1495 struct ipw_rts_threshold rts_threshold = {
1496 .rts_threshold = rts,
1497 };
1498 struct host_cmd cmd = {
1499 .cmd = IPW_CMD_RTS_THRESHOLD,
1500 .len = sizeof(rts_threshold)
1501 };
1502
1503 if (!priv) {
1504 IPW_ERROR("Invalid args\n");
1505 return -1;
1506 }
1507
1508 memcpy(&cmd.param, &rts_threshold, sizeof(rts_threshold));
1509 if (ipw_send_cmd(priv, &cmd)) {
1510 IPW_ERROR("failed to send RTS_THRESHOLD command\n");
1511 return -1;
1512 }
1513
1514 return 0;
1515}
1516
1517static int ipw_send_frag_threshold(struct ipw_priv *priv, u16 frag)
1518{
1519 struct ipw_frag_threshold frag_threshold = {
1520 .frag_threshold = frag,
1521 };
1522 struct host_cmd cmd = {
1523 .cmd = IPW_CMD_FRAG_THRESHOLD,
1524 .len = sizeof(frag_threshold)
1525 };
1526
1527 if (!priv) {
1528 IPW_ERROR("Invalid args\n");
1529 return -1;
1530 }
1531
1532 memcpy(&cmd.param, &frag_threshold, sizeof(frag_threshold));
1533 if (ipw_send_cmd(priv, &cmd)) {
1534 IPW_ERROR("failed to send FRAG_THRESHOLD command\n");
1535 return -1;
1536 }
1537
1538 return 0;
1539}
1540
1541static int ipw_send_power_mode(struct ipw_priv *priv, u32 mode)
1542{
1543 struct host_cmd cmd = {
1544 .cmd = IPW_CMD_POWER_MODE,
1545 .len = sizeof(u32)
1546 };
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001547 u32 *param = (u32 *) (&cmd.param);
James Ketrenos43f66a62005-03-25 12:31:53 -06001548
1549 if (!priv) {
1550 IPW_ERROR("Invalid args\n");
1551 return -1;
1552 }
Jeff Garzikbf794512005-07-31 13:07:26 -04001553
James Ketrenos43f66a62005-03-25 12:31:53 -06001554 /* If on battery, set to 3, if AC set to CAM, else user
1555 * level */
1556 switch (mode) {
1557 case IPW_POWER_BATTERY:
1558 *param = IPW_POWER_INDEX_3;
1559 break;
1560 case IPW_POWER_AC:
1561 *param = IPW_POWER_MODE_CAM;
1562 break;
1563 default:
1564 *param = mode;
1565 break;
1566 }
1567
1568 if (ipw_send_cmd(priv, &cmd)) {
1569 IPW_ERROR("failed to send POWER_MODE command\n");
1570 return -1;
1571 }
1572
1573 return 0;
1574}
1575
1576/*
1577 * The IPW device contains a Microwire compatible EEPROM that stores
1578 * various data like the MAC address. Usually the firmware has exclusive
1579 * access to the eeprom, but during device initialization (before the
1580 * device driver has sent the HostComplete command to the firmware) the
1581 * device driver has read access to the EEPROM by way of indirect addressing
1582 * through a couple of memory mapped registers.
1583 *
1584 * The following is a simplified implementation for pulling data out of the
1585 * the eeprom, along with some helper functions to find information in
1586 * the per device private data's copy of the eeprom.
1587 *
1588 * NOTE: To better understand how these functions work (i.e what is a chip
1589 * select and why do have to keep driving the eeprom clock?), read
1590 * just about any data sheet for a Microwire compatible EEPROM.
1591 */
1592
1593/* write a 32 bit value into the indirect accessor register */
1594static inline void eeprom_write_reg(struct ipw_priv *p, u32 data)
1595{
1596 ipw_write_reg32(p, FW_MEM_REG_EEPROM_ACCESS, data);
Jeff Garzikbf794512005-07-31 13:07:26 -04001597
James Ketrenos43f66a62005-03-25 12:31:53 -06001598 /* the eeprom requires some time to complete the operation */
1599 udelay(p->eeprom_delay);
1600
1601 return;
1602}
1603
1604/* perform a chip select operation */
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001605static inline void eeprom_cs(struct ipw_priv *priv)
James Ketrenos43f66a62005-03-25 12:31:53 -06001606{
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001607 eeprom_write_reg(priv, 0);
1608 eeprom_write_reg(priv, EEPROM_BIT_CS);
1609 eeprom_write_reg(priv, EEPROM_BIT_CS | EEPROM_BIT_SK);
1610 eeprom_write_reg(priv, EEPROM_BIT_CS);
James Ketrenos43f66a62005-03-25 12:31:53 -06001611}
1612
1613/* perform a chip select operation */
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001614static inline void eeprom_disable_cs(struct ipw_priv *priv)
James Ketrenos43f66a62005-03-25 12:31:53 -06001615{
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001616 eeprom_write_reg(priv, EEPROM_BIT_CS);
1617 eeprom_write_reg(priv, 0);
1618 eeprom_write_reg(priv, EEPROM_BIT_SK);
James Ketrenos43f66a62005-03-25 12:31:53 -06001619}
1620
1621/* push a single bit down to the eeprom */
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001622static inline void eeprom_write_bit(struct ipw_priv *p, u8 bit)
James Ketrenos43f66a62005-03-25 12:31:53 -06001623{
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001624 int d = (bit ? EEPROM_BIT_DI : 0);
1625 eeprom_write_reg(p, EEPROM_BIT_CS | d);
1626 eeprom_write_reg(p, EEPROM_BIT_CS | d | EEPROM_BIT_SK);
James Ketrenos43f66a62005-03-25 12:31:53 -06001627}
1628
1629/* push an opcode followed by an address down to the eeprom */
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001630static void eeprom_op(struct ipw_priv *priv, u8 op, u8 addr)
James Ketrenos43f66a62005-03-25 12:31:53 -06001631{
1632 int i;
1633
1634 eeprom_cs(priv);
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001635 eeprom_write_bit(priv, 1);
1636 eeprom_write_bit(priv, op & 2);
1637 eeprom_write_bit(priv, op & 1);
1638 for (i = 7; i >= 0; i--) {
1639 eeprom_write_bit(priv, addr & (1 << i));
James Ketrenos43f66a62005-03-25 12:31:53 -06001640 }
1641}
1642
1643/* pull 16 bits off the eeprom, one bit at a time */
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001644static u16 eeprom_read_u16(struct ipw_priv *priv, u8 addr)
James Ketrenos43f66a62005-03-25 12:31:53 -06001645{
1646 int i;
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001647 u16 r = 0;
Jeff Garzikbf794512005-07-31 13:07:26 -04001648
James Ketrenos43f66a62005-03-25 12:31:53 -06001649 /* Send READ Opcode */
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001650 eeprom_op(priv, EEPROM_CMD_READ, addr);
James Ketrenos43f66a62005-03-25 12:31:53 -06001651
1652 /* Send dummy bit */
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001653 eeprom_write_reg(priv, EEPROM_BIT_CS);
James Ketrenos43f66a62005-03-25 12:31:53 -06001654
1655 /* Read the byte off the eeprom one bit at a time */
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001656 for (i = 0; i < 16; i++) {
James Ketrenos43f66a62005-03-25 12:31:53 -06001657 u32 data = 0;
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001658 eeprom_write_reg(priv, EEPROM_BIT_CS | EEPROM_BIT_SK);
1659 eeprom_write_reg(priv, EEPROM_BIT_CS);
1660 data = ipw_read_reg32(priv, FW_MEM_REG_EEPROM_ACCESS);
1661 r = (r << 1) | ((data & EEPROM_BIT_DO) ? 1 : 0);
James Ketrenos43f66a62005-03-25 12:31:53 -06001662 }
Jeff Garzikbf794512005-07-31 13:07:26 -04001663
James Ketrenos43f66a62005-03-25 12:31:53 -06001664 /* Send another dummy bit */
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001665 eeprom_write_reg(priv, 0);
James Ketrenos43f66a62005-03-25 12:31:53 -06001666 eeprom_disable_cs(priv);
Jeff Garzikbf794512005-07-31 13:07:26 -04001667
James Ketrenos43f66a62005-03-25 12:31:53 -06001668 return r;
1669}
1670
1671/* helper function for pulling the mac address out of the private */
1672/* data's copy of the eeprom data */
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001673static void eeprom_parse_mac(struct ipw_priv *priv, u8 * mac)
James Ketrenos43f66a62005-03-25 12:31:53 -06001674{
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001675 u8 *ee = (u8 *) priv->eeprom;
James Ketrenos43f66a62005-03-25 12:31:53 -06001676 memcpy(mac, &ee[EEPROM_MAC_ADDRESS], 6);
1677}
1678
1679/*
1680 * Either the device driver (i.e. the host) or the firmware can
1681 * load eeprom data into the designated region in SRAM. If neither
1682 * happens then the FW will shutdown with a fatal error.
1683 *
1684 * In order to signal the FW to load the EEPROM, the EEPROM_LOAD_DISABLE
1685 * bit needs region of shared SRAM needs to be non-zero.
1686 */
1687static void ipw_eeprom_init_sram(struct ipw_priv *priv)
1688{
1689 int i;
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001690 u16 *eeprom = (u16 *) priv->eeprom;
Jeff Garzikbf794512005-07-31 13:07:26 -04001691
James Ketrenos43f66a62005-03-25 12:31:53 -06001692 IPW_DEBUG_TRACE(">>\n");
1693
1694 /* read entire contents of eeprom into private buffer */
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001695 for (i = 0; i < 128; i++)
1696 eeprom[i] = eeprom_read_u16(priv, (u8) i);
James Ketrenos43f66a62005-03-25 12:31:53 -06001697
Jeff Garzikbf794512005-07-31 13:07:26 -04001698 /*
1699 If the data looks correct, then copy it to our private
James Ketrenos43f66a62005-03-25 12:31:53 -06001700 copy. Otherwise let the firmware know to perform the operation
1701 on it's own
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001702 */
James Ketrenos43f66a62005-03-25 12:31:53 -06001703 if ((priv->eeprom + EEPROM_VERSION) != 0) {
1704 IPW_DEBUG_INFO("Writing EEPROM data into SRAM\n");
1705
1706 /* write the eeprom data to sram */
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001707 for (i = 0; i < CX2_EEPROM_IMAGE_SIZE; i++)
1708 ipw_write8(priv, IPW_EEPROM_DATA + i, priv->eeprom[i]);
James Ketrenos43f66a62005-03-25 12:31:53 -06001709
1710 /* Do not load eeprom data on fatal error or suspend */
1711 ipw_write32(priv, IPW_EEPROM_LOAD_DISABLE, 0);
1712 } else {
1713 IPW_DEBUG_INFO("Enabling FW initializationg of SRAM\n");
1714
1715 /* Load eeprom data on fatal error or suspend */
1716 ipw_write32(priv, IPW_EEPROM_LOAD_DISABLE, 1);
1717 }
1718
1719 IPW_DEBUG_TRACE("<<\n");
1720}
1721
James Ketrenos43f66a62005-03-25 12:31:53 -06001722static inline void ipw_zero_memory(struct ipw_priv *priv, u32 start, u32 count)
1723{
1724 count >>= 2;
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001725 if (!count)
1726 return;
James Ketrenos43f66a62005-03-25 12:31:53 -06001727 _ipw_write32(priv, CX2_AUTOINC_ADDR, start);
Jeff Garzikbf794512005-07-31 13:07:26 -04001728 while (count--)
James Ketrenos43f66a62005-03-25 12:31:53 -06001729 _ipw_write32(priv, CX2_AUTOINC_DATA, 0);
1730}
1731
1732static inline void ipw_fw_dma_reset_command_blocks(struct ipw_priv *priv)
1733{
1734 ipw_zero_memory(priv, CX2_SHARED_SRAM_DMA_CONTROL,
Jeff Garzikbf794512005-07-31 13:07:26 -04001735 CB_NUMBER_OF_ELEMENTS_SMALL *
James Ketrenos43f66a62005-03-25 12:31:53 -06001736 sizeof(struct command_block));
1737}
1738
1739static int ipw_fw_dma_enable(struct ipw_priv *priv)
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001740{ /* start dma engine but no transfers yet */
James Ketrenos43f66a62005-03-25 12:31:53 -06001741
1742 IPW_DEBUG_FW(">> : \n");
Jeff Garzikbf794512005-07-31 13:07:26 -04001743
James Ketrenos43f66a62005-03-25 12:31:53 -06001744 /* Start the dma */
1745 ipw_fw_dma_reset_command_blocks(priv);
Jeff Garzikbf794512005-07-31 13:07:26 -04001746
James Ketrenos43f66a62005-03-25 12:31:53 -06001747 /* Write CB base address */
1748 ipw_write_reg32(priv, CX2_DMA_I_CB_BASE, CX2_SHARED_SRAM_DMA_CONTROL);
1749
1750 IPW_DEBUG_FW("<< : \n");
1751 return 0;
1752}
1753
1754static void ipw_fw_dma_abort(struct ipw_priv *priv)
1755{
1756 u32 control = 0;
1757
1758 IPW_DEBUG_FW(">> :\n");
Jeff Garzikbf794512005-07-31 13:07:26 -04001759
1760 //set the Stop and Abort bit
James Ketrenos43f66a62005-03-25 12:31:53 -06001761 control = DMA_CONTROL_SMALL_CB_CONST_VALUE | DMA_CB_STOP_AND_ABORT;
1762 ipw_write_reg32(priv, CX2_DMA_I_DMA_CONTROL, control);
1763 priv->sram_desc.last_cb_index = 0;
Jeff Garzikbf794512005-07-31 13:07:26 -04001764
James Ketrenos43f66a62005-03-25 12:31:53 -06001765 IPW_DEBUG_FW("<< \n");
1766}
1767
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001768static int ipw_fw_dma_write_command_block(struct ipw_priv *priv, int index,
1769 struct command_block *cb)
James Ketrenos43f66a62005-03-25 12:31:53 -06001770{
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001771 u32 address =
1772 CX2_SHARED_SRAM_DMA_CONTROL +
1773 (sizeof(struct command_block) * index);
James Ketrenos43f66a62005-03-25 12:31:53 -06001774 IPW_DEBUG_FW(">> :\n");
1775
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001776 ipw_write_indirect(priv, address, (u8 *) cb,
1777 (int)sizeof(struct command_block));
James Ketrenos43f66a62005-03-25 12:31:53 -06001778
1779 IPW_DEBUG_FW("<< :\n");
1780 return 0;
1781
1782}
1783
1784static int ipw_fw_dma_kick(struct ipw_priv *priv)
1785{
1786 u32 control = 0;
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001787 u32 index = 0;
James Ketrenos43f66a62005-03-25 12:31:53 -06001788
1789 IPW_DEBUG_FW(">> :\n");
Jeff Garzikbf794512005-07-31 13:07:26 -04001790
James Ketrenos43f66a62005-03-25 12:31:53 -06001791 for (index = 0; index < priv->sram_desc.last_cb_index; index++)
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001792 ipw_fw_dma_write_command_block(priv, index,
1793 &priv->sram_desc.cb_list[index]);
James Ketrenos43f66a62005-03-25 12:31:53 -06001794
1795 /* Enable the DMA in the CSR register */
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001796 ipw_clear_bit(priv, CX2_RESET_REG,
1797 CX2_RESET_REG_MASTER_DISABLED |
1798 CX2_RESET_REG_STOP_MASTER);
Jeff Garzikbf794512005-07-31 13:07:26 -04001799
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001800 /* Set the Start bit. */
James Ketrenos43f66a62005-03-25 12:31:53 -06001801 control = DMA_CONTROL_SMALL_CB_CONST_VALUE | DMA_CB_START;
1802 ipw_write_reg32(priv, CX2_DMA_I_DMA_CONTROL, control);
1803
1804 IPW_DEBUG_FW("<< :\n");
1805 return 0;
1806}
1807
1808static void ipw_fw_dma_dump_command_block(struct ipw_priv *priv)
1809{
1810 u32 address;
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001811 u32 register_value = 0;
1812 u32 cb_fields_address = 0;
James Ketrenos43f66a62005-03-25 12:31:53 -06001813
1814 IPW_DEBUG_FW(">> :\n");
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001815 address = ipw_read_reg32(priv, CX2_DMA_I_CURRENT_CB);
1816 IPW_DEBUG_FW_INFO("Current CB is 0x%x \n", address);
James Ketrenos43f66a62005-03-25 12:31:53 -06001817
1818 /* Read the DMA Controlor register */
1819 register_value = ipw_read_reg32(priv, CX2_DMA_I_DMA_CONTROL);
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001820 IPW_DEBUG_FW_INFO("CX2_DMA_I_DMA_CONTROL is 0x%x \n", register_value);
James Ketrenos43f66a62005-03-25 12:31:53 -06001821
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001822 /* Print the CB values */
James Ketrenos43f66a62005-03-25 12:31:53 -06001823 cb_fields_address = address;
1824 register_value = ipw_read_reg32(priv, cb_fields_address);
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001825 IPW_DEBUG_FW_INFO("Current CB ControlField is 0x%x \n", register_value);
James Ketrenos43f66a62005-03-25 12:31:53 -06001826
1827 cb_fields_address += sizeof(u32);
1828 register_value = ipw_read_reg32(priv, cb_fields_address);
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001829 IPW_DEBUG_FW_INFO("Current CB Source Field is 0x%x \n", register_value);
James Ketrenos43f66a62005-03-25 12:31:53 -06001830
1831 cb_fields_address += sizeof(u32);
1832 register_value = ipw_read_reg32(priv, cb_fields_address);
1833 IPW_DEBUG_FW_INFO("Current CB Destination Field is 0x%x \n",
1834 register_value);
1835
1836 cb_fields_address += sizeof(u32);
1837 register_value = ipw_read_reg32(priv, cb_fields_address);
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001838 IPW_DEBUG_FW_INFO("Current CB Status Field is 0x%x \n", register_value);
James Ketrenos43f66a62005-03-25 12:31:53 -06001839
1840 IPW_DEBUG_FW(">> :\n");
1841}
1842
1843static int ipw_fw_dma_command_block_index(struct ipw_priv *priv)
1844{
1845 u32 current_cb_address = 0;
1846 u32 current_cb_index = 0;
1847
1848 IPW_DEBUG_FW("<< :\n");
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001849 current_cb_address = ipw_read_reg32(priv, CX2_DMA_I_CURRENT_CB);
Jeff Garzikbf794512005-07-31 13:07:26 -04001850
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001851 current_cb_index = (current_cb_address - CX2_SHARED_SRAM_DMA_CONTROL) /
1852 sizeof(struct command_block);
Jeff Garzikbf794512005-07-31 13:07:26 -04001853
James Ketrenos43f66a62005-03-25 12:31:53 -06001854 IPW_DEBUG_FW_INFO("Current CB index 0x%x address = 0x%X \n",
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001855 current_cb_index, current_cb_address);
James Ketrenos43f66a62005-03-25 12:31:53 -06001856
1857 IPW_DEBUG_FW(">> :\n");
1858 return current_cb_index;
1859
1860}
1861
1862static int ipw_fw_dma_add_command_block(struct ipw_priv *priv,
1863 u32 src_address,
1864 u32 dest_address,
1865 u32 length,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001866 int interrupt_enabled, int is_last)
James Ketrenos43f66a62005-03-25 12:31:53 -06001867{
1868
Jeff Garzikbf794512005-07-31 13:07:26 -04001869 u32 control = CB_VALID | CB_SRC_LE | CB_DEST_LE | CB_SRC_AUTOINC |
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001870 CB_SRC_IO_GATED | CB_DEST_AUTOINC | CB_SRC_SIZE_LONG |
1871 CB_DEST_SIZE_LONG;
James Ketrenos43f66a62005-03-25 12:31:53 -06001872 struct command_block *cb;
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001873 u32 last_cb_element = 0;
James Ketrenos43f66a62005-03-25 12:31:53 -06001874
1875 IPW_DEBUG_FW_INFO("src_address=0x%x dest_address=0x%x length=0x%x\n",
1876 src_address, dest_address, length);
1877
1878 if (priv->sram_desc.last_cb_index >= CB_NUMBER_OF_ELEMENTS_SMALL)
1879 return -1;
1880
1881 last_cb_element = priv->sram_desc.last_cb_index;
1882 cb = &priv->sram_desc.cb_list[last_cb_element];
1883 priv->sram_desc.last_cb_index++;
1884
1885 /* Calculate the new CB control word */
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001886 if (interrupt_enabled)
James Ketrenos43f66a62005-03-25 12:31:53 -06001887 control |= CB_INT_ENABLED;
1888
1889 if (is_last)
1890 control |= CB_LAST_VALID;
Jeff Garzikbf794512005-07-31 13:07:26 -04001891
James Ketrenos43f66a62005-03-25 12:31:53 -06001892 control |= length;
1893
1894 /* Calculate the CB Element's checksum value */
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001895 cb->status = control ^ src_address ^ dest_address;
James Ketrenos43f66a62005-03-25 12:31:53 -06001896
1897 /* Copy the Source and Destination addresses */
1898 cb->dest_addr = dest_address;
1899 cb->source_addr = src_address;
1900
1901 /* Copy the Control Word last */
1902 cb->control = control;
1903
1904 return 0;
1905}
1906
1907static int ipw_fw_dma_add_buffer(struct ipw_priv *priv,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001908 u32 src_phys, u32 dest_address, u32 length)
James Ketrenos43f66a62005-03-25 12:31:53 -06001909{
1910 u32 bytes_left = length;
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001911 u32 src_offset = 0;
1912 u32 dest_offset = 0;
James Ketrenos43f66a62005-03-25 12:31:53 -06001913 int status = 0;
1914 IPW_DEBUG_FW(">> \n");
1915 IPW_DEBUG_FW_INFO("src_phys=0x%x dest_address=0x%x length=0x%x\n",
1916 src_phys, dest_address, length);
1917 while (bytes_left > CB_MAX_LENGTH) {
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001918 status = ipw_fw_dma_add_command_block(priv,
1919 src_phys + src_offset,
1920 dest_address +
1921 dest_offset,
1922 CB_MAX_LENGTH, 0, 0);
James Ketrenos43f66a62005-03-25 12:31:53 -06001923 if (status) {
1924 IPW_DEBUG_FW_INFO(": Failed\n");
1925 return -1;
Jeff Garzikbf794512005-07-31 13:07:26 -04001926 } else
James Ketrenos43f66a62005-03-25 12:31:53 -06001927 IPW_DEBUG_FW_INFO(": Added new cb\n");
1928
1929 src_offset += CB_MAX_LENGTH;
1930 dest_offset += CB_MAX_LENGTH;
1931 bytes_left -= CB_MAX_LENGTH;
1932 }
1933
1934 /* add the buffer tail */
1935 if (bytes_left > 0) {
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001936 status =
1937 ipw_fw_dma_add_command_block(priv, src_phys + src_offset,
1938 dest_address + dest_offset,
1939 bytes_left, 0, 0);
James Ketrenos43f66a62005-03-25 12:31:53 -06001940 if (status) {
1941 IPW_DEBUG_FW_INFO(": Failed on the buffer tail\n");
1942 return -1;
Jeff Garzikbf794512005-07-31 13:07:26 -04001943 } else
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001944 IPW_DEBUG_FW_INFO
1945 (": Adding new cb - the buffer tail\n");
James Ketrenos43f66a62005-03-25 12:31:53 -06001946 }
Jeff Garzikbf794512005-07-31 13:07:26 -04001947
James Ketrenos43f66a62005-03-25 12:31:53 -06001948 IPW_DEBUG_FW("<< \n");
1949 return 0;
1950}
1951
1952static int ipw_fw_dma_wait(struct ipw_priv *priv)
1953{
1954 u32 current_index = 0;
1955 u32 watchdog = 0;
1956
1957 IPW_DEBUG_FW(">> : \n");
1958
1959 current_index = ipw_fw_dma_command_block_index(priv);
Jeff Garzikbf794512005-07-31 13:07:26 -04001960 IPW_DEBUG_FW_INFO("sram_desc.last_cb_index:0x%8X\n",
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001961 (int)priv->sram_desc.last_cb_index);
James Ketrenos43f66a62005-03-25 12:31:53 -06001962
1963 while (current_index < priv->sram_desc.last_cb_index) {
1964 udelay(50);
1965 current_index = ipw_fw_dma_command_block_index(priv);
1966
1967 watchdog++;
1968
1969 if (watchdog > 400) {
1970 IPW_DEBUG_FW_INFO("Timeout\n");
1971 ipw_fw_dma_dump_command_block(priv);
1972 ipw_fw_dma_abort(priv);
1973 return -1;
1974 }
1975 }
1976
1977 ipw_fw_dma_abort(priv);
1978
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001979 /*Disable the DMA in the CSR register */
1980 ipw_set_bit(priv, CX2_RESET_REG,
James Ketrenos43f66a62005-03-25 12:31:53 -06001981 CX2_RESET_REG_MASTER_DISABLED | CX2_RESET_REG_STOP_MASTER);
1982
1983 IPW_DEBUG_FW("<< dmaWaitSync \n");
1984 return 0;
1985}
1986
Jeff Garzikbf794512005-07-31 13:07:26 -04001987static void ipw_remove_current_network(struct ipw_priv *priv)
James Ketrenos43f66a62005-03-25 12:31:53 -06001988{
1989 struct list_head *element, *safe;
Jeff Garzikbf794512005-07-31 13:07:26 -04001990 struct ieee80211_network *network = NULL;
James Ketrenos43f66a62005-03-25 12:31:53 -06001991 list_for_each_safe(element, safe, &priv->ieee->network_list) {
1992 network = list_entry(element, struct ieee80211_network, list);
1993 if (!memcmp(network->bssid, priv->bssid, ETH_ALEN)) {
1994 list_del(element);
Jeff Garzikbf794512005-07-31 13:07:26 -04001995 list_add_tail(&network->list,
James Ketrenos43f66a62005-03-25 12:31:53 -06001996 &priv->ieee->network_free_list);
1997 }
1998 }
1999}
2000
2001/**
Jeff Garzikbf794512005-07-31 13:07:26 -04002002 * Check that card is still alive.
James Ketrenos43f66a62005-03-25 12:31:53 -06002003 * Reads debug register from domain0.
2004 * If card is present, pre-defined value should
2005 * be found there.
Jeff Garzikbf794512005-07-31 13:07:26 -04002006 *
James Ketrenos43f66a62005-03-25 12:31:53 -06002007 * @param priv
2008 * @return 1 if card is present, 0 otherwise
2009 */
2010static inline int ipw_alive(struct ipw_priv *priv)
2011{
2012 return ipw_read32(priv, 0x90) == 0xd55555d5;
2013}
2014
2015static inline int ipw_poll_bit(struct ipw_priv *priv, u32 addr, u32 mask,
2016 int timeout)
2017{
2018 int i = 0;
2019
2020 do {
Jeff Garzikbf794512005-07-31 13:07:26 -04002021 if ((ipw_read32(priv, addr) & mask) == mask)
James Ketrenos43f66a62005-03-25 12:31:53 -06002022 return i;
2023 mdelay(10);
2024 i += 10;
2025 } while (i < timeout);
Jeff Garzikbf794512005-07-31 13:07:26 -04002026
James Ketrenos43f66a62005-03-25 12:31:53 -06002027 return -ETIME;
2028}
2029
Jeff Garzikbf794512005-07-31 13:07:26 -04002030/* These functions load the firmware and micro code for the operation of
James Ketrenos43f66a62005-03-25 12:31:53 -06002031 * the ipw hardware. It assumes the buffer has all the bits for the
2032 * image and the caller is handling the memory allocation and clean up.
2033 */
2034
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002035static int ipw_stop_master(struct ipw_priv *priv)
James Ketrenos43f66a62005-03-25 12:31:53 -06002036{
2037 int rc;
Jeff Garzikbf794512005-07-31 13:07:26 -04002038
James Ketrenos43f66a62005-03-25 12:31:53 -06002039 IPW_DEBUG_TRACE(">> \n");
2040 /* stop master. typical delay - 0 */
2041 ipw_set_bit(priv, CX2_RESET_REG, CX2_RESET_REG_STOP_MASTER);
2042
2043 rc = ipw_poll_bit(priv, CX2_RESET_REG,
2044 CX2_RESET_REG_MASTER_DISABLED, 100);
2045 if (rc < 0) {
2046 IPW_ERROR("stop master failed in 10ms\n");
2047 return -1;
2048 }
2049
2050 IPW_DEBUG_INFO("stop master %dms\n", rc);
2051
2052 return rc;
2053}
2054
2055static void ipw_arc_release(struct ipw_priv *priv)
2056{
2057 IPW_DEBUG_TRACE(">> \n");
2058 mdelay(5);
2059
2060 ipw_clear_bit(priv, CX2_RESET_REG, CBD_RESET_REG_PRINCETON_RESET);
2061
2062 /* no one knows timing, for safety add some delay */
2063 mdelay(5);
2064}
2065
2066struct fw_header {
2067 u32 version;
2068 u32 mode;
2069};
2070
2071struct fw_chunk {
2072 u32 address;
2073 u32 length;
2074};
2075
2076#define IPW_FW_MAJOR_VERSION 2
2077#define IPW_FW_MINOR_VERSION 2
2078
2079#define IPW_FW_MINOR(x) ((x & 0xff) >> 8)
2080#define IPW_FW_MAJOR(x) (x & 0xff)
2081
2082#define IPW_FW_VERSION ((IPW_FW_MINOR_VERSION << 8) | \
2083 IPW_FW_MAJOR_VERSION)
2084
2085#define IPW_FW_PREFIX "ipw-" __stringify(IPW_FW_MAJOR_VERSION) \
2086"." __stringify(IPW_FW_MINOR_VERSION) "-"
2087
2088#if IPW_FW_MAJOR_VERSION >= 2 && IPW_FW_MINOR_VERSION > 0
2089#define IPW_FW_NAME(x) IPW_FW_PREFIX "" x ".fw"
2090#else
2091#define IPW_FW_NAME(x) "ipw2200_" x ".fw"
2092#endif
2093
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002094static int ipw_load_ucode(struct ipw_priv *priv, u8 * data, size_t len)
James Ketrenos43f66a62005-03-25 12:31:53 -06002095{
2096 int rc = 0, i, addr;
2097 u8 cr = 0;
2098 u16 *image;
2099
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002100 image = (u16 *) data;
Jeff Garzikbf794512005-07-31 13:07:26 -04002101
James Ketrenos43f66a62005-03-25 12:31:53 -06002102 IPW_DEBUG_TRACE(">> \n");
2103
2104 rc = ipw_stop_master(priv);
2105
2106 if (rc < 0)
2107 return rc;
Jeff Garzikbf794512005-07-31 13:07:26 -04002108
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002109// spin_lock_irqsave(&priv->lock, flags);
Jeff Garzikbf794512005-07-31 13:07:26 -04002110
James Ketrenos43f66a62005-03-25 12:31:53 -06002111 for (addr = CX2_SHARED_LOWER_BOUND;
2112 addr < CX2_REGISTER_DOMAIN1_END; addr += 4) {
2113 ipw_write32(priv, addr, 0);
2114 }
2115
2116 /* no ucode (yet) */
2117 memset(&priv->dino_alive, 0, sizeof(priv->dino_alive));
2118 /* destroy DMA queues */
2119 /* reset sequence */
2120
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002121 ipw_write_reg32(priv, CX2_MEM_HALT_AND_RESET, CX2_BIT_HALT_RESET_ON);
James Ketrenos43f66a62005-03-25 12:31:53 -06002122 ipw_arc_release(priv);
2123 ipw_write_reg32(priv, CX2_MEM_HALT_AND_RESET, CX2_BIT_HALT_RESET_OFF);
2124 mdelay(1);
2125
2126 /* reset PHY */
2127 ipw_write_reg32(priv, CX2_INTERNAL_CMD_EVENT, CX2_BASEBAND_POWER_DOWN);
2128 mdelay(1);
Jeff Garzikbf794512005-07-31 13:07:26 -04002129
James Ketrenos43f66a62005-03-25 12:31:53 -06002130 ipw_write_reg32(priv, CX2_INTERNAL_CMD_EVENT, 0);
2131 mdelay(1);
Jeff Garzikbf794512005-07-31 13:07:26 -04002132
James Ketrenos43f66a62005-03-25 12:31:53 -06002133 /* enable ucode store */
2134 ipw_write_reg8(priv, DINO_CONTROL_REG, 0x0);
2135 ipw_write_reg8(priv, DINO_CONTROL_REG, DINO_ENABLE_CS);
2136 mdelay(1);
2137
2138 /* write ucode */
2139 /**
2140 * @bug
2141 * Do NOT set indirect address register once and then
2142 * store data to indirect data register in the loop.
2143 * It seems very reasonable, but in this case DINO do not
2144 * accept ucode. It is essential to set address each time.
2145 */
2146 /* load new ipw uCode */
2147 for (i = 0; i < len / 2; i++)
2148 ipw_write_reg16(priv, CX2_BASEBAND_CONTROL_STORE, image[i]);
2149
James Ketrenos43f66a62005-03-25 12:31:53 -06002150 /* enable DINO */
2151 ipw_write_reg8(priv, CX2_BASEBAND_CONTROL_STATUS, 0);
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002152 ipw_write_reg8(priv, CX2_BASEBAND_CONTROL_STATUS, DINO_ENABLE_SYSTEM);
James Ketrenos43f66a62005-03-25 12:31:53 -06002153
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002154 /* this is where the igx / win driver deveates from the VAP driver. */
James Ketrenos43f66a62005-03-25 12:31:53 -06002155
2156 /* wait for alive response */
2157 for (i = 0; i < 100; i++) {
2158 /* poll for incoming data */
2159 cr = ipw_read_reg8(priv, CX2_BASEBAND_CONTROL_STATUS);
2160 if (cr & DINO_RXFIFO_DATA)
2161 break;
2162 mdelay(1);
2163 }
2164
2165 if (cr & DINO_RXFIFO_DATA) {
2166 /* alive_command_responce size is NOT multiple of 4 */
2167 u32 response_buffer[(sizeof(priv->dino_alive) + 3) / 4];
Jeff Garzikbf794512005-07-31 13:07:26 -04002168
2169 for (i = 0; i < ARRAY_SIZE(response_buffer); i++)
James Ketrenos43f66a62005-03-25 12:31:53 -06002170 response_buffer[i] =
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002171 ipw_read_reg32(priv, CX2_BASEBAND_RX_FIFO_READ);
James Ketrenos43f66a62005-03-25 12:31:53 -06002172 memcpy(&priv->dino_alive, response_buffer,
2173 sizeof(priv->dino_alive));
2174 if (priv->dino_alive.alive_command == 1
2175 && priv->dino_alive.ucode_valid == 1) {
2176 rc = 0;
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002177 IPW_DEBUG_INFO
2178 ("Microcode OK, rev. %d (0x%x) dev. %d (0x%x) "
2179 "of %02d/%02d/%02d %02d:%02d\n",
2180 priv->dino_alive.software_revision,
2181 priv->dino_alive.software_revision,
2182 priv->dino_alive.device_identifier,
2183 priv->dino_alive.device_identifier,
2184 priv->dino_alive.time_stamp[0],
2185 priv->dino_alive.time_stamp[1],
2186 priv->dino_alive.time_stamp[2],
2187 priv->dino_alive.time_stamp[3],
2188 priv->dino_alive.time_stamp[4]);
James Ketrenos43f66a62005-03-25 12:31:53 -06002189 } else {
2190 IPW_DEBUG_INFO("Microcode is not alive\n");
2191 rc = -EINVAL;
2192 }
2193 } else {
2194 IPW_DEBUG_INFO("No alive response from DINO\n");
2195 rc = -ETIME;
2196 }
2197
2198 /* disable DINO, otherwise for some reason
2199 firmware have problem getting alive resp. */
2200 ipw_write_reg8(priv, CX2_BASEBAND_CONTROL_STATUS, 0);
2201
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002202// spin_unlock_irqrestore(&priv->lock, flags);
James Ketrenos43f66a62005-03-25 12:31:53 -06002203
2204 return rc;
2205}
2206
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002207static int ipw_load_firmware(struct ipw_priv *priv, u8 * data, size_t len)
James Ketrenos43f66a62005-03-25 12:31:53 -06002208{
2209 int rc = -1;
2210 int offset = 0;
2211 struct fw_chunk *chunk;
2212 dma_addr_t shared_phys;
2213 u8 *shared_virt;
2214
2215 IPW_DEBUG_TRACE("<< : \n");
2216 shared_virt = pci_alloc_consistent(priv->pci_dev, len, &shared_phys);
2217
2218 if (!shared_virt)
2219 return -ENOMEM;
2220
2221 memmove(shared_virt, data, len);
2222
2223 /* Start the Dma */
2224 rc = ipw_fw_dma_enable(priv);
2225
2226 if (priv->sram_desc.last_cb_index > 0) {
2227 /* the DMA is already ready this would be a bug. */
2228 BUG();
2229 goto out;
2230 }
2231
2232 do {
2233 chunk = (struct fw_chunk *)(data + offset);
2234 offset += sizeof(struct fw_chunk);
2235 /* build DMA packet and queue up for sending */
Jeff Garzikbf794512005-07-31 13:07:26 -04002236 /* dma to chunk->address, the chunk->length bytes from data +
James Ketrenos43f66a62005-03-25 12:31:53 -06002237 * offeset*/
2238 /* Dma loading */
2239 rc = ipw_fw_dma_add_buffer(priv, shared_phys + offset,
2240 chunk->address, chunk->length);
2241 if (rc) {
2242 IPW_DEBUG_INFO("dmaAddBuffer Failed\n");
2243 goto out;
2244 }
Jeff Garzikbf794512005-07-31 13:07:26 -04002245
James Ketrenos43f66a62005-03-25 12:31:53 -06002246 offset += chunk->length;
2247 } while (offset < len);
2248
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002249 /* Run the DMA and wait for the answer */
James Ketrenos43f66a62005-03-25 12:31:53 -06002250 rc = ipw_fw_dma_kick(priv);
2251 if (rc) {
2252 IPW_ERROR("dmaKick Failed\n");
2253 goto out;
2254 }
2255
2256 rc = ipw_fw_dma_wait(priv);
2257 if (rc) {
2258 IPW_ERROR("dmaWaitSync Failed\n");
2259 goto out;
2260 }
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002261 out:
2262 pci_free_consistent(priv->pci_dev, len, shared_virt, shared_phys);
James Ketrenos43f66a62005-03-25 12:31:53 -06002263 return rc;
2264}
2265
2266/* stop nic */
2267static int ipw_stop_nic(struct ipw_priv *priv)
2268{
2269 int rc = 0;
2270
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002271 /* stop */
James Ketrenos43f66a62005-03-25 12:31:53 -06002272 ipw_write32(priv, CX2_RESET_REG, CX2_RESET_REG_STOP_MASTER);
Jeff Garzikbf794512005-07-31 13:07:26 -04002273
2274 rc = ipw_poll_bit(priv, CX2_RESET_REG,
2275 CX2_RESET_REG_MASTER_DISABLED, 500);
James Ketrenos43f66a62005-03-25 12:31:53 -06002276 if (rc < 0) {
2277 IPW_ERROR("wait for reg master disabled failed\n");
2278 return rc;
Jeff Garzikbf794512005-07-31 13:07:26 -04002279 }
James Ketrenos43f66a62005-03-25 12:31:53 -06002280
2281 ipw_set_bit(priv, CX2_RESET_REG, CBD_RESET_REG_PRINCETON_RESET);
Jeff Garzikbf794512005-07-31 13:07:26 -04002282
James Ketrenos43f66a62005-03-25 12:31:53 -06002283 return rc;
2284}
2285
2286static void ipw_start_nic(struct ipw_priv *priv)
2287{
2288 IPW_DEBUG_TRACE(">>\n");
2289
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002290 /* prvHwStartNic release ARC */
James Ketrenos43f66a62005-03-25 12:31:53 -06002291 ipw_clear_bit(priv, CX2_RESET_REG,
Jeff Garzikbf794512005-07-31 13:07:26 -04002292 CX2_RESET_REG_MASTER_DISABLED |
2293 CX2_RESET_REG_STOP_MASTER |
James Ketrenos43f66a62005-03-25 12:31:53 -06002294 CBD_RESET_REG_PRINCETON_RESET);
Jeff Garzikbf794512005-07-31 13:07:26 -04002295
James Ketrenos43f66a62005-03-25 12:31:53 -06002296 /* enable power management */
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002297 ipw_set_bit(priv, CX2_GP_CNTRL_RW,
2298 CX2_GP_CNTRL_BIT_HOST_ALLOWS_STANDBY);
James Ketrenos43f66a62005-03-25 12:31:53 -06002299
2300 IPW_DEBUG_TRACE("<<\n");
2301}
Jeff Garzikbf794512005-07-31 13:07:26 -04002302
James Ketrenos43f66a62005-03-25 12:31:53 -06002303static int ipw_init_nic(struct ipw_priv *priv)
2304{
2305 int rc;
2306
2307 IPW_DEBUG_TRACE(">>\n");
Jeff Garzikbf794512005-07-31 13:07:26 -04002308 /* reset */
James Ketrenos43f66a62005-03-25 12:31:53 -06002309 /*prvHwInitNic */
2310 /* set "initialization complete" bit to move adapter to D0 state */
2311 ipw_set_bit(priv, CX2_GP_CNTRL_RW, CX2_GP_CNTRL_BIT_INIT_DONE);
2312
2313 /* low-level PLL activation */
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002314 ipw_write32(priv, CX2_READ_INT_REGISTER,
2315 CX2_BIT_INT_HOST_SRAM_READ_INT_REGISTER);
James Ketrenos43f66a62005-03-25 12:31:53 -06002316
2317 /* wait for clock stabilization */
Jeff Garzikbf794512005-07-31 13:07:26 -04002318 rc = ipw_poll_bit(priv, CX2_GP_CNTRL_RW,
2319 CX2_GP_CNTRL_BIT_CLOCK_READY, 250);
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002320 if (rc < 0)
James Ketrenos43f66a62005-03-25 12:31:53 -06002321 IPW_DEBUG_INFO("FAILED wait for clock stablization\n");
2322
2323 /* assert SW reset */
2324 ipw_set_bit(priv, CX2_RESET_REG, CX2_RESET_REG_SW_RESET);
2325
2326 udelay(10);
2327
2328 /* set "initialization complete" bit to move adapter to D0 state */
2329 ipw_set_bit(priv, CX2_GP_CNTRL_RW, CX2_GP_CNTRL_BIT_INIT_DONE);
2330
2331 IPW_DEBUG_TRACE(">>\n");
2332 return 0;
2333}
2334
Jeff Garzikbf794512005-07-31 13:07:26 -04002335/* Call this function from process context, it will sleep in request_firmware.
James Ketrenos43f66a62005-03-25 12:31:53 -06002336 * Probe is an ok place to call this from.
2337 */
2338static int ipw_reset_nic(struct ipw_priv *priv)
2339{
2340 int rc = 0;
2341
2342 IPW_DEBUG_TRACE(">>\n");
Jeff Garzikbf794512005-07-31 13:07:26 -04002343
James Ketrenos43f66a62005-03-25 12:31:53 -06002344 rc = ipw_init_nic(priv);
Jeff Garzikbf794512005-07-31 13:07:26 -04002345
James Ketrenos43f66a62005-03-25 12:31:53 -06002346 /* Clear the 'host command active' bit... */
2347 priv->status &= ~STATUS_HCMD_ACTIVE;
2348 wake_up_interruptible(&priv->wait_command_queue);
2349
2350 IPW_DEBUG_TRACE("<<\n");
2351 return rc;
Jeff Garzikbf794512005-07-31 13:07:26 -04002352}
James Ketrenos43f66a62005-03-25 12:31:53 -06002353
Jeff Garzikbf794512005-07-31 13:07:26 -04002354static int ipw_get_fw(struct ipw_priv *priv,
James Ketrenos43f66a62005-03-25 12:31:53 -06002355 const struct firmware **fw, const char *name)
2356{
2357 struct fw_header *header;
2358 int rc;
2359
2360 /* ask firmware_class module to get the boot firmware off disk */
2361 rc = request_firmware(fw, name, &priv->pci_dev->dev);
2362 if (rc < 0) {
2363 IPW_ERROR("%s load failed: Reason %d\n", name, rc);
2364 return rc;
Jeff Garzikbf794512005-07-31 13:07:26 -04002365 }
James Ketrenos43f66a62005-03-25 12:31:53 -06002366
2367 header = (struct fw_header *)(*fw)->data;
2368 if (IPW_FW_MAJOR(header->version) != IPW_FW_MAJOR_VERSION) {
2369 IPW_ERROR("'%s' firmware version not compatible (%d != %d)\n",
2370 name,
2371 IPW_FW_MAJOR(header->version), IPW_FW_MAJOR_VERSION);
2372 return -EINVAL;
2373 }
2374
Jiri Bencaaa4d302005-06-07 14:58:41 +02002375 IPW_DEBUG_INFO("Loading firmware '%s' file v%d.%d (%zd bytes)\n",
James Ketrenos43f66a62005-03-25 12:31:53 -06002376 name,
2377 IPW_FW_MAJOR(header->version),
2378 IPW_FW_MINOR(header->version),
2379 (*fw)->size - sizeof(struct fw_header));
2380 return 0;
2381}
2382
2383#define CX2_RX_BUF_SIZE (3000)
2384
2385static inline void ipw_rx_queue_reset(struct ipw_priv *priv,
2386 struct ipw_rx_queue *rxq)
2387{
2388 unsigned long flags;
2389 int i;
2390
2391 spin_lock_irqsave(&rxq->lock, flags);
2392
2393 INIT_LIST_HEAD(&rxq->rx_free);
2394 INIT_LIST_HEAD(&rxq->rx_used);
2395
2396 /* Fill the rx_used queue with _all_ of the Rx buffers */
2397 for (i = 0; i < RX_FREE_BUFFERS + RX_QUEUE_SIZE; i++) {
2398 /* In the reset function, these buffers may have been allocated
2399 * to an SKB, so we need to unmap and free potential storage */
2400 if (rxq->pool[i].skb != NULL) {
2401 pci_unmap_single(priv->pci_dev, rxq->pool[i].dma_addr,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002402 CX2_RX_BUF_SIZE, PCI_DMA_FROMDEVICE);
James Ketrenos43f66a62005-03-25 12:31:53 -06002403 dev_kfree_skb(rxq->pool[i].skb);
2404 }
2405 list_add_tail(&rxq->pool[i].list, &rxq->rx_used);
2406 }
Jeff Garzikbf794512005-07-31 13:07:26 -04002407
James Ketrenos43f66a62005-03-25 12:31:53 -06002408 /* Set us so that we have processed and used all buffers, but have
2409 * not restocked the Rx queue with fresh buffers */
2410 rxq->read = rxq->write = 0;
2411 rxq->processed = RX_QUEUE_SIZE - 1;
2412 rxq->free_count = 0;
2413 spin_unlock_irqrestore(&rxq->lock, flags);
2414}
2415
2416#ifdef CONFIG_PM
2417static int fw_loaded = 0;
2418static const struct firmware *bootfw = NULL;
2419static const struct firmware *firmware = NULL;
2420static const struct firmware *ucode = NULL;
2421#endif
2422
2423static int ipw_load(struct ipw_priv *priv)
2424{
2425#ifndef CONFIG_PM
2426 const struct firmware *bootfw = NULL;
2427 const struct firmware *firmware = NULL;
2428 const struct firmware *ucode = NULL;
2429#endif
2430 int rc = 0, retries = 3;
2431
2432#ifdef CONFIG_PM
2433 if (!fw_loaded) {
2434#endif
2435 rc = ipw_get_fw(priv, &bootfw, IPW_FW_NAME("boot"));
Jeff Garzikbf794512005-07-31 13:07:26 -04002436 if (rc)
James Ketrenos43f66a62005-03-25 12:31:53 -06002437 goto error;
Jeff Garzikbf794512005-07-31 13:07:26 -04002438
James Ketrenos43f66a62005-03-25 12:31:53 -06002439 switch (priv->ieee->iw_mode) {
2440 case IW_MODE_ADHOC:
Jeff Garzikbf794512005-07-31 13:07:26 -04002441 rc = ipw_get_fw(priv, &ucode,
James Ketrenos43f66a62005-03-25 12:31:53 -06002442 IPW_FW_NAME("ibss_ucode"));
Jeff Garzikbf794512005-07-31 13:07:26 -04002443 if (rc)
James Ketrenos43f66a62005-03-25 12:31:53 -06002444 goto error;
Jeff Garzikbf794512005-07-31 13:07:26 -04002445
James Ketrenos43f66a62005-03-25 12:31:53 -06002446 rc = ipw_get_fw(priv, &firmware, IPW_FW_NAME("ibss"));
2447 break;
Jeff Garzikbf794512005-07-31 13:07:26 -04002448
James Ketrenos43f66a62005-03-25 12:31:53 -06002449#ifdef CONFIG_IPW_PROMISC
2450 case IW_MODE_MONITOR:
Jeff Garzikbf794512005-07-31 13:07:26 -04002451 rc = ipw_get_fw(priv, &ucode,
James Ketrenos43f66a62005-03-25 12:31:53 -06002452 IPW_FW_NAME("ibss_ucode"));
Jeff Garzikbf794512005-07-31 13:07:26 -04002453 if (rc)
James Ketrenos43f66a62005-03-25 12:31:53 -06002454 goto error;
Jeff Garzikbf794512005-07-31 13:07:26 -04002455
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002456 rc = ipw_get_fw(priv, &firmware,
2457 IPW_FW_NAME("sniffer"));
James Ketrenos43f66a62005-03-25 12:31:53 -06002458 break;
2459#endif
2460 case IW_MODE_INFRA:
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002461 rc = ipw_get_fw(priv, &ucode, IPW_FW_NAME("bss_ucode"));
Jeff Garzikbf794512005-07-31 13:07:26 -04002462 if (rc)
James Ketrenos43f66a62005-03-25 12:31:53 -06002463 goto error;
Jeff Garzikbf794512005-07-31 13:07:26 -04002464
James Ketrenos43f66a62005-03-25 12:31:53 -06002465 rc = ipw_get_fw(priv, &firmware, IPW_FW_NAME("bss"));
2466 break;
Jeff Garzikbf794512005-07-31 13:07:26 -04002467
James Ketrenos43f66a62005-03-25 12:31:53 -06002468 default:
2469 rc = -EINVAL;
2470 }
2471
Jeff Garzikbf794512005-07-31 13:07:26 -04002472 if (rc)
James Ketrenos43f66a62005-03-25 12:31:53 -06002473 goto error;
2474
2475#ifdef CONFIG_PM
2476 fw_loaded = 1;
2477 }
2478#endif
2479
2480 if (!priv->rxq)
2481 priv->rxq = ipw_rx_queue_alloc(priv);
2482 else
2483 ipw_rx_queue_reset(priv, priv->rxq);
2484 if (!priv->rxq) {
2485 IPW_ERROR("Unable to initialize Rx queue\n");
2486 goto error;
2487 }
2488
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002489 retry:
James Ketrenos43f66a62005-03-25 12:31:53 -06002490 /* Ensure interrupts are disabled */
2491 ipw_write32(priv, CX2_INTA_MASK_R, ~CX2_INTA_MASK_ALL);
2492 priv->status &= ~STATUS_INT_ENABLED;
2493
2494 /* ack pending interrupts */
2495 ipw_write32(priv, CX2_INTA_RW, CX2_INTA_MASK_ALL);
Jeff Garzikbf794512005-07-31 13:07:26 -04002496
James Ketrenos43f66a62005-03-25 12:31:53 -06002497 ipw_stop_nic(priv);
2498
2499 rc = ipw_reset_nic(priv);
2500 if (rc) {
2501 IPW_ERROR("Unable to reset NIC\n");
2502 goto error;
2503 }
2504
Jeff Garzikbf794512005-07-31 13:07:26 -04002505 ipw_zero_memory(priv, CX2_NIC_SRAM_LOWER_BOUND,
James Ketrenos43f66a62005-03-25 12:31:53 -06002506 CX2_NIC_SRAM_UPPER_BOUND - CX2_NIC_SRAM_LOWER_BOUND);
2507
2508 /* DMA the initial boot firmware into the device */
Jeff Garzikbf794512005-07-31 13:07:26 -04002509 rc = ipw_load_firmware(priv, bootfw->data + sizeof(struct fw_header),
James Ketrenos43f66a62005-03-25 12:31:53 -06002510 bootfw->size - sizeof(struct fw_header));
2511 if (rc < 0) {
2512 IPW_ERROR("Unable to load boot firmware\n");
2513 goto error;
2514 }
2515
2516 /* kick start the device */
2517 ipw_start_nic(priv);
2518
2519 /* wait for the device to finish it's initial startup sequence */
Jeff Garzikbf794512005-07-31 13:07:26 -04002520 rc = ipw_poll_bit(priv, CX2_INTA_RW,
2521 CX2_INTA_BIT_FW_INITIALIZATION_DONE, 500);
James Ketrenos43f66a62005-03-25 12:31:53 -06002522 if (rc < 0) {
2523 IPW_ERROR("device failed to boot initial fw image\n");
2524 goto error;
2525 }
2526 IPW_DEBUG_INFO("initial device response after %dms\n", rc);
2527
Jeff Garzikbf794512005-07-31 13:07:26 -04002528 /* ack fw init done interrupt */
James Ketrenos43f66a62005-03-25 12:31:53 -06002529 ipw_write32(priv, CX2_INTA_RW, CX2_INTA_BIT_FW_INITIALIZATION_DONE);
2530
2531 /* DMA the ucode into the device */
Jeff Garzikbf794512005-07-31 13:07:26 -04002532 rc = ipw_load_ucode(priv, ucode->data + sizeof(struct fw_header),
James Ketrenos43f66a62005-03-25 12:31:53 -06002533 ucode->size - sizeof(struct fw_header));
2534 if (rc < 0) {
2535 IPW_ERROR("Unable to load ucode\n");
2536 goto error;
2537 }
Jeff Garzikbf794512005-07-31 13:07:26 -04002538
James Ketrenos43f66a62005-03-25 12:31:53 -06002539 /* stop nic */
2540 ipw_stop_nic(priv);
2541
2542 /* DMA bss firmware into the device */
Jeff Garzikbf794512005-07-31 13:07:26 -04002543 rc = ipw_load_firmware(priv, firmware->data +
2544 sizeof(struct fw_header),
James Ketrenos43f66a62005-03-25 12:31:53 -06002545 firmware->size - sizeof(struct fw_header));
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002546 if (rc < 0) {
James Ketrenos43f66a62005-03-25 12:31:53 -06002547 IPW_ERROR("Unable to load firmware\n");
2548 goto error;
2549 }
2550
2551 ipw_write32(priv, IPW_EEPROM_LOAD_DISABLE, 0);
2552
2553 rc = ipw_queue_reset(priv);
2554 if (rc) {
2555 IPW_ERROR("Unable to initialize queues\n");
2556 goto error;
2557 }
2558
2559 /* Ensure interrupts are disabled */
2560 ipw_write32(priv, CX2_INTA_MASK_R, ~CX2_INTA_MASK_ALL);
Jeff Garzikbf794512005-07-31 13:07:26 -04002561
James Ketrenos43f66a62005-03-25 12:31:53 -06002562 /* kick start the device */
2563 ipw_start_nic(priv);
2564
2565 if (ipw_read32(priv, CX2_INTA_RW) & CX2_INTA_BIT_PARITY_ERROR) {
2566 if (retries > 0) {
2567 IPW_WARNING("Parity error. Retrying init.\n");
2568 retries--;
2569 goto retry;
2570 }
2571
2572 IPW_ERROR("TODO: Handle parity error -- schedule restart?\n");
2573 rc = -EIO;
2574 goto error;
2575 }
2576
2577 /* wait for the device */
Jeff Garzikbf794512005-07-31 13:07:26 -04002578 rc = ipw_poll_bit(priv, CX2_INTA_RW,
2579 CX2_INTA_BIT_FW_INITIALIZATION_DONE, 500);
James Ketrenos43f66a62005-03-25 12:31:53 -06002580 if (rc < 0) {
2581 IPW_ERROR("device failed to start after 500ms\n");
2582 goto error;
2583 }
2584 IPW_DEBUG_INFO("device response after %dms\n", rc);
2585
2586 /* ack fw init done interrupt */
2587 ipw_write32(priv, CX2_INTA_RW, CX2_INTA_BIT_FW_INITIALIZATION_DONE);
2588
2589 /* read eeprom data and initialize the eeprom region of sram */
2590 priv->eeprom_delay = 1;
Jeff Garzikbf794512005-07-31 13:07:26 -04002591 ipw_eeprom_init_sram(priv);
James Ketrenos43f66a62005-03-25 12:31:53 -06002592
2593 /* enable interrupts */
2594 ipw_enable_interrupts(priv);
2595
2596 /* Ensure our queue has valid packets */
2597 ipw_rx_queue_replenish(priv);
2598
2599 ipw_write32(priv, CX2_RX_READ_INDEX, priv->rxq->read);
2600
2601 /* ack pending interrupts */
2602 ipw_write32(priv, CX2_INTA_RW, CX2_INTA_MASK_ALL);
2603
2604#ifndef CONFIG_PM
2605 release_firmware(bootfw);
2606 release_firmware(ucode);
2607 release_firmware(firmware);
2608#endif
2609 return 0;
2610
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002611 error:
James Ketrenos43f66a62005-03-25 12:31:53 -06002612 if (priv->rxq) {
2613 ipw_rx_queue_free(priv, priv->rxq);
2614 priv->rxq = NULL;
2615 }
2616 ipw_tx_queue_free(priv);
2617 if (bootfw)
2618 release_firmware(bootfw);
2619 if (ucode)
2620 release_firmware(ucode);
2621 if (firmware)
2622 release_firmware(firmware);
2623#ifdef CONFIG_PM
2624 fw_loaded = 0;
2625 bootfw = ucode = firmware = NULL;
2626#endif
2627
2628 return rc;
2629}
2630
Jeff Garzikbf794512005-07-31 13:07:26 -04002631/**
James Ketrenos43f66a62005-03-25 12:31:53 -06002632 * DMA services
2633 *
2634 * Theory of operation
2635 *
2636 * A queue is a circular buffers with 'Read' and 'Write' pointers.
2637 * 2 empty entries always kept in the buffer to protect from overflow.
2638 *
2639 * For Tx queue, there are low mark and high mark limits. If, after queuing
Jeff Garzikbf794512005-07-31 13:07:26 -04002640 * the packet for Tx, free space become < low mark, Tx queue stopped. When
2641 * reclaiming packets (on 'tx done IRQ), if free space become > high mark,
James Ketrenos43f66a62005-03-25 12:31:53 -06002642 * Tx queue resumed.
2643 *
2644 * The IPW operates with six queues, one receive queue in the device's
2645 * sram, one transmit queue for sending commands to the device firmware,
Jeff Garzikbf794512005-07-31 13:07:26 -04002646 * and four transmit queues for data.
James Ketrenos43f66a62005-03-25 12:31:53 -06002647 *
Jeff Garzikbf794512005-07-31 13:07:26 -04002648 * The four transmit queues allow for performing quality of service (qos)
James Ketrenos43f66a62005-03-25 12:31:53 -06002649 * transmissions as per the 802.11 protocol. Currently Linux does not
Jeff Garzikbf794512005-07-31 13:07:26 -04002650 * provide a mechanism to the user for utilizing prioritized queues, so
James Ketrenos43f66a62005-03-25 12:31:53 -06002651 * we only utilize the first data transmit queue (queue1).
2652 */
2653
2654/**
2655 * Driver allocates buffers of this size for Rx
2656 */
2657
2658static inline int ipw_queue_space(const struct clx2_queue *q)
2659{
2660 int s = q->last_used - q->first_empty;
2661 if (s <= 0)
2662 s += q->n_bd;
2663 s -= 2; /* keep some reserve to not confuse empty and full situations */
2664 if (s < 0)
2665 s = 0;
2666 return s;
2667}
2668
2669static inline int ipw_queue_inc_wrap(int index, int n_bd)
2670{
2671 return (++index == n_bd) ? 0 : index;
2672}
2673
2674/**
2675 * Initialize common DMA queue structure
Jeff Garzikbf794512005-07-31 13:07:26 -04002676 *
James Ketrenos43f66a62005-03-25 12:31:53 -06002677 * @param q queue to init
2678 * @param count Number of BD's to allocate. Should be power of 2
2679 * @param read_register Address for 'read' register
2680 * (not offset within BAR, full address)
2681 * @param write_register Address for 'write' register
2682 * (not offset within BAR, full address)
2683 * @param base_register Address for 'base' register
2684 * (not offset within BAR, full address)
2685 * @param size Address for 'size' register
2686 * (not offset within BAR, full address)
2687 */
Jeff Garzikbf794512005-07-31 13:07:26 -04002688static void ipw_queue_init(struct ipw_priv *priv, struct clx2_queue *q,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002689 int count, u32 read, u32 write, u32 base, u32 size)
James Ketrenos43f66a62005-03-25 12:31:53 -06002690{
2691 q->n_bd = count;
2692
2693 q->low_mark = q->n_bd / 4;
2694 if (q->low_mark < 4)
2695 q->low_mark = 4;
2696
2697 q->high_mark = q->n_bd / 8;
2698 if (q->high_mark < 2)
2699 q->high_mark = 2;
2700
2701 q->first_empty = q->last_used = 0;
2702 q->reg_r = read;
2703 q->reg_w = write;
2704
2705 ipw_write32(priv, base, q->dma_addr);
2706 ipw_write32(priv, size, count);
2707 ipw_write32(priv, read, 0);
2708 ipw_write32(priv, write, 0);
2709
2710 _ipw_read32(priv, 0x90);
2711}
2712
Jeff Garzikbf794512005-07-31 13:07:26 -04002713static int ipw_queue_tx_init(struct ipw_priv *priv,
James Ketrenos43f66a62005-03-25 12:31:53 -06002714 struct clx2_tx_queue *q,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002715 int count, u32 read, u32 write, u32 base, u32 size)
James Ketrenos43f66a62005-03-25 12:31:53 -06002716{
2717 struct pci_dev *dev = priv->pci_dev;
2718
2719 q->txb = kmalloc(sizeof(q->txb[0]) * count, GFP_KERNEL);
2720 if (!q->txb) {
2721 IPW_ERROR("vmalloc for auxilary BD structures failed\n");
2722 return -ENOMEM;
2723 }
2724
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002725 q->bd =
2726 pci_alloc_consistent(dev, sizeof(q->bd[0]) * count, &q->q.dma_addr);
James Ketrenos43f66a62005-03-25 12:31:53 -06002727 if (!q->bd) {
Jiri Bencaaa4d302005-06-07 14:58:41 +02002728 IPW_ERROR("pci_alloc_consistent(%zd) failed\n",
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002729 sizeof(q->bd[0]) * count);
James Ketrenos43f66a62005-03-25 12:31:53 -06002730 kfree(q->txb);
2731 q->txb = NULL;
2732 return -ENOMEM;
2733 }
2734
2735 ipw_queue_init(priv, &q->q, count, read, write, base, size);
2736 return 0;
2737}
2738
2739/**
2740 * Free one TFD, those at index [txq->q.last_used].
2741 * Do NOT advance any indexes
Jeff Garzikbf794512005-07-31 13:07:26 -04002742 *
James Ketrenos43f66a62005-03-25 12:31:53 -06002743 * @param dev
2744 * @param txq
2745 */
2746static void ipw_queue_tx_free_tfd(struct ipw_priv *priv,
2747 struct clx2_tx_queue *txq)
2748{
2749 struct tfd_frame *bd = &txq->bd[txq->q.last_used];
2750 struct pci_dev *dev = priv->pci_dev;
2751 int i;
Jeff Garzikbf794512005-07-31 13:07:26 -04002752
James Ketrenos43f66a62005-03-25 12:31:53 -06002753 /* classify bd */
2754 if (bd->control_flags.message_type == TX_HOST_COMMAND_TYPE)
2755 /* nothing to cleanup after for host commands */
2756 return;
2757
2758 /* sanity check */
2759 if (bd->u.data.num_chunks > NUM_TFD_CHUNKS) {
2760 IPW_ERROR("Too many chunks: %i\n", bd->u.data.num_chunks);
2761 /** @todo issue fatal error, it is quite serious situation */
2762 return;
2763 }
2764
2765 /* unmap chunks if any */
2766 for (i = 0; i < bd->u.data.num_chunks; i++) {
2767 pci_unmap_single(dev, bd->u.data.chunk_ptr[i],
2768 bd->u.data.chunk_len[i], PCI_DMA_TODEVICE);
2769 if (txq->txb[txq->q.last_used]) {
2770 ieee80211_txb_free(txq->txb[txq->q.last_used]);
2771 txq->txb[txq->q.last_used] = NULL;
2772 }
2773 }
2774}
2775
2776/**
2777 * Deallocate DMA queue.
Jeff Garzikbf794512005-07-31 13:07:26 -04002778 *
James Ketrenos43f66a62005-03-25 12:31:53 -06002779 * Empty queue by removing and destroying all BD's.
2780 * Free all buffers.
Jeff Garzikbf794512005-07-31 13:07:26 -04002781 *
James Ketrenos43f66a62005-03-25 12:31:53 -06002782 * @param dev
2783 * @param q
2784 */
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002785static void ipw_queue_tx_free(struct ipw_priv *priv, struct clx2_tx_queue *txq)
James Ketrenos43f66a62005-03-25 12:31:53 -06002786{
2787 struct clx2_queue *q = &txq->q;
2788 struct pci_dev *dev = priv->pci_dev;
2789
Jeff Garzikbf794512005-07-31 13:07:26 -04002790 if (q->n_bd == 0)
2791 return;
James Ketrenos43f66a62005-03-25 12:31:53 -06002792
2793 /* first, empty all BD's */
2794 for (; q->first_empty != q->last_used;
2795 q->last_used = ipw_queue_inc_wrap(q->last_used, q->n_bd)) {
2796 ipw_queue_tx_free_tfd(priv, txq);
2797 }
Jeff Garzikbf794512005-07-31 13:07:26 -04002798
James Ketrenos43f66a62005-03-25 12:31:53 -06002799 /* free buffers belonging to queue itself */
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002800 pci_free_consistent(dev, sizeof(txq->bd[0]) * q->n_bd, txq->bd,
James Ketrenos43f66a62005-03-25 12:31:53 -06002801 q->dma_addr);
2802 kfree(txq->txb);
2803
2804 /* 0 fill whole structure */
2805 memset(txq, 0, sizeof(*txq));
2806}
2807
James Ketrenos43f66a62005-03-25 12:31:53 -06002808/**
2809 * Destroy all DMA queues and structures
Jeff Garzikbf794512005-07-31 13:07:26 -04002810 *
James Ketrenos43f66a62005-03-25 12:31:53 -06002811 * @param priv
2812 */
2813static void ipw_tx_queue_free(struct ipw_priv *priv)
2814{
2815 /* Tx CMD queue */
2816 ipw_queue_tx_free(priv, &priv->txq_cmd);
2817
2818 /* Tx queues */
2819 ipw_queue_tx_free(priv, &priv->txq[0]);
2820 ipw_queue_tx_free(priv, &priv->txq[1]);
2821 ipw_queue_tx_free(priv, &priv->txq[2]);
2822 ipw_queue_tx_free(priv, &priv->txq[3]);
2823}
2824
2825static void inline __maybe_wake_tx(struct ipw_priv *priv)
2826{
2827 if (netif_running(priv->net_dev)) {
2828 switch (priv->port_type) {
2829 case DCR_TYPE_MU_BSS:
2830 case DCR_TYPE_MU_IBSS:
2831 if (!(priv->status & STATUS_ASSOCIATED)) {
2832 return;
2833 }
2834 }
2835 netif_wake_queue(priv->net_dev);
2836 }
2837
2838}
2839
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002840static inline void ipw_create_bssid(struct ipw_priv *priv, u8 * bssid)
James Ketrenos43f66a62005-03-25 12:31:53 -06002841{
2842 /* First 3 bytes are manufacturer */
2843 bssid[0] = priv->mac_addr[0];
2844 bssid[1] = priv->mac_addr[1];
2845 bssid[2] = priv->mac_addr[2];
2846
2847 /* Last bytes are random */
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002848 get_random_bytes(&bssid[3], ETH_ALEN - 3);
James Ketrenos43f66a62005-03-25 12:31:53 -06002849
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002850 bssid[0] &= 0xfe; /* clear multicast bit */
2851 bssid[0] |= 0x02; /* set local assignment bit (IEEE802) */
James Ketrenos43f66a62005-03-25 12:31:53 -06002852}
2853
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002854static inline u8 ipw_add_station(struct ipw_priv *priv, u8 * bssid)
James Ketrenos43f66a62005-03-25 12:31:53 -06002855{
2856 struct ipw_station_entry entry;
2857 int i;
2858
2859 for (i = 0; i < priv->num_stations; i++) {
2860 if (!memcmp(priv->stations[i], bssid, ETH_ALEN)) {
2861 /* Another node is active in network */
2862 priv->missed_adhoc_beacons = 0;
2863 if (!(priv->config & CFG_STATIC_CHANNEL))
2864 /* when other nodes drop out, we drop out */
2865 priv->config &= ~CFG_ADHOC_PERSIST;
2866
2867 return i;
2868 }
2869 }
2870
2871 if (i == MAX_STATIONS)
2872 return IPW_INVALID_STATION;
2873
2874 IPW_DEBUG_SCAN("Adding AdHoc station: " MAC_FMT "\n", MAC_ARG(bssid));
2875
2876 entry.reserved = 0;
2877 entry.support_mode = 0;
2878 memcpy(entry.mac_addr, bssid, ETH_ALEN);
2879 memcpy(priv->stations[i], bssid, ETH_ALEN);
2880 ipw_write_direct(priv, IPW_STATION_TABLE_LOWER + i * sizeof(entry),
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002881 &entry, sizeof(entry));
James Ketrenos43f66a62005-03-25 12:31:53 -06002882 priv->num_stations++;
2883
2884 return i;
2885}
2886
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002887static inline u8 ipw_find_station(struct ipw_priv *priv, u8 * bssid)
James Ketrenos43f66a62005-03-25 12:31:53 -06002888{
2889 int i;
2890
Jeff Garzikbf794512005-07-31 13:07:26 -04002891 for (i = 0; i < priv->num_stations; i++)
2892 if (!memcmp(priv->stations[i], bssid, ETH_ALEN))
James Ketrenos43f66a62005-03-25 12:31:53 -06002893 return i;
2894
2895 return IPW_INVALID_STATION;
2896}
2897
2898static void ipw_send_disassociate(struct ipw_priv *priv, int quiet)
2899{
2900 int err;
2901
2902 if (!(priv->status & (STATUS_ASSOCIATING | STATUS_ASSOCIATED))) {
2903 IPW_DEBUG_ASSOC("Disassociating while not associated.\n");
2904 return;
2905 }
2906
2907 IPW_DEBUG_ASSOC("Disassocation attempt from " MAC_FMT " "
2908 "on channel %d.\n",
Jeff Garzikbf794512005-07-31 13:07:26 -04002909 MAC_ARG(priv->assoc_request.bssid),
James Ketrenos43f66a62005-03-25 12:31:53 -06002910 priv->assoc_request.channel);
2911
2912 priv->status &= ~(STATUS_ASSOCIATING | STATUS_ASSOCIATED);
2913 priv->status |= STATUS_DISASSOCIATING;
2914
2915 if (quiet)
2916 priv->assoc_request.assoc_type = HC_DISASSOC_QUIET;
2917 else
2918 priv->assoc_request.assoc_type = HC_DISASSOCIATE;
2919 err = ipw_send_associate(priv, &priv->assoc_request);
2920 if (err) {
2921 IPW_DEBUG_HC("Attempt to send [dis]associate command "
2922 "failed.\n");
2923 return;
2924 }
2925
2926}
2927
2928static void ipw_disassociate(void *data)
2929{
2930 ipw_send_disassociate(data, 0);
2931}
2932
2933static void notify_wx_assoc_event(struct ipw_priv *priv)
2934{
2935 union iwreq_data wrqu;
2936 wrqu.ap_addr.sa_family = ARPHRD_ETHER;
2937 if (priv->status & STATUS_ASSOCIATED)
2938 memcpy(wrqu.ap_addr.sa_data, priv->bssid, ETH_ALEN);
2939 else
2940 memset(wrqu.ap_addr.sa_data, 0, ETH_ALEN);
2941 wireless_send_event(priv->net_dev, SIOCGIWAP, &wrqu, NULL);
2942}
2943
2944struct ipw_status_code {
2945 u16 status;
2946 const char *reason;
2947};
2948
2949static const struct ipw_status_code ipw_status_codes[] = {
2950 {0x00, "Successful"},
2951 {0x01, "Unspecified failure"},
2952 {0x0A, "Cannot support all requested capabilities in the "
2953 "Capability information field"},
2954 {0x0B, "Reassociation denied due to inability to confirm that "
2955 "association exists"},
2956 {0x0C, "Association denied due to reason outside the scope of this "
2957 "standard"},
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002958 {0x0D,
2959 "Responding station does not support the specified authentication "
James Ketrenos43f66a62005-03-25 12:31:53 -06002960 "algorithm"},
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002961 {0x0E,
2962 "Received an Authentication frame with authentication sequence "
James Ketrenos43f66a62005-03-25 12:31:53 -06002963 "transaction sequence number out of expected sequence"},
2964 {0x0F, "Authentication rejected because of challenge failure"},
2965 {0x10, "Authentication rejected due to timeout waiting for next "
2966 "frame in sequence"},
2967 {0x11, "Association denied because AP is unable to handle additional "
2968 "associated stations"},
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002969 {0x12,
2970 "Association denied due to requesting station not supporting all "
James Ketrenos43f66a62005-03-25 12:31:53 -06002971 "of the datarates in the BSSBasicServiceSet Parameter"},
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002972 {0x13,
2973 "Association denied due to requesting station not supporting "
James Ketrenos43f66a62005-03-25 12:31:53 -06002974 "short preamble operation"},
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002975 {0x14,
2976 "Association denied due to requesting station not supporting "
James Ketrenos43f66a62005-03-25 12:31:53 -06002977 "PBCC encoding"},
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002978 {0x15,
2979 "Association denied due to requesting station not supporting "
James Ketrenos43f66a62005-03-25 12:31:53 -06002980 "channel agility"},
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002981 {0x19,
2982 "Association denied due to requesting station not supporting "
James Ketrenos43f66a62005-03-25 12:31:53 -06002983 "short slot operation"},
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002984 {0x1A,
2985 "Association denied due to requesting station not supporting "
James Ketrenos43f66a62005-03-25 12:31:53 -06002986 "DSSS-OFDM operation"},
2987 {0x28, "Invalid Information Element"},
2988 {0x29, "Group Cipher is not valid"},
2989 {0x2A, "Pairwise Cipher is not valid"},
2990 {0x2B, "AKMP is not valid"},
2991 {0x2C, "Unsupported RSN IE version"},
2992 {0x2D, "Invalid RSN IE Capabilities"},
2993 {0x2E, "Cipher suite is rejected per security policy"},
2994};
2995
2996#ifdef CONFIG_IPW_DEBUG
Jeff Garzikbf794512005-07-31 13:07:26 -04002997static const char *ipw_get_status_code(u16 status)
James Ketrenos43f66a62005-03-25 12:31:53 -06002998{
2999 int i;
Jeff Garzikbf794512005-07-31 13:07:26 -04003000 for (i = 0; i < ARRAY_SIZE(ipw_status_codes); i++)
James Ketrenos43f66a62005-03-25 12:31:53 -06003001 if (ipw_status_codes[i].status == status)
3002 return ipw_status_codes[i].reason;
3003 return "Unknown status value.";
3004}
3005#endif
3006
3007static void inline average_init(struct average *avg)
3008{
3009 memset(avg, 0, sizeof(*avg));
3010}
3011
3012static void inline average_add(struct average *avg, s16 val)
3013{
3014 avg->sum -= avg->entries[avg->pos];
3015 avg->sum += val;
3016 avg->entries[avg->pos++] = val;
3017 if (unlikely(avg->pos == AVG_ENTRIES)) {
3018 avg->init = 1;
3019 avg->pos = 0;
3020 }
3021}
3022
3023static s16 inline average_value(struct average *avg)
3024{
3025 if (!unlikely(avg->init)) {
3026 if (avg->pos)
3027 return avg->sum / avg->pos;
3028 return 0;
3029 }
3030
3031 return avg->sum / AVG_ENTRIES;
3032}
3033
3034static void ipw_reset_stats(struct ipw_priv *priv)
3035{
3036 u32 len = sizeof(u32);
3037
3038 priv->quality = 0;
3039
3040 average_init(&priv->average_missed_beacons);
3041 average_init(&priv->average_rssi);
3042 average_init(&priv->average_noise);
3043
3044 priv->last_rate = 0;
3045 priv->last_missed_beacons = 0;
3046 priv->last_rx_packets = 0;
3047 priv->last_tx_packets = 0;
3048 priv->last_tx_failures = 0;
Jeff Garzikbf794512005-07-31 13:07:26 -04003049
James Ketrenos43f66a62005-03-25 12:31:53 -06003050 /* Firmware managed, reset only when NIC is restarted, so we have to
3051 * normalize on the current value */
Jeff Garzikbf794512005-07-31 13:07:26 -04003052 ipw_get_ordinal(priv, IPW_ORD_STAT_RX_ERR_CRC,
James Ketrenos43f66a62005-03-25 12:31:53 -06003053 &priv->last_rx_err, &len);
Jeff Garzikbf794512005-07-31 13:07:26 -04003054 ipw_get_ordinal(priv, IPW_ORD_STAT_TX_FAILURE,
James Ketrenos43f66a62005-03-25 12:31:53 -06003055 &priv->last_tx_failures, &len);
3056
3057 /* Driver managed, reset with each association */
3058 priv->missed_adhoc_beacons = 0;
3059 priv->missed_beacons = 0;
3060 priv->tx_packets = 0;
3061 priv->rx_packets = 0;
3062
3063}
3064
James Ketrenos43f66a62005-03-25 12:31:53 -06003065static inline u32 ipw_get_max_rate(struct ipw_priv *priv)
3066{
3067 u32 i = 0x80000000;
3068 u32 mask = priv->rates_mask;
3069 /* If currently associated in B mode, restrict the maximum
3070 * rate match to B rates */
3071 if (priv->assoc_request.ieee_mode == IPW_B_MODE)
3072 mask &= IEEE80211_CCK_RATES_MASK;
3073
3074 /* TODO: Verify that the rate is supported by the current rates
3075 * list. */
3076
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003077 while (i && !(mask & i))
3078 i >>= 1;
James Ketrenos43f66a62005-03-25 12:31:53 -06003079 switch (i) {
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003080 case IEEE80211_CCK_RATE_1MB_MASK: return 1000000;
3081 case IEEE80211_CCK_RATE_2MB_MASK: return 2000000;
3082 case IEEE80211_CCK_RATE_5MB_MASK: return 5500000;
3083 case IEEE80211_OFDM_RATE_6MB_MASK: return 6000000;
3084 case IEEE80211_OFDM_RATE_9MB_MASK: return 9000000;
3085 case IEEE80211_CCK_RATE_11MB_MASK: return 11000000;
3086 case IEEE80211_OFDM_RATE_12MB_MASK: return 12000000;
3087 case IEEE80211_OFDM_RATE_18MB_MASK: return 18000000;
3088 case IEEE80211_OFDM_RATE_24MB_MASK: return 24000000;
3089 case IEEE80211_OFDM_RATE_36MB_MASK: return 36000000;
3090 case IEEE80211_OFDM_RATE_48MB_MASK: return 48000000;
3091 case IEEE80211_OFDM_RATE_54MB_MASK: return 54000000;
James Ketrenos43f66a62005-03-25 12:31:53 -06003092 }
3093
Jeff Garzikbf794512005-07-31 13:07:26 -04003094 if (priv->ieee->mode == IEEE_B)
James Ketrenos43f66a62005-03-25 12:31:53 -06003095 return 11000000;
3096 else
3097 return 54000000;
3098}
3099
3100static u32 ipw_get_current_rate(struct ipw_priv *priv)
3101{
3102 u32 rate, len = sizeof(rate);
3103 int err;
3104
Jeff Garzikbf794512005-07-31 13:07:26 -04003105 if (!(priv->status & STATUS_ASSOCIATED))
James Ketrenos43f66a62005-03-25 12:31:53 -06003106 return 0;
3107
3108 if (priv->tx_packets > IPW_REAL_RATE_RX_PACKET_THRESHOLD) {
Jeff Garzikbf794512005-07-31 13:07:26 -04003109 err = ipw_get_ordinal(priv, IPW_ORD_STAT_TX_CURR_RATE, &rate,
James Ketrenos43f66a62005-03-25 12:31:53 -06003110 &len);
3111 if (err) {
3112 IPW_DEBUG_INFO("failed querying ordinals.\n");
3113 return 0;
3114 }
Jeff Garzikbf794512005-07-31 13:07:26 -04003115 } else
James Ketrenos43f66a62005-03-25 12:31:53 -06003116 return ipw_get_max_rate(priv);
3117
3118 switch (rate) {
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003119 case IPW_TX_RATE_1MB: return 1000000;
3120 case IPW_TX_RATE_2MB: return 2000000;
3121 case IPW_TX_RATE_5MB: return 5500000;
3122 case IPW_TX_RATE_6MB: return 6000000;
3123 case IPW_TX_RATE_9MB: return 9000000;
3124 case IPW_TX_RATE_11MB: return 11000000;
3125 case IPW_TX_RATE_12MB: return 12000000;
3126 case IPW_TX_RATE_18MB: return 18000000;
3127 case IPW_TX_RATE_24MB: return 24000000;
3128 case IPW_TX_RATE_36MB: return 36000000;
3129 case IPW_TX_RATE_48MB: return 48000000;
3130 case IPW_TX_RATE_54MB: return 54000000;
James Ketrenos43f66a62005-03-25 12:31:53 -06003131 }
3132
3133 return 0;
3134}
3135
3136#define PERFECT_RSSI (-50)
3137#define WORST_RSSI (-85)
3138#define IPW_STATS_INTERVAL (2 * HZ)
3139static void ipw_gather_stats(struct ipw_priv *priv)
3140{
3141 u32 rx_err, rx_err_delta, rx_packets_delta;
3142 u32 tx_failures, tx_failures_delta, tx_packets_delta;
3143 u32 missed_beacons_percent, missed_beacons_delta;
3144 u32 quality = 0;
3145 u32 len = sizeof(u32);
3146 s16 rssi;
Jeff Garzikbf794512005-07-31 13:07:26 -04003147 u32 beacon_quality, signal_quality, tx_quality, rx_quality,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003148 rate_quality;
James Ketrenos43f66a62005-03-25 12:31:53 -06003149
3150 if (!(priv->status & STATUS_ASSOCIATED)) {
3151 priv->quality = 0;
3152 return;
3153 }
3154
3155 /* Update the statistics */
Jeff Garzikbf794512005-07-31 13:07:26 -04003156 ipw_get_ordinal(priv, IPW_ORD_STAT_MISSED_BEACONS,
James Ketrenos43f66a62005-03-25 12:31:53 -06003157 &priv->missed_beacons, &len);
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003158 missed_beacons_delta = priv->missed_beacons - priv->last_missed_beacons;
James Ketrenos43f66a62005-03-25 12:31:53 -06003159 priv->last_missed_beacons = priv->missed_beacons;
3160 if (priv->assoc_request.beacon_interval) {
3161 missed_beacons_percent = missed_beacons_delta *
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003162 (HZ * priv->assoc_request.beacon_interval) /
3163 (IPW_STATS_INTERVAL * 10);
James Ketrenos43f66a62005-03-25 12:31:53 -06003164 } else {
3165 missed_beacons_percent = 0;
3166 }
3167 average_add(&priv->average_missed_beacons, missed_beacons_percent);
3168
3169 ipw_get_ordinal(priv, IPW_ORD_STAT_RX_ERR_CRC, &rx_err, &len);
3170 rx_err_delta = rx_err - priv->last_rx_err;
3171 priv->last_rx_err = rx_err;
3172
3173 ipw_get_ordinal(priv, IPW_ORD_STAT_TX_FAILURE, &tx_failures, &len);
3174 tx_failures_delta = tx_failures - priv->last_tx_failures;
3175 priv->last_tx_failures = tx_failures;
3176
3177 rx_packets_delta = priv->rx_packets - priv->last_rx_packets;
3178 priv->last_rx_packets = priv->rx_packets;
3179
3180 tx_packets_delta = priv->tx_packets - priv->last_tx_packets;
3181 priv->last_tx_packets = priv->tx_packets;
3182
3183 /* Calculate quality based on the following:
Jeff Garzikbf794512005-07-31 13:07:26 -04003184 *
James Ketrenos43f66a62005-03-25 12:31:53 -06003185 * Missed beacon: 100% = 0, 0% = 70% missed
3186 * Rate: 60% = 1Mbs, 100% = Max
3187 * Rx and Tx errors represent a straight % of total Rx/Tx
3188 * RSSI: 100% = > -50, 0% = < -80
3189 * Rx errors: 100% = 0, 0% = 50% missed
Jeff Garzikbf794512005-07-31 13:07:26 -04003190 *
James Ketrenos43f66a62005-03-25 12:31:53 -06003191 * The lowest computed quality is used.
3192 *
3193 */
3194#define BEACON_THRESHOLD 5
3195 beacon_quality = 100 - missed_beacons_percent;
3196 if (beacon_quality < BEACON_THRESHOLD)
3197 beacon_quality = 0;
3198 else
Jeff Garzikbf794512005-07-31 13:07:26 -04003199 beacon_quality = (beacon_quality - BEACON_THRESHOLD) * 100 /
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003200 (100 - BEACON_THRESHOLD);
Jeff Garzikbf794512005-07-31 13:07:26 -04003201 IPW_DEBUG_STATS("Missed beacon: %3d%% (%d%%)\n",
James Ketrenos43f66a62005-03-25 12:31:53 -06003202 beacon_quality, missed_beacons_percent);
Jeff Garzikbf794512005-07-31 13:07:26 -04003203
James Ketrenos43f66a62005-03-25 12:31:53 -06003204 priv->last_rate = ipw_get_current_rate(priv);
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003205 rate_quality = priv->last_rate * 40 / priv->last_rate + 60;
James Ketrenos43f66a62005-03-25 12:31:53 -06003206 IPW_DEBUG_STATS("Rate quality : %3d%% (%dMbs)\n",
3207 rate_quality, priv->last_rate / 1000000);
Jeff Garzikbf794512005-07-31 13:07:26 -04003208
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003209 if (rx_packets_delta > 100 && rx_packets_delta + rx_err_delta)
Jeff Garzikbf794512005-07-31 13:07:26 -04003210 rx_quality = 100 - (rx_err_delta * 100) /
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003211 (rx_packets_delta + rx_err_delta);
James Ketrenos43f66a62005-03-25 12:31:53 -06003212 else
3213 rx_quality = 100;
3214 IPW_DEBUG_STATS("Rx quality : %3d%% (%u errors, %u packets)\n",
3215 rx_quality, rx_err_delta, rx_packets_delta);
Jeff Garzikbf794512005-07-31 13:07:26 -04003216
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003217 if (tx_packets_delta > 100 && tx_packets_delta + tx_failures_delta)
Jeff Garzikbf794512005-07-31 13:07:26 -04003218 tx_quality = 100 - (tx_failures_delta * 100) /
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003219 (tx_packets_delta + tx_failures_delta);
James Ketrenos43f66a62005-03-25 12:31:53 -06003220 else
3221 tx_quality = 100;
3222 IPW_DEBUG_STATS("Tx quality : %3d%% (%u errors, %u packets)\n",
3223 tx_quality, tx_failures_delta, tx_packets_delta);
Jeff Garzikbf794512005-07-31 13:07:26 -04003224
James Ketrenos43f66a62005-03-25 12:31:53 -06003225 rssi = average_value(&priv->average_rssi);
3226 if (rssi > PERFECT_RSSI)
3227 signal_quality = 100;
3228 else if (rssi < WORST_RSSI)
3229 signal_quality = 0;
3230 else
Jeff Garzikbf794512005-07-31 13:07:26 -04003231 signal_quality = (rssi - WORST_RSSI) * 100 /
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003232 (PERFECT_RSSI - WORST_RSSI);
James Ketrenos43f66a62005-03-25 12:31:53 -06003233 IPW_DEBUG_STATS("Signal level : %3d%% (%d dBm)\n",
3234 signal_quality, rssi);
Jeff Garzikbf794512005-07-31 13:07:26 -04003235
3236 quality = min(beacon_quality,
James Ketrenos43f66a62005-03-25 12:31:53 -06003237 min(rate_quality,
3238 min(tx_quality, min(rx_quality, signal_quality))));
3239 if (quality == beacon_quality)
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003240 IPW_DEBUG_STATS("Quality (%d%%): Clamped to missed beacons.\n",
3241 quality);
James Ketrenos43f66a62005-03-25 12:31:53 -06003242 if (quality == rate_quality)
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003243 IPW_DEBUG_STATS("Quality (%d%%): Clamped to rate quality.\n",
3244 quality);
James Ketrenos43f66a62005-03-25 12:31:53 -06003245 if (quality == tx_quality)
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003246 IPW_DEBUG_STATS("Quality (%d%%): Clamped to Tx quality.\n",
3247 quality);
James Ketrenos43f66a62005-03-25 12:31:53 -06003248 if (quality == rx_quality)
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003249 IPW_DEBUG_STATS("Quality (%d%%): Clamped to Rx quality.\n",
3250 quality);
James Ketrenos43f66a62005-03-25 12:31:53 -06003251 if (quality == signal_quality)
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003252 IPW_DEBUG_STATS("Quality (%d%%): Clamped to signal quality.\n",
3253 quality);
James Ketrenos43f66a62005-03-25 12:31:53 -06003254
3255 priv->quality = quality;
Jeff Garzikbf794512005-07-31 13:07:26 -04003256
3257 queue_delayed_work(priv->workqueue, &priv->gather_stats,
James Ketrenos43f66a62005-03-25 12:31:53 -06003258 IPW_STATS_INTERVAL);
3259}
3260
3261/**
3262 * Handle host notification packet.
3263 * Called from interrupt routine
3264 */
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003265static inline void ipw_rx_notification(struct ipw_priv *priv,
James Ketrenos43f66a62005-03-25 12:31:53 -06003266 struct ipw_rx_notification *notif)
3267{
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003268 IPW_DEBUG_NOTIF("type = %i (%d bytes)\n", notif->subtype, notif->size);
Jeff Garzikbf794512005-07-31 13:07:26 -04003269
James Ketrenos43f66a62005-03-25 12:31:53 -06003270 switch (notif->subtype) {
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003271 case HOST_NOTIFICATION_STATUS_ASSOCIATED:{
3272 struct notif_association *assoc = &notif->u.assoc;
Jeff Garzikbf794512005-07-31 13:07:26 -04003273
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003274 switch (assoc->state) {
3275 case CMAS_ASSOCIATED:{
3276 IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE |
3277 IPW_DL_ASSOC,
3278 "associated: '%s' " MAC_FMT
3279 " \n",
3280 escape_essid(priv->essid,
3281 priv->essid_len),
3282 MAC_ARG(priv->bssid));
Jeff Garzikbf794512005-07-31 13:07:26 -04003283
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003284 switch (priv->ieee->iw_mode) {
3285 case IW_MODE_INFRA:
3286 memcpy(priv->ieee->bssid,
3287 priv->bssid, ETH_ALEN);
3288 break;
James Ketrenos43f66a62005-03-25 12:31:53 -06003289
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003290 case IW_MODE_ADHOC:
3291 memcpy(priv->ieee->bssid,
3292 priv->bssid, ETH_ALEN);
Jeff Garzikbf794512005-07-31 13:07:26 -04003293
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003294 /* clear out the station table */
3295 priv->num_stations = 0;
James Ketrenos43f66a62005-03-25 12:31:53 -06003296
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003297 IPW_DEBUG_ASSOC
3298 ("queueing adhoc check\n");
3299 queue_delayed_work(priv->
3300 workqueue,
3301 &priv->
3302 adhoc_check,
3303 priv->
3304 assoc_request.
3305 beacon_interval);
3306 break;
3307 }
James Ketrenos43f66a62005-03-25 12:31:53 -06003308
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003309 priv->status &= ~STATUS_ASSOCIATING;
3310 priv->status |= STATUS_ASSOCIATED;
James Ketrenos43f66a62005-03-25 12:31:53 -06003311
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003312 netif_carrier_on(priv->net_dev);
3313 if (netif_queue_stopped(priv->net_dev)) {
3314 IPW_DEBUG_NOTIF
3315 ("waking queue\n");
3316 netif_wake_queue(priv->net_dev);
3317 } else {
3318 IPW_DEBUG_NOTIF
3319 ("starting queue\n");
3320 netif_start_queue(priv->
3321 net_dev);
3322 }
James Ketrenos43f66a62005-03-25 12:31:53 -06003323
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003324 ipw_reset_stats(priv);
3325 /* Ensure the rate is updated immediately */
3326 priv->last_rate =
3327 ipw_get_current_rate(priv);
3328 schedule_work(&priv->gather_stats);
3329 notify_wx_assoc_event(priv);
James Ketrenos43f66a62005-03-25 12:31:53 -06003330
Jeff Garzikbf794512005-07-31 13:07:26 -04003331/* queue_delayed_work(priv->workqueue,
James Ketrenos43f66a62005-03-25 12:31:53 -06003332 &priv->request_scan,
3333 SCAN_ASSOCIATED_INTERVAL);
3334*/
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003335 break;
3336 }
3337
3338 case CMAS_AUTHENTICATED:{
3339 if (priv->
3340 status & (STATUS_ASSOCIATED |
3341 STATUS_AUTH)) {
3342#ifdef CONFIG_IPW_DEBUG
3343 struct notif_authenticate *auth
3344 = &notif->u.auth;
3345 IPW_DEBUG(IPW_DL_NOTIF |
3346 IPW_DL_STATE |
3347 IPW_DL_ASSOC,
3348 "deauthenticated: '%s' "
3349 MAC_FMT
3350 ": (0x%04X) - %s \n",
3351 escape_essid(priv->
3352 essid,
3353 priv->
3354 essid_len),
3355 MAC_ARG(priv->bssid),
3356 ntohs(auth->status),
3357 ipw_get_status_code
3358 (ntohs
3359 (auth->status)));
3360#endif
3361
3362 priv->status &=
3363 ~(STATUS_ASSOCIATING |
3364 STATUS_AUTH |
3365 STATUS_ASSOCIATED);
3366
3367 netif_carrier_off(priv->
3368 net_dev);
3369 netif_stop_queue(priv->net_dev);
3370 queue_work(priv->workqueue,
3371 &priv->request_scan);
3372 notify_wx_assoc_event(priv);
3373 break;
3374 }
3375
3376 IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE |
3377 IPW_DL_ASSOC,
3378 "authenticated: '%s' " MAC_FMT
3379 "\n",
3380 escape_essid(priv->essid,
3381 priv->essid_len),
3382 MAC_ARG(priv->bssid));
3383 break;
3384 }
3385
3386 case CMAS_INIT:{
3387 IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE |
3388 IPW_DL_ASSOC,
3389 "disassociated: '%s' " MAC_FMT
3390 " \n",
3391 escape_essid(priv->essid,
3392 priv->essid_len),
3393 MAC_ARG(priv->bssid));
3394
3395 priv->status &=
3396 ~(STATUS_DISASSOCIATING |
3397 STATUS_ASSOCIATING |
3398 STATUS_ASSOCIATED | STATUS_AUTH);
3399
3400 netif_stop_queue(priv->net_dev);
3401 if (!(priv->status & STATUS_ROAMING)) {
3402 netif_carrier_off(priv->
3403 net_dev);
3404 notify_wx_assoc_event(priv);
3405
3406 /* Cancel any queued work ... */
3407 cancel_delayed_work(&priv->
3408 request_scan);
3409 cancel_delayed_work(&priv->
3410 adhoc_check);
3411
3412 /* Queue up another scan... */
3413 queue_work(priv->workqueue,
3414 &priv->request_scan);
3415
3416 cancel_delayed_work(&priv->
3417 gather_stats);
3418 } else {
3419 priv->status |= STATUS_ROAMING;
3420 queue_work(priv->workqueue,
3421 &priv->request_scan);
3422 }
3423
3424 ipw_reset_stats(priv);
3425 break;
3426 }
3427
3428 default:
3429 IPW_ERROR("assoc: unknown (%d)\n",
3430 assoc->state);
3431 break;
3432 }
3433
James Ketrenos43f66a62005-03-25 12:31:53 -06003434 break;
3435 }
Jeff Garzikbf794512005-07-31 13:07:26 -04003436
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003437 case HOST_NOTIFICATION_STATUS_AUTHENTICATE:{
3438 struct notif_authenticate *auth = &notif->u.auth;
3439 switch (auth->state) {
3440 case CMAS_AUTHENTICATED:
3441 IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE,
3442 "authenticated: '%s' " MAC_FMT " \n",
3443 escape_essid(priv->essid,
3444 priv->essid_len),
3445 MAC_ARG(priv->bssid));
3446 priv->status |= STATUS_AUTH;
3447 break;
3448
3449 case CMAS_INIT:
3450 if (priv->status & STATUS_AUTH) {
3451 IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE |
3452 IPW_DL_ASSOC,
3453 "authentication failed (0x%04X): %s\n",
3454 ntohs(auth->status),
3455 ipw_get_status_code(ntohs
3456 (auth->
3457 status)));
3458 }
3459 IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE |
3460 IPW_DL_ASSOC,
3461 "deauthenticated: '%s' " MAC_FMT "\n",
3462 escape_essid(priv->essid,
3463 priv->essid_len),
3464 MAC_ARG(priv->bssid));
James Ketrenos43f66a62005-03-25 12:31:53 -06003465
3466 priv->status &= ~(STATUS_ASSOCIATING |
3467 STATUS_AUTH |
3468 STATUS_ASSOCIATED);
3469
3470 netif_carrier_off(priv->net_dev);
3471 netif_stop_queue(priv->net_dev);
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003472 queue_work(priv->workqueue,
3473 &priv->request_scan);
Jeff Garzikbf794512005-07-31 13:07:26 -04003474 notify_wx_assoc_event(priv);
James Ketrenos43f66a62005-03-25 12:31:53 -06003475 break;
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003476
3477 case CMAS_TX_AUTH_SEQ_1:
3478 IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE |
3479 IPW_DL_ASSOC, "AUTH_SEQ_1\n");
3480 break;
3481 case CMAS_RX_AUTH_SEQ_2:
3482 IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE |
3483 IPW_DL_ASSOC, "AUTH_SEQ_2\n");
3484 break;
3485 case CMAS_AUTH_SEQ_1_PASS:
3486 IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE |
3487 IPW_DL_ASSOC, "AUTH_SEQ_1_PASS\n");
3488 break;
3489 case CMAS_AUTH_SEQ_1_FAIL:
3490 IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE |
3491 IPW_DL_ASSOC, "AUTH_SEQ_1_FAIL\n");
3492 break;
3493 case CMAS_TX_AUTH_SEQ_3:
3494 IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE |
3495 IPW_DL_ASSOC, "AUTH_SEQ_3\n");
3496 break;
3497 case CMAS_RX_AUTH_SEQ_4:
3498 IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE |
3499 IPW_DL_ASSOC, "RX_AUTH_SEQ_4\n");
3500 break;
3501 case CMAS_AUTH_SEQ_2_PASS:
3502 IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE |
3503 IPW_DL_ASSOC, "AUTH_SEQ_2_PASS\n");
3504 break;
3505 case CMAS_AUTH_SEQ_2_FAIL:
3506 IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE |
3507 IPW_DL_ASSOC, "AUT_SEQ_2_FAIL\n");
3508 break;
3509 case CMAS_TX_ASSOC:
3510 IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE |
3511 IPW_DL_ASSOC, "TX_ASSOC\n");
3512 break;
3513 case CMAS_RX_ASSOC_RESP:
3514 IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE |
3515 IPW_DL_ASSOC, "RX_ASSOC_RESP\n");
3516 break;
3517 case CMAS_ASSOCIATED:
3518 IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE |
3519 IPW_DL_ASSOC, "ASSOCIATED\n");
3520 break;
3521 default:
3522 IPW_DEBUG_NOTIF("auth: failure - %d\n",
3523 auth->state);
3524 break;
3525 }
3526 break;
3527 }
3528
3529 case HOST_NOTIFICATION_STATUS_SCAN_CHANNEL_RESULT:{
3530 struct notif_channel_result *x =
3531 &notif->u.channel_result;
3532
3533 if (notif->size == sizeof(*x)) {
3534 IPW_DEBUG_SCAN("Scan result for channel %d\n",
3535 x->channel_num);
3536 } else {
3537 IPW_DEBUG_SCAN("Scan result of wrong size %d "
3538 "(should be %zd)\n",
3539 notif->size, sizeof(*x));
3540 }
3541 break;
3542 }
3543
3544 case HOST_NOTIFICATION_STATUS_SCAN_COMPLETED:{
3545 struct notif_scan_complete *x = &notif->u.scan_complete;
3546 if (notif->size == sizeof(*x)) {
3547 IPW_DEBUG_SCAN
3548 ("Scan completed: type %d, %d channels, "
3549 "%d status\n", x->scan_type,
3550 x->num_channels, x->status);
3551 } else {
3552 IPW_ERROR("Scan completed of wrong size %d "
3553 "(should be %zd)\n",
3554 notif->size, sizeof(*x));
3555 }
3556
3557 priv->status &=
3558 ~(STATUS_SCANNING | STATUS_SCAN_ABORTING);
3559
3560 cancel_delayed_work(&priv->scan_check);
3561
3562 if (!(priv->status & (STATUS_ASSOCIATED |
3563 STATUS_ASSOCIATING |
3564 STATUS_ROAMING |
3565 STATUS_DISASSOCIATING)))
3566 queue_work(priv->workqueue, &priv->associate);
3567 else if (priv->status & STATUS_ROAMING) {
3568 /* If a scan completed and we are in roam mode, then
3569 * the scan that completed was the one requested as a
3570 * result of entering roam... so, schedule the
3571 * roam work */
3572 queue_work(priv->workqueue, &priv->roam);
3573 } else if (priv->status & STATUS_SCAN_PENDING)
3574 queue_work(priv->workqueue,
3575 &priv->request_scan);
3576
3577 priv->ieee->scans++;
3578 break;
3579 }
3580
3581 case HOST_NOTIFICATION_STATUS_FRAG_LENGTH:{
3582 struct notif_frag_length *x = &notif->u.frag_len;
3583
3584 if (notif->size == sizeof(*x)) {
3585 IPW_ERROR("Frag length: %d\n", x->frag_length);
3586 } else {
3587 IPW_ERROR("Frag length of wrong size %d "
3588 "(should be %zd)\n",
3589 notif->size, sizeof(*x));
3590 }
3591 break;
3592 }
3593
3594 case HOST_NOTIFICATION_STATUS_LINK_DETERIORATION:{
3595 struct notif_link_deterioration *x =
3596 &notif->u.link_deterioration;
3597 if (notif->size == sizeof(*x)) {
3598 IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE,
3599 "link deterioration: '%s' " MAC_FMT
3600 " \n", escape_essid(priv->essid,
3601 priv->essid_len),
3602 MAC_ARG(priv->bssid));
3603 memcpy(&priv->last_link_deterioration, x,
3604 sizeof(*x));
3605 } else {
3606 IPW_ERROR("Link Deterioration of wrong size %d "
3607 "(should be %zd)\n",
3608 notif->size, sizeof(*x));
3609 }
3610 break;
3611 }
3612
3613 case HOST_NOTIFICATION_DINO_CONFIG_RESPONSE:{
3614 IPW_ERROR("Dino config\n");
3615 if (priv->hcmd
3616 && priv->hcmd->cmd == HOST_CMD_DINO_CONFIG) {
3617 /* TODO: Do anything special? */
3618 } else {
3619 IPW_ERROR("Unexpected DINO_CONFIG_RESPONSE\n");
3620 }
3621 break;
3622 }
3623
3624 case HOST_NOTIFICATION_STATUS_BEACON_STATE:{
3625 struct notif_beacon_state *x = &notif->u.beacon_state;
3626 if (notif->size != sizeof(*x)) {
3627 IPW_ERROR
3628 ("Beacon state of wrong size %d (should "
3629 "be %zd)\n", notif->size, sizeof(*x));
3630 break;
Jeff Garzikbf794512005-07-31 13:07:26 -04003631 }
James Ketrenos43f66a62005-03-25 12:31:53 -06003632
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003633 if (x->state == HOST_NOTIFICATION_STATUS_BEACON_MISSING) {
3634 if (priv->status & STATUS_SCANNING) {
3635 /* Stop scan to keep fw from getting
3636 * stuck... */
3637 queue_work(priv->workqueue,
3638 &priv->abort_scan);
3639 }
Jeff Garzikbf794512005-07-31 13:07:26 -04003640
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003641 if (x->number > priv->missed_beacon_threshold &&
3642 priv->status & STATUS_ASSOCIATED) {
3643 IPW_DEBUG(IPW_DL_INFO | IPW_DL_NOTIF |
3644 IPW_DL_STATE,
3645 "Missed beacon: %d - disassociate\n",
3646 x->number);
3647 queue_work(priv->workqueue,
3648 &priv->disassociate);
3649 } else if (x->number > priv->roaming_threshold) {
3650 IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE,
3651 "Missed beacon: %d - initiate "
3652 "roaming\n", x->number);
3653 queue_work(priv->workqueue,
3654 &priv->roam);
3655 } else {
3656 IPW_DEBUG_NOTIF("Missed beacon: %d\n",
3657 x->number);
3658 }
James Ketrenos43f66a62005-03-25 12:31:53 -06003659
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003660 priv->notif_missed_beacons = x->number;
Jeff Garzikbf794512005-07-31 13:07:26 -04003661
James Ketrenos43f66a62005-03-25 12:31:53 -06003662 }
Jeff Garzikbf794512005-07-31 13:07:26 -04003663
James Ketrenos43f66a62005-03-25 12:31:53 -06003664 break;
3665 }
Jeff Garzikbf794512005-07-31 13:07:26 -04003666
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003667 case HOST_NOTIFICATION_STATUS_TGI_TX_KEY:{
3668 struct notif_tgi_tx_key *x = &notif->u.tgi_tx_key;
3669 if (notif->size == sizeof(*x)) {
3670 IPW_ERROR("TGi Tx Key: state 0x%02x sec type "
3671 "0x%02x station %d\n",
3672 x->key_state, x->security_type,
3673 x->station_index);
3674 break;
James Ketrenos43f66a62005-03-25 12:31:53 -06003675 }
3676
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003677 IPW_ERROR
3678 ("TGi Tx Key of wrong size %d (should be %zd)\n",
3679 notif->size, sizeof(*x));
3680 break;
3681 }
3682
3683 case HOST_NOTIFICATION_CALIB_KEEP_RESULTS:{
3684 struct notif_calibration *x = &notif->u.calibration;
3685
3686 if (notif->size == sizeof(*x)) {
3687 memcpy(&priv->calib, x, sizeof(*x));
3688 IPW_DEBUG_INFO("TODO: Calibration\n");
3689 break;
James Ketrenos43f66a62005-03-25 12:31:53 -06003690 }
3691
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003692 IPW_ERROR
3693 ("Calibration of wrong size %d (should be %zd)\n",
3694 notif->size, sizeof(*x));
James Ketrenos43f66a62005-03-25 12:31:53 -06003695 break;
Jeff Garzikbf794512005-07-31 13:07:26 -04003696 }
James Ketrenos43f66a62005-03-25 12:31:53 -06003697
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003698 case HOST_NOTIFICATION_NOISE_STATS:{
3699 if (notif->size == sizeof(u32)) {
3700 priv->last_noise =
3701 (u8) (notif->u.noise.value & 0xff);
3702 average_add(&priv->average_noise,
3703 priv->last_noise);
3704 break;
3705 }
James Ketrenos43f66a62005-03-25 12:31:53 -06003706
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003707 IPW_ERROR
3708 ("Noise stat is wrong size %d (should be %zd)\n",
3709 notif->size, sizeof(u32));
James Ketrenos43f66a62005-03-25 12:31:53 -06003710 break;
Jeff Garzikbf794512005-07-31 13:07:26 -04003711 }
3712
James Ketrenos43f66a62005-03-25 12:31:53 -06003713 default:
3714 IPW_ERROR("Unknown notification: "
3715 "subtype=%d,flags=0x%2x,size=%d\n",
3716 notif->subtype, notif->flags, notif->size);
3717 }
3718}
3719
3720/**
3721 * Destroys all DMA structures and initialise them again
Jeff Garzikbf794512005-07-31 13:07:26 -04003722 *
James Ketrenos43f66a62005-03-25 12:31:53 -06003723 * @param priv
3724 * @return error code
3725 */
3726static int ipw_queue_reset(struct ipw_priv *priv)
3727{
3728 int rc = 0;
3729 /** @todo customize queue sizes */
3730 int nTx = 64, nTxCmd = 8;
3731 ipw_tx_queue_free(priv);
3732 /* Tx CMD queue */
3733 rc = ipw_queue_tx_init(priv, &priv->txq_cmd, nTxCmd,
3734 CX2_TX_CMD_QUEUE_READ_INDEX,
3735 CX2_TX_CMD_QUEUE_WRITE_INDEX,
3736 CX2_TX_CMD_QUEUE_BD_BASE,
3737 CX2_TX_CMD_QUEUE_BD_SIZE);
3738 if (rc) {
3739 IPW_ERROR("Tx Cmd queue init failed\n");
3740 goto error;
3741 }
3742 /* Tx queue(s) */
3743 rc = ipw_queue_tx_init(priv, &priv->txq[0], nTx,
3744 CX2_TX_QUEUE_0_READ_INDEX,
3745 CX2_TX_QUEUE_0_WRITE_INDEX,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003746 CX2_TX_QUEUE_0_BD_BASE, CX2_TX_QUEUE_0_BD_SIZE);
James Ketrenos43f66a62005-03-25 12:31:53 -06003747 if (rc) {
3748 IPW_ERROR("Tx 0 queue init failed\n");
3749 goto error;
3750 }
3751 rc = ipw_queue_tx_init(priv, &priv->txq[1], nTx,
3752 CX2_TX_QUEUE_1_READ_INDEX,
3753 CX2_TX_QUEUE_1_WRITE_INDEX,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003754 CX2_TX_QUEUE_1_BD_BASE, CX2_TX_QUEUE_1_BD_SIZE);
James Ketrenos43f66a62005-03-25 12:31:53 -06003755 if (rc) {
3756 IPW_ERROR("Tx 1 queue init failed\n");
3757 goto error;
3758 }
3759 rc = ipw_queue_tx_init(priv, &priv->txq[2], nTx,
3760 CX2_TX_QUEUE_2_READ_INDEX,
3761 CX2_TX_QUEUE_2_WRITE_INDEX,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003762 CX2_TX_QUEUE_2_BD_BASE, CX2_TX_QUEUE_2_BD_SIZE);
James Ketrenos43f66a62005-03-25 12:31:53 -06003763 if (rc) {
3764 IPW_ERROR("Tx 2 queue init failed\n");
3765 goto error;
3766 }
3767 rc = ipw_queue_tx_init(priv, &priv->txq[3], nTx,
3768 CX2_TX_QUEUE_3_READ_INDEX,
3769 CX2_TX_QUEUE_3_WRITE_INDEX,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003770 CX2_TX_QUEUE_3_BD_BASE, CX2_TX_QUEUE_3_BD_SIZE);
James Ketrenos43f66a62005-03-25 12:31:53 -06003771 if (rc) {
3772 IPW_ERROR("Tx 3 queue init failed\n");
3773 goto error;
3774 }
3775 /* statistics */
3776 priv->rx_bufs_min = 0;
3777 priv->rx_pend_max = 0;
3778 return rc;
3779
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003780 error:
James Ketrenos43f66a62005-03-25 12:31:53 -06003781 ipw_tx_queue_free(priv);
3782 return rc;
3783}
3784
3785/**
3786 * Reclaim Tx queue entries no more used by NIC.
Jeff Garzikbf794512005-07-31 13:07:26 -04003787 *
James Ketrenos43f66a62005-03-25 12:31:53 -06003788 * When FW adwances 'R' index, all entries between old and
3789 * new 'R' index need to be reclaimed. As result, some free space
3790 * forms. If there is enough free space (> low mark), wake Tx queue.
Jeff Garzikbf794512005-07-31 13:07:26 -04003791 *
James Ketrenos43f66a62005-03-25 12:31:53 -06003792 * @note Need to protect against garbage in 'R' index
3793 * @param priv
3794 * @param txq
3795 * @param qindex
3796 * @return Number of used entries remains in the queue
3797 */
Jeff Garzikbf794512005-07-31 13:07:26 -04003798static int ipw_queue_tx_reclaim(struct ipw_priv *priv,
James Ketrenos43f66a62005-03-25 12:31:53 -06003799 struct clx2_tx_queue *txq, int qindex)
3800{
3801 u32 hw_tail;
3802 int used;
3803 struct clx2_queue *q = &txq->q;
3804
3805 hw_tail = ipw_read32(priv, q->reg_r);
3806 if (hw_tail >= q->n_bd) {
3807 IPW_ERROR
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003808 ("Read index for DMA queue (%d) is out of range [0-%d)\n",
3809 hw_tail, q->n_bd);
James Ketrenos43f66a62005-03-25 12:31:53 -06003810 goto done;
3811 }
3812 for (; q->last_used != hw_tail;
3813 q->last_used = ipw_queue_inc_wrap(q->last_used, q->n_bd)) {
3814 ipw_queue_tx_free_tfd(priv, txq);
3815 priv->tx_packets++;
3816 }
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003817 done:
James Ketrenos43f66a62005-03-25 12:31:53 -06003818 if (ipw_queue_space(q) > q->low_mark && qindex >= 0) {
3819 __maybe_wake_tx(priv);
3820 }
3821 used = q->first_empty - q->last_used;
3822 if (used < 0)
3823 used += q->n_bd;
3824
3825 return used;
3826}
3827
3828static int ipw_queue_tx_hcmd(struct ipw_priv *priv, int hcmd, void *buf,
3829 int len, int sync)
3830{
3831 struct clx2_tx_queue *txq = &priv->txq_cmd;
3832 struct clx2_queue *q = &txq->q;
3833 struct tfd_frame *tfd;
3834
3835 if (ipw_queue_space(q) < (sync ? 1 : 2)) {
3836 IPW_ERROR("No space for Tx\n");
3837 return -EBUSY;
3838 }
3839
3840 tfd = &txq->bd[q->first_empty];
3841 txq->txb[q->first_empty] = NULL;
3842
3843 memset(tfd, 0, sizeof(*tfd));
3844 tfd->control_flags.message_type = TX_HOST_COMMAND_TYPE;
3845 tfd->control_flags.control_bits = TFD_NEED_IRQ_MASK;
3846 priv->hcmd_seq++;
3847 tfd->u.cmd.index = hcmd;
3848 tfd->u.cmd.length = len;
3849 memcpy(tfd->u.cmd.payload, buf, len);
3850 q->first_empty = ipw_queue_inc_wrap(q->first_empty, q->n_bd);
3851 ipw_write32(priv, q->reg_w, q->first_empty);
3852 _ipw_read32(priv, 0x90);
3853
3854 return 0;
3855}
3856
Jeff Garzikbf794512005-07-31 13:07:26 -04003857/*
James Ketrenos43f66a62005-03-25 12:31:53 -06003858 * Rx theory of operation
3859 *
3860 * The host allocates 32 DMA target addresses and passes the host address
3861 * to the firmware at register CX2_RFDS_TABLE_LOWER + N * RFD_SIZE where N is
3862 * 0 to 31
3863 *
3864 * Rx Queue Indexes
3865 * The host/firmware share two index registers for managing the Rx buffers.
3866 *
Jeff Garzikbf794512005-07-31 13:07:26 -04003867 * The READ index maps to the first position that the firmware may be writing
3868 * to -- the driver can read up to (but not including) this position and get
3869 * good data.
James Ketrenos43f66a62005-03-25 12:31:53 -06003870 * The READ index is managed by the firmware once the card is enabled.
3871 *
3872 * The WRITE index maps to the last position the driver has read from -- the
3873 * position preceding WRITE is the last slot the firmware can place a packet.
3874 *
3875 * The queue is empty (no good data) if WRITE = READ - 1, and is full if
Jeff Garzikbf794512005-07-31 13:07:26 -04003876 * WRITE = READ.
James Ketrenos43f66a62005-03-25 12:31:53 -06003877 *
Jeff Garzikbf794512005-07-31 13:07:26 -04003878 * During initialization the host sets up the READ queue position to the first
James Ketrenos43f66a62005-03-25 12:31:53 -06003879 * INDEX position, and WRITE to the last (READ - 1 wrapped)
3880 *
3881 * When the firmware places a packet in a buffer it will advance the READ index
3882 * and fire the RX interrupt. The driver can then query the READ index and
3883 * process as many packets as possible, moving the WRITE index forward as it
3884 * resets the Rx queue buffers with new memory.
Jeff Garzikbf794512005-07-31 13:07:26 -04003885 *
James Ketrenos43f66a62005-03-25 12:31:53 -06003886 * The management in the driver is as follows:
Jeff Garzikbf794512005-07-31 13:07:26 -04003887 * + A list of pre-allocated SKBs is stored in ipw->rxq->rx_free. When
James Ketrenos43f66a62005-03-25 12:31:53 -06003888 * ipw->rxq->free_count drops to or below RX_LOW_WATERMARK, work is scheduled
Jeff Garzikbf794512005-07-31 13:07:26 -04003889 * to replensish the ipw->rxq->rx_free.
James Ketrenos43f66a62005-03-25 12:31:53 -06003890 * + In ipw_rx_queue_replenish (scheduled) if 'processed' != 'read' then the
3891 * ipw->rxq is replenished and the READ INDEX is updated (updating the
3892 * 'processed' and 'read' driver indexes as well)
3893 * + A received packet is processed and handed to the kernel network stack,
3894 * detached from the ipw->rxq. The driver 'processed' index is updated.
3895 * + The Host/Firmware ipw->rxq is replenished at tasklet time from the rx_free
Jeff Garzikbf794512005-07-31 13:07:26 -04003896 * list. If there are no allocated buffers in ipw->rxq->rx_free, the READ
3897 * INDEX is not incremented and ipw->status(RX_STALLED) is set. If there
James Ketrenos43f66a62005-03-25 12:31:53 -06003898 * were enough free buffers and RX_STALLED is set it is cleared.
3899 *
3900 *
3901 * Driver sequence:
3902 *
Jeff Garzikbf794512005-07-31 13:07:26 -04003903 * ipw_rx_queue_alloc() Allocates rx_free
James Ketrenos43f66a62005-03-25 12:31:53 -06003904 * ipw_rx_queue_replenish() Replenishes rx_free list from rx_used, and calls
3905 * ipw_rx_queue_restock
3906 * ipw_rx_queue_restock() Moves available buffers from rx_free into Rx
3907 * queue, updates firmware pointers, and updates
3908 * the WRITE index. If insufficient rx_free buffers
3909 * are available, schedules ipw_rx_queue_replenish
3910 *
3911 * -- enable interrupts --
3912 * ISR - ipw_rx() Detach ipw_rx_mem_buffers from pool up to the
Jeff Garzikbf794512005-07-31 13:07:26 -04003913 * READ INDEX, detaching the SKB from the pool.
James Ketrenos43f66a62005-03-25 12:31:53 -06003914 * Moves the packet buffer from queue to rx_used.
3915 * Calls ipw_rx_queue_restock to refill any empty
3916 * slots.
3917 * ...
3918 *
3919 */
3920
Jeff Garzikbf794512005-07-31 13:07:26 -04003921/*
James Ketrenos43f66a62005-03-25 12:31:53 -06003922 * If there are slots in the RX queue that need to be restocked,
3923 * and we have free pre-allocated buffers, fill the ranks as much
3924 * as we can pulling from rx_free.
3925 *
3926 * This moves the 'write' index forward to catch up with 'processed', and
3927 * also updates the memory address in the firmware to reference the new
3928 * target buffer.
3929 */
3930static void ipw_rx_queue_restock(struct ipw_priv *priv)
3931{
3932 struct ipw_rx_queue *rxq = priv->rxq;
3933 struct list_head *element;
3934 struct ipw_rx_mem_buffer *rxb;
3935 unsigned long flags;
3936 int write;
3937
3938 spin_lock_irqsave(&rxq->lock, flags);
3939 write = rxq->write;
3940 while ((rxq->write != rxq->processed) && (rxq->free_count)) {
3941 element = rxq->rx_free.next;
3942 rxb = list_entry(element, struct ipw_rx_mem_buffer, list);
3943 list_del(element);
3944
3945 ipw_write32(priv, CX2_RFDS_TABLE_LOWER + rxq->write * RFD_SIZE,
3946 rxb->dma_addr);
3947 rxq->queue[rxq->write] = rxb;
3948 rxq->write = (rxq->write + 1) % RX_QUEUE_SIZE;
3949 rxq->free_count--;
3950 }
3951 spin_unlock_irqrestore(&rxq->lock, flags);
3952
Jeff Garzikbf794512005-07-31 13:07:26 -04003953 /* If the pre-allocated buffer pool is dropping low, schedule to
James Ketrenos43f66a62005-03-25 12:31:53 -06003954 * refill it */
3955 if (rxq->free_count <= RX_LOW_WATERMARK)
3956 queue_work(priv->workqueue, &priv->rx_replenish);
3957
3958 /* If we've added more space for the firmware to place data, tell it */
Jeff Garzikbf794512005-07-31 13:07:26 -04003959 if (write != rxq->write)
James Ketrenos43f66a62005-03-25 12:31:53 -06003960 ipw_write32(priv, CX2_RX_WRITE_INDEX, rxq->write);
3961}
3962
3963/*
3964 * Move all used packet from rx_used to rx_free, allocating a new SKB for each.
Jeff Garzikbf794512005-07-31 13:07:26 -04003965 * Also restock the Rx queue via ipw_rx_queue_restock.
3966 *
James Ketrenos43f66a62005-03-25 12:31:53 -06003967 * This is called as a scheduled work item (except for during intialization)
3968 */
3969static void ipw_rx_queue_replenish(void *data)
3970{
3971 struct ipw_priv *priv = data;
3972 struct ipw_rx_queue *rxq = priv->rxq;
3973 struct list_head *element;
3974 struct ipw_rx_mem_buffer *rxb;
3975 unsigned long flags;
3976
3977 spin_lock_irqsave(&rxq->lock, flags);
3978 while (!list_empty(&rxq->rx_used)) {
3979 element = rxq->rx_used.next;
3980 rxb = list_entry(element, struct ipw_rx_mem_buffer, list);
3981 rxb->skb = alloc_skb(CX2_RX_BUF_SIZE, GFP_ATOMIC);
3982 if (!rxb->skb) {
3983 printk(KERN_CRIT "%s: Can not allocate SKB buffers.\n",
3984 priv->net_dev->name);
3985 /* We don't reschedule replenish work here -- we will
3986 * call the restock method and if it still needs
3987 * more buffers it will schedule replenish */
3988 break;
3989 }
3990 list_del(element);
Jeff Garzikbf794512005-07-31 13:07:26 -04003991
James Ketrenos43f66a62005-03-25 12:31:53 -06003992 rxb->rxb = (struct ipw_rx_buffer *)rxb->skb->data;
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003993 rxb->dma_addr =
3994 pci_map_single(priv->pci_dev, rxb->skb->data,
3995 CX2_RX_BUF_SIZE, PCI_DMA_FROMDEVICE);
Jeff Garzikbf794512005-07-31 13:07:26 -04003996
James Ketrenos43f66a62005-03-25 12:31:53 -06003997 list_add_tail(&rxb->list, &rxq->rx_free);
3998 rxq->free_count++;
3999 }
4000 spin_unlock_irqrestore(&rxq->lock, flags);
4001
4002 ipw_rx_queue_restock(priv);
4003}
4004
4005/* Assumes that the skb field of the buffers in 'pool' is kept accurate.
4006 * If an SKB has been detached, the POOL needs to have it's SKB set to NULL
Jeff Garzikbf794512005-07-31 13:07:26 -04004007 * This free routine walks the list of POOL entries and if SKB is set to
James Ketrenos43f66a62005-03-25 12:31:53 -06004008 * non NULL it is unmapped and freed
4009 */
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004010static void ipw_rx_queue_free(struct ipw_priv *priv, struct ipw_rx_queue *rxq)
James Ketrenos43f66a62005-03-25 12:31:53 -06004011{
4012 int i;
4013
4014 if (!rxq)
4015 return;
Jeff Garzikbf794512005-07-31 13:07:26 -04004016
James Ketrenos43f66a62005-03-25 12:31:53 -06004017 for (i = 0; i < RX_QUEUE_SIZE + RX_FREE_BUFFERS; i++) {
4018 if (rxq->pool[i].skb != NULL) {
4019 pci_unmap_single(priv->pci_dev, rxq->pool[i].dma_addr,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004020 CX2_RX_BUF_SIZE, PCI_DMA_FROMDEVICE);
James Ketrenos43f66a62005-03-25 12:31:53 -06004021 dev_kfree_skb(rxq->pool[i].skb);
4022 }
4023 }
4024
4025 kfree(rxq);
4026}
4027
4028static struct ipw_rx_queue *ipw_rx_queue_alloc(struct ipw_priv *priv)
4029{
4030 struct ipw_rx_queue *rxq;
4031 int i;
4032
4033 rxq = (struct ipw_rx_queue *)kmalloc(sizeof(*rxq), GFP_KERNEL);
Panagiotis Issarisad18b0e2005-09-05 04:14:10 +02004034 if (unlikely(!rxq)) {
4035 IPW_ERROR("memory allocation failed\n");
4036 return NULL;
4037 }
James Ketrenos43f66a62005-03-25 12:31:53 -06004038 memset(rxq, 0, sizeof(*rxq));
4039 spin_lock_init(&rxq->lock);
4040 INIT_LIST_HEAD(&rxq->rx_free);
4041 INIT_LIST_HEAD(&rxq->rx_used);
4042
4043 /* Fill the rx_used queue with _all_ of the Rx buffers */
Jeff Garzikbf794512005-07-31 13:07:26 -04004044 for (i = 0; i < RX_FREE_BUFFERS + RX_QUEUE_SIZE; i++)
James Ketrenos43f66a62005-03-25 12:31:53 -06004045 list_add_tail(&rxq->pool[i].list, &rxq->rx_used);
4046
4047 /* Set us so that we have processed and used all buffers, but have
4048 * not restocked the Rx queue with fresh buffers */
4049 rxq->read = rxq->write = 0;
4050 rxq->processed = RX_QUEUE_SIZE - 1;
4051 rxq->free_count = 0;
4052
4053 return rxq;
4054}
4055
4056static int ipw_is_rate_in_mask(struct ipw_priv *priv, int ieee_mode, u8 rate)
4057{
4058 rate &= ~IEEE80211_BASIC_RATE_MASK;
4059 if (ieee_mode == IEEE_A) {
4060 switch (rate) {
Jeff Garzikbf794512005-07-31 13:07:26 -04004061 case IEEE80211_OFDM_RATE_6MB:
4062 return priv->rates_mask & IEEE80211_OFDM_RATE_6MB_MASK ?
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004063 1 : 0;
Jeff Garzikbf794512005-07-31 13:07:26 -04004064 case IEEE80211_OFDM_RATE_9MB:
4065 return priv->rates_mask & IEEE80211_OFDM_RATE_9MB_MASK ?
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004066 1 : 0;
Jeff Garzikbf794512005-07-31 13:07:26 -04004067 case IEEE80211_OFDM_RATE_12MB:
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004068 return priv->
4069 rates_mask & IEEE80211_OFDM_RATE_12MB_MASK ? 1 : 0;
Jeff Garzikbf794512005-07-31 13:07:26 -04004070 case IEEE80211_OFDM_RATE_18MB:
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004071 return priv->
4072 rates_mask & IEEE80211_OFDM_RATE_18MB_MASK ? 1 : 0;
Jeff Garzikbf794512005-07-31 13:07:26 -04004073 case IEEE80211_OFDM_RATE_24MB:
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004074 return priv->
4075 rates_mask & IEEE80211_OFDM_RATE_24MB_MASK ? 1 : 0;
Jeff Garzikbf794512005-07-31 13:07:26 -04004076 case IEEE80211_OFDM_RATE_36MB:
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004077 return priv->
4078 rates_mask & IEEE80211_OFDM_RATE_36MB_MASK ? 1 : 0;
Jeff Garzikbf794512005-07-31 13:07:26 -04004079 case IEEE80211_OFDM_RATE_48MB:
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004080 return priv->
4081 rates_mask & IEEE80211_OFDM_RATE_48MB_MASK ? 1 : 0;
Jeff Garzikbf794512005-07-31 13:07:26 -04004082 case IEEE80211_OFDM_RATE_54MB:
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004083 return priv->
4084 rates_mask & IEEE80211_OFDM_RATE_54MB_MASK ? 1 : 0;
James Ketrenos43f66a62005-03-25 12:31:53 -06004085 default:
4086 return 0;
4087 }
4088 }
Jeff Garzikbf794512005-07-31 13:07:26 -04004089
James Ketrenos43f66a62005-03-25 12:31:53 -06004090 /* B and G mixed */
4091 switch (rate) {
Jeff Garzikbf794512005-07-31 13:07:26 -04004092 case IEEE80211_CCK_RATE_1MB:
James Ketrenos43f66a62005-03-25 12:31:53 -06004093 return priv->rates_mask & IEEE80211_CCK_RATE_1MB_MASK ? 1 : 0;
Jeff Garzikbf794512005-07-31 13:07:26 -04004094 case IEEE80211_CCK_RATE_2MB:
James Ketrenos43f66a62005-03-25 12:31:53 -06004095 return priv->rates_mask & IEEE80211_CCK_RATE_2MB_MASK ? 1 : 0;
Jeff Garzikbf794512005-07-31 13:07:26 -04004096 case IEEE80211_CCK_RATE_5MB:
James Ketrenos43f66a62005-03-25 12:31:53 -06004097 return priv->rates_mask & IEEE80211_CCK_RATE_5MB_MASK ? 1 : 0;
Jeff Garzikbf794512005-07-31 13:07:26 -04004098 case IEEE80211_CCK_RATE_11MB:
James Ketrenos43f66a62005-03-25 12:31:53 -06004099 return priv->rates_mask & IEEE80211_CCK_RATE_11MB_MASK ? 1 : 0;
4100 }
4101
4102 /* If we are limited to B modulations, bail at this point */
4103 if (ieee_mode == IEEE_B)
4104 return 0;
4105
4106 /* G */
4107 switch (rate) {
Jeff Garzikbf794512005-07-31 13:07:26 -04004108 case IEEE80211_OFDM_RATE_6MB:
James Ketrenos43f66a62005-03-25 12:31:53 -06004109 return priv->rates_mask & IEEE80211_OFDM_RATE_6MB_MASK ? 1 : 0;
Jeff Garzikbf794512005-07-31 13:07:26 -04004110 case IEEE80211_OFDM_RATE_9MB:
James Ketrenos43f66a62005-03-25 12:31:53 -06004111 return priv->rates_mask & IEEE80211_OFDM_RATE_9MB_MASK ? 1 : 0;
Jeff Garzikbf794512005-07-31 13:07:26 -04004112 case IEEE80211_OFDM_RATE_12MB:
James Ketrenos43f66a62005-03-25 12:31:53 -06004113 return priv->rates_mask & IEEE80211_OFDM_RATE_12MB_MASK ? 1 : 0;
Jeff Garzikbf794512005-07-31 13:07:26 -04004114 case IEEE80211_OFDM_RATE_18MB:
James Ketrenos43f66a62005-03-25 12:31:53 -06004115 return priv->rates_mask & IEEE80211_OFDM_RATE_18MB_MASK ? 1 : 0;
Jeff Garzikbf794512005-07-31 13:07:26 -04004116 case IEEE80211_OFDM_RATE_24MB:
James Ketrenos43f66a62005-03-25 12:31:53 -06004117 return priv->rates_mask & IEEE80211_OFDM_RATE_24MB_MASK ? 1 : 0;
Jeff Garzikbf794512005-07-31 13:07:26 -04004118 case IEEE80211_OFDM_RATE_36MB:
James Ketrenos43f66a62005-03-25 12:31:53 -06004119 return priv->rates_mask & IEEE80211_OFDM_RATE_36MB_MASK ? 1 : 0;
Jeff Garzikbf794512005-07-31 13:07:26 -04004120 case IEEE80211_OFDM_RATE_48MB:
James Ketrenos43f66a62005-03-25 12:31:53 -06004121 return priv->rates_mask & IEEE80211_OFDM_RATE_48MB_MASK ? 1 : 0;
Jeff Garzikbf794512005-07-31 13:07:26 -04004122 case IEEE80211_OFDM_RATE_54MB:
James Ketrenos43f66a62005-03-25 12:31:53 -06004123 return priv->rates_mask & IEEE80211_OFDM_RATE_54MB_MASK ? 1 : 0;
4124 }
4125
4126 return 0;
4127}
4128
Jeff Garzikbf794512005-07-31 13:07:26 -04004129static int ipw_compatible_rates(struct ipw_priv *priv,
James Ketrenos43f66a62005-03-25 12:31:53 -06004130 const struct ieee80211_network *network,
4131 struct ipw_supported_rates *rates)
4132{
4133 int num_rates, i;
4134
4135 memset(rates, 0, sizeof(*rates));
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004136 num_rates = min(network->rates_len, (u8) IPW_MAX_RATES);
James Ketrenos43f66a62005-03-25 12:31:53 -06004137 rates->num_rates = 0;
4138 for (i = 0; i < num_rates; i++) {
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004139 if (!ipw_is_rate_in_mask
4140 (priv, network->mode, network->rates[i])) {
James Ketrenos43f66a62005-03-25 12:31:53 -06004141 IPW_DEBUG_SCAN("Rate %02X masked : 0x%08X\n",
4142 network->rates[i], priv->rates_mask);
4143 continue;
4144 }
Jeff Garzikbf794512005-07-31 13:07:26 -04004145
James Ketrenos43f66a62005-03-25 12:31:53 -06004146 rates->supported_rates[rates->num_rates++] = network->rates[i];
4147 }
4148
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004149 num_rates =
4150 min(network->rates_ex_len, (u8) (IPW_MAX_RATES - num_rates));
James Ketrenos43f66a62005-03-25 12:31:53 -06004151 for (i = 0; i < num_rates; i++) {
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004152 if (!ipw_is_rate_in_mask
4153 (priv, network->mode, network->rates_ex[i])) {
James Ketrenos43f66a62005-03-25 12:31:53 -06004154 IPW_DEBUG_SCAN("Rate %02X masked : 0x%08X\n",
4155 network->rates_ex[i], priv->rates_mask);
4156 continue;
4157 }
Jeff Garzikbf794512005-07-31 13:07:26 -04004158
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004159 rates->supported_rates[rates->num_rates++] =
4160 network->rates_ex[i];
James Ketrenos43f66a62005-03-25 12:31:53 -06004161 }
4162
4163 return rates->num_rates;
4164}
4165
4166static inline void ipw_copy_rates(struct ipw_supported_rates *dest,
4167 const struct ipw_supported_rates *src)
4168{
4169 u8 i;
4170 for (i = 0; i < src->num_rates; i++)
4171 dest->supported_rates[i] = src->supported_rates[i];
4172 dest->num_rates = src->num_rates;
4173}
4174
4175/* TODO: Look at sniffed packets in the air to determine if the basic rate
4176 * mask should ever be used -- right now all callers to add the scan rates are
4177 * set with the modulation = CCK, so BASIC_RATE_MASK is never set... */
4178static void ipw_add_cck_scan_rates(struct ipw_supported_rates *rates,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004179 u8 modulation, u32 rate_mask)
James Ketrenos43f66a62005-03-25 12:31:53 -06004180{
Jeff Garzikbf794512005-07-31 13:07:26 -04004181 u8 basic_mask = (IEEE80211_OFDM_MODULATION == modulation) ?
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004182 IEEE80211_BASIC_RATE_MASK : 0;
Jeff Garzikbf794512005-07-31 13:07:26 -04004183
James Ketrenos43f66a62005-03-25 12:31:53 -06004184 if (rate_mask & IEEE80211_CCK_RATE_1MB_MASK)
Jeff Garzikbf794512005-07-31 13:07:26 -04004185 rates->supported_rates[rates->num_rates++] =
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004186 IEEE80211_BASIC_RATE_MASK | IEEE80211_CCK_RATE_1MB;
James Ketrenos43f66a62005-03-25 12:31:53 -06004187
4188 if (rate_mask & IEEE80211_CCK_RATE_2MB_MASK)
Jeff Garzikbf794512005-07-31 13:07:26 -04004189 rates->supported_rates[rates->num_rates++] =
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004190 IEEE80211_BASIC_RATE_MASK | IEEE80211_CCK_RATE_2MB;
James Ketrenos43f66a62005-03-25 12:31:53 -06004191
4192 if (rate_mask & IEEE80211_CCK_RATE_5MB_MASK)
Jeff Garzikbf794512005-07-31 13:07:26 -04004193 rates->supported_rates[rates->num_rates++] = basic_mask |
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004194 IEEE80211_CCK_RATE_5MB;
James Ketrenos43f66a62005-03-25 12:31:53 -06004195
4196 if (rate_mask & IEEE80211_CCK_RATE_11MB_MASK)
Jeff Garzikbf794512005-07-31 13:07:26 -04004197 rates->supported_rates[rates->num_rates++] = basic_mask |
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004198 IEEE80211_CCK_RATE_11MB;
James Ketrenos43f66a62005-03-25 12:31:53 -06004199}
4200
4201static void ipw_add_ofdm_scan_rates(struct ipw_supported_rates *rates,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004202 u8 modulation, u32 rate_mask)
James Ketrenos43f66a62005-03-25 12:31:53 -06004203{
Jeff Garzikbf794512005-07-31 13:07:26 -04004204 u8 basic_mask = (IEEE80211_OFDM_MODULATION == modulation) ?
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004205 IEEE80211_BASIC_RATE_MASK : 0;
James Ketrenos43f66a62005-03-25 12:31:53 -06004206
4207 if (rate_mask & IEEE80211_OFDM_RATE_6MB_MASK)
Jeff Garzikbf794512005-07-31 13:07:26 -04004208 rates->supported_rates[rates->num_rates++] = basic_mask |
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004209 IEEE80211_OFDM_RATE_6MB;
James Ketrenos43f66a62005-03-25 12:31:53 -06004210
4211 if (rate_mask & IEEE80211_OFDM_RATE_9MB_MASK)
Jeff Garzikbf794512005-07-31 13:07:26 -04004212 rates->supported_rates[rates->num_rates++] =
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004213 IEEE80211_OFDM_RATE_9MB;
James Ketrenos43f66a62005-03-25 12:31:53 -06004214
4215 if (rate_mask & IEEE80211_OFDM_RATE_12MB_MASK)
Jeff Garzikbf794512005-07-31 13:07:26 -04004216 rates->supported_rates[rates->num_rates++] = basic_mask |
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004217 IEEE80211_OFDM_RATE_12MB;
James Ketrenos43f66a62005-03-25 12:31:53 -06004218
4219 if (rate_mask & IEEE80211_OFDM_RATE_18MB_MASK)
Jeff Garzikbf794512005-07-31 13:07:26 -04004220 rates->supported_rates[rates->num_rates++] =
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004221 IEEE80211_OFDM_RATE_18MB;
James Ketrenos43f66a62005-03-25 12:31:53 -06004222
4223 if (rate_mask & IEEE80211_OFDM_RATE_24MB_MASK)
Jeff Garzikbf794512005-07-31 13:07:26 -04004224 rates->supported_rates[rates->num_rates++] = basic_mask |
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004225 IEEE80211_OFDM_RATE_24MB;
James Ketrenos43f66a62005-03-25 12:31:53 -06004226
4227 if (rate_mask & IEEE80211_OFDM_RATE_36MB_MASK)
Jeff Garzikbf794512005-07-31 13:07:26 -04004228 rates->supported_rates[rates->num_rates++] =
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004229 IEEE80211_OFDM_RATE_36MB;
James Ketrenos43f66a62005-03-25 12:31:53 -06004230
4231 if (rate_mask & IEEE80211_OFDM_RATE_48MB_MASK)
Jeff Garzikbf794512005-07-31 13:07:26 -04004232 rates->supported_rates[rates->num_rates++] =
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004233 IEEE80211_OFDM_RATE_48MB;
James Ketrenos43f66a62005-03-25 12:31:53 -06004234
4235 if (rate_mask & IEEE80211_OFDM_RATE_54MB_MASK)
Jeff Garzikbf794512005-07-31 13:07:26 -04004236 rates->supported_rates[rates->num_rates++] =
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004237 IEEE80211_OFDM_RATE_54MB;
James Ketrenos43f66a62005-03-25 12:31:53 -06004238}
4239
4240struct ipw_network_match {
4241 struct ieee80211_network *network;
4242 struct ipw_supported_rates rates;
4243};
4244
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004245static int ipw_best_network(struct ipw_priv *priv,
4246 struct ipw_network_match *match,
4247 struct ieee80211_network *network, int roaming)
James Ketrenos43f66a62005-03-25 12:31:53 -06004248{
4249 struct ipw_supported_rates rates;
4250
4251 /* Verify that this network's capability is compatible with the
4252 * current mode (AdHoc or Infrastructure) */
4253 if ((priv->ieee->iw_mode == IW_MODE_INFRA &&
Jouni Malinen24743852005-08-14 20:59:59 -07004254 !(network->capability & WLAN_CAPABILITY_ESS)) ||
James Ketrenos43f66a62005-03-25 12:31:53 -06004255 (priv->ieee->iw_mode == IW_MODE_ADHOC &&
4256 !(network->capability & WLAN_CAPABILITY_IBSS))) {
4257 IPW_DEBUG_ASSOC("Network '%s (" MAC_FMT ")' excluded due to "
Jeff Garzikbf794512005-07-31 13:07:26 -04004258 "capability mismatch.\n",
James Ketrenos43f66a62005-03-25 12:31:53 -06004259 escape_essid(network->ssid, network->ssid_len),
4260 MAC_ARG(network->bssid));
4261 return 0;
4262 }
4263
4264 /* If we do not have an ESSID for this AP, we can not associate with
4265 * it */
4266 if (network->flags & NETWORK_EMPTY_ESSID) {
4267 IPW_DEBUG_ASSOC("Network '%s (" MAC_FMT ")' excluded "
4268 "because of hidden ESSID.\n",
4269 escape_essid(network->ssid, network->ssid_len),
4270 MAC_ARG(network->bssid));
4271 return 0;
4272 }
Jeff Garzikbf794512005-07-31 13:07:26 -04004273
James Ketrenos43f66a62005-03-25 12:31:53 -06004274 if (unlikely(roaming)) {
4275 /* If we are roaming, then ensure check if this is a valid
4276 * network to try and roam to */
4277 if ((network->ssid_len != match->network->ssid_len) ||
Jeff Garzikbf794512005-07-31 13:07:26 -04004278 memcmp(network->ssid, match->network->ssid,
James Ketrenos43f66a62005-03-25 12:31:53 -06004279 network->ssid_len)) {
4280 IPW_DEBUG_ASSOC("Netowrk '%s (" MAC_FMT ")' excluded "
4281 "because of non-network ESSID.\n",
Jeff Garzikbf794512005-07-31 13:07:26 -04004282 escape_essid(network->ssid,
James Ketrenos43f66a62005-03-25 12:31:53 -06004283 network->ssid_len),
4284 MAC_ARG(network->bssid));
4285 return 0;
4286 }
4287 } else {
Jeff Garzikbf794512005-07-31 13:07:26 -04004288 /* If an ESSID has been configured then compare the broadcast
4289 * ESSID to ours */
4290 if ((priv->config & CFG_STATIC_ESSID) &&
James Ketrenos43f66a62005-03-25 12:31:53 -06004291 ((network->ssid_len != priv->essid_len) ||
Jeff Garzikbf794512005-07-31 13:07:26 -04004292 memcmp(network->ssid, priv->essid,
James Ketrenos43f66a62005-03-25 12:31:53 -06004293 min(network->ssid_len, priv->essid_len)))) {
4294 char escaped[IW_ESSID_MAX_SIZE * 2 + 1];
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004295 strncpy(escaped,
4296 escape_essid(network->ssid, network->ssid_len),
James Ketrenos43f66a62005-03-25 12:31:53 -06004297 sizeof(escaped));
4298 IPW_DEBUG_ASSOC("Network '%s (" MAC_FMT ")' excluded "
Jeff Garzikbf794512005-07-31 13:07:26 -04004299 "because of ESSID mismatch: '%s'.\n",
James Ketrenos43f66a62005-03-25 12:31:53 -06004300 escaped, MAC_ARG(network->bssid),
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004301 escape_essid(priv->essid,
4302 priv->essid_len));
James Ketrenos43f66a62005-03-25 12:31:53 -06004303 return 0;
4304 }
4305 }
4306
4307 /* If the old network rate is better than this one, don't bother
4308 * testing everything else. */
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004309 if (match->network && match->network->stats.rssi > network->stats.rssi) {
James Ketrenos43f66a62005-03-25 12:31:53 -06004310 char escaped[IW_ESSID_MAX_SIZE * 2 + 1];
Jeff Garzikbf794512005-07-31 13:07:26 -04004311 strncpy(escaped,
4312 escape_essid(network->ssid, network->ssid_len),
James Ketrenos43f66a62005-03-25 12:31:53 -06004313 sizeof(escaped));
4314 IPW_DEBUG_ASSOC("Network '%s (" MAC_FMT ")' excluded because "
4315 "'%s (" MAC_FMT ")' has a stronger signal.\n",
4316 escaped, MAC_ARG(network->bssid),
4317 escape_essid(match->network->ssid,
4318 match->network->ssid_len),
4319 MAC_ARG(match->network->bssid));
4320 return 0;
4321 }
Jeff Garzikbf794512005-07-31 13:07:26 -04004322
James Ketrenos43f66a62005-03-25 12:31:53 -06004323 /* If this network has already had an association attempt within the
4324 * last 3 seconds, do not try and associate again... */
4325 if (network->last_associate &&
4326 time_after(network->last_associate + (HZ * 5UL), jiffies)) {
4327 IPW_DEBUG_ASSOC("Network '%s (" MAC_FMT ")' excluded "
4328 "because of storming (%lu since last "
4329 "assoc attempt).\n",
4330 escape_essid(network->ssid, network->ssid_len),
4331 MAC_ARG(network->bssid),
4332 (jiffies - network->last_associate) / HZ);
4333 return 0;
4334 }
4335
4336 /* Now go through and see if the requested network is valid... */
Jeff Garzikbf794512005-07-31 13:07:26 -04004337 if (priv->ieee->scan_age != 0 &&
James Ketrenos43f66a62005-03-25 12:31:53 -06004338 jiffies - network->last_scanned > priv->ieee->scan_age) {
4339 IPW_DEBUG_ASSOC("Network '%s (" MAC_FMT ")' excluded "
4340 "because of age: %lums.\n",
4341 escape_essid(network->ssid, network->ssid_len),
4342 MAC_ARG(network->bssid),
4343 (jiffies - network->last_scanned) / (HZ / 100));
4344 return 0;
Jeff Garzikbf794512005-07-31 13:07:26 -04004345 }
James Ketrenos43f66a62005-03-25 12:31:53 -06004346
Jeff Garzikbf794512005-07-31 13:07:26 -04004347 if ((priv->config & CFG_STATIC_CHANNEL) &&
James Ketrenos43f66a62005-03-25 12:31:53 -06004348 (network->channel != priv->channel)) {
4349 IPW_DEBUG_ASSOC("Network '%s (" MAC_FMT ")' excluded "
4350 "because of channel mismatch: %d != %d.\n",
4351 escape_essid(network->ssid, network->ssid_len),
4352 MAC_ARG(network->bssid),
4353 network->channel, priv->channel);
4354 return 0;
4355 }
Jeff Garzikbf794512005-07-31 13:07:26 -04004356
James Ketrenos43f66a62005-03-25 12:31:53 -06004357 /* Verify privacy compatability */
Jeff Garzikbf794512005-07-31 13:07:26 -04004358 if (((priv->capability & CAP_PRIVACY_ON) ? 1 : 0) !=
James Ketrenos43f66a62005-03-25 12:31:53 -06004359 ((network->capability & WLAN_CAPABILITY_PRIVACY) ? 1 : 0)) {
4360 IPW_DEBUG_ASSOC("Network '%s (" MAC_FMT ")' excluded "
4361 "because of privacy mismatch: %s != %s.\n",
4362 escape_essid(network->ssid, network->ssid_len),
4363 MAC_ARG(network->bssid),
Jeff Garzikbf794512005-07-31 13:07:26 -04004364 priv->capability & CAP_PRIVACY_ON ? "on" :
James Ketrenos43f66a62005-03-25 12:31:53 -06004365 "off",
Jeff Garzikbf794512005-07-31 13:07:26 -04004366 network->capability &
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004367 WLAN_CAPABILITY_PRIVACY ? "on" : "off");
James Ketrenos43f66a62005-03-25 12:31:53 -06004368 return 0;
4369 }
Jeff Garzikbf794512005-07-31 13:07:26 -04004370
4371 if ((priv->config & CFG_STATIC_BSSID) &&
James Ketrenos43f66a62005-03-25 12:31:53 -06004372 memcmp(network->bssid, priv->bssid, ETH_ALEN)) {
4373 IPW_DEBUG_ASSOC("Network '%s (" MAC_FMT ")' excluded "
4374 "because of BSSID mismatch: " MAC_FMT ".\n",
4375 escape_essid(network->ssid, network->ssid_len),
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004376 MAC_ARG(network->bssid), MAC_ARG(priv->bssid));
James Ketrenos43f66a62005-03-25 12:31:53 -06004377 return 0;
4378 }
Jeff Garzikbf794512005-07-31 13:07:26 -04004379
James Ketrenos43f66a62005-03-25 12:31:53 -06004380 /* Filter out any incompatible freq / mode combinations */
4381 if (!ieee80211_is_valid_mode(priv->ieee, network->mode)) {
4382 IPW_DEBUG_ASSOC("Network '%s (" MAC_FMT ")' excluded "
4383 "because of invalid frequency/mode "
4384 "combination.\n",
4385 escape_essid(network->ssid, network->ssid_len),
4386 MAC_ARG(network->bssid));
4387 return 0;
4388 }
Jeff Garzikbf794512005-07-31 13:07:26 -04004389
James Ketrenos43f66a62005-03-25 12:31:53 -06004390 ipw_compatible_rates(priv, network, &rates);
4391 if (rates.num_rates == 0) {
4392 IPW_DEBUG_ASSOC("Network '%s (" MAC_FMT ")' excluded "
4393 "because of no compatible rates.\n",
4394 escape_essid(network->ssid, network->ssid_len),
4395 MAC_ARG(network->bssid));
4396 return 0;
4397 }
Jeff Garzikbf794512005-07-31 13:07:26 -04004398
James Ketrenos43f66a62005-03-25 12:31:53 -06004399 /* TODO: Perform any further minimal comparititive tests. We do not
4400 * want to put too much policy logic here; intelligent scan selection
4401 * should occur within a generic IEEE 802.11 user space tool. */
4402
4403 /* Set up 'new' AP to this network */
4404 ipw_copy_rates(&match->rates, &rates);
4405 match->network = network;
4406
4407 IPW_DEBUG_ASSOC("Network '%s (" MAC_FMT ")' is a viable match.\n",
4408 escape_essid(network->ssid, network->ssid_len),
4409 MAC_ARG(network->bssid));
4410
4411 return 1;
4412}
4413
Jeff Garzikbf794512005-07-31 13:07:26 -04004414static void ipw_adhoc_create(struct ipw_priv *priv,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004415 struct ieee80211_network *network)
James Ketrenos43f66a62005-03-25 12:31:53 -06004416{
4417 /*
4418 * For the purposes of scanning, we can set our wireless mode
4419 * to trigger scans across combinations of bands, but when it
4420 * comes to creating a new ad-hoc network, we have tell the FW
4421 * exactly which band to use.
4422 *
Jeff Garzikbf794512005-07-31 13:07:26 -04004423 * We also have the possibility of an invalid channel for the
James Ketrenos43f66a62005-03-25 12:31:53 -06004424 * chossen band. Attempting to create a new ad-hoc network
4425 * with an invalid channel for wireless mode will trigger a
4426 * FW fatal error.
4427 */
4428 network->mode = is_valid_channel(priv->ieee->mode, priv->channel);
4429 if (network->mode) {
4430 network->channel = priv->channel;
4431 } else {
4432 IPW_WARNING("Overriding invalid channel\n");
4433 if (priv->ieee->mode & IEEE_A) {
4434 network->mode = IEEE_A;
4435 priv->channel = band_a_active_channel[0];
4436 } else if (priv->ieee->mode & IEEE_G) {
4437 network->mode = IEEE_G;
4438 priv->channel = band_b_active_channel[0];
4439 } else {
4440 network->mode = IEEE_B;
4441 priv->channel = band_b_active_channel[0];
4442 }
4443 }
4444
4445 network->channel = priv->channel;
4446 priv->config |= CFG_ADHOC_PERSIST;
4447 ipw_create_bssid(priv, network->bssid);
4448 network->ssid_len = priv->essid_len;
4449 memcpy(network->ssid, priv->essid, priv->essid_len);
4450 memset(&network->stats, 0, sizeof(network->stats));
4451 network->capability = WLAN_CAPABILITY_IBSS;
4452 if (priv->capability & CAP_PRIVACY_ON)
4453 network->capability |= WLAN_CAPABILITY_PRIVACY;
4454 network->rates_len = min(priv->rates.num_rates, MAX_RATES_LENGTH);
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004455 memcpy(network->rates, priv->rates.supported_rates, network->rates_len);
James Ketrenos43f66a62005-03-25 12:31:53 -06004456 network->rates_ex_len = priv->rates.num_rates - network->rates_len;
Jeff Garzikbf794512005-07-31 13:07:26 -04004457 memcpy(network->rates_ex,
James Ketrenos43f66a62005-03-25 12:31:53 -06004458 &priv->rates.supported_rates[network->rates_len],
4459 network->rates_ex_len);
4460 network->last_scanned = 0;
4461 network->flags = 0;
4462 network->last_associate = 0;
4463 network->time_stamp[0] = 0;
4464 network->time_stamp[1] = 0;
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004465 network->beacon_interval = 100; /* Default */
4466 network->listen_interval = 10; /* Default */
4467 network->atim_window = 0; /* Default */
Jeff Garzikbf794512005-07-31 13:07:26 -04004468#ifdef CONFIG_IEEE80211_WPA
James Ketrenos43f66a62005-03-25 12:31:53 -06004469 network->wpa_ie_len = 0;
4470 network->rsn_ie_len = 0;
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004471#endif /* CONFIG_IEEE80211_WPA */
James Ketrenos43f66a62005-03-25 12:31:53 -06004472}
4473
4474static void ipw_send_wep_keys(struct ipw_priv *priv)
4475{
4476 struct ipw_wep_key *key;
4477 int i;
4478 struct host_cmd cmd = {
4479 .cmd = IPW_CMD_WEP_KEY,
4480 .len = sizeof(*key)
4481 };
4482
4483 key = (struct ipw_wep_key *)&cmd.param;
4484 key->cmd_id = DINO_CMD_WEP_KEY;
4485 key->seq_num = 0;
4486
Jeff Garzikbf794512005-07-31 13:07:26 -04004487 for (i = 0; i < 4; i++) {
James Ketrenos43f66a62005-03-25 12:31:53 -06004488 key->key_index = i;
4489 if (!(priv->sec.flags & (1 << i))) {
4490 key->key_size = 0;
4491 } else {
4492 key->key_size = priv->sec.key_sizes[i];
4493 memcpy(key->key, priv->sec.keys[i], key->key_size);
4494 }
4495
4496 if (ipw_send_cmd(priv, &cmd)) {
4497 IPW_ERROR("failed to send WEP_KEY command\n");
4498 return;
4499 }
Jeff Garzikbf794512005-07-31 13:07:26 -04004500 }
James Ketrenos43f66a62005-03-25 12:31:53 -06004501}
4502
4503static void ipw_adhoc_check(void *data)
4504{
4505 struct ipw_priv *priv = data;
Jeff Garzikbf794512005-07-31 13:07:26 -04004506
James Ketrenos43f66a62005-03-25 12:31:53 -06004507 if (priv->missed_adhoc_beacons++ > priv->missed_beacon_threshold &&
4508 !(priv->config & CFG_ADHOC_PERSIST)) {
4509 IPW_DEBUG_SCAN("Disassociating due to missed beacons\n");
4510 ipw_remove_current_network(priv);
4511 ipw_disassociate(priv);
4512 return;
4513 }
4514
Jeff Garzikbf794512005-07-31 13:07:26 -04004515 queue_delayed_work(priv->workqueue, &priv->adhoc_check,
James Ketrenos43f66a62005-03-25 12:31:53 -06004516 priv->assoc_request.beacon_interval);
4517}
4518
4519#ifdef CONFIG_IPW_DEBUG
4520static void ipw_debug_config(struct ipw_priv *priv)
4521{
4522 IPW_DEBUG_INFO("Scan completed, no valid APs matched "
4523 "[CFG 0x%08X]\n", priv->config);
4524 if (priv->config & CFG_STATIC_CHANNEL)
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004525 IPW_DEBUG_INFO("Channel locked to %d\n", priv->channel);
James Ketrenos43f66a62005-03-25 12:31:53 -06004526 else
4527 IPW_DEBUG_INFO("Channel unlocked.\n");
4528 if (priv->config & CFG_STATIC_ESSID)
Jeff Garzikbf794512005-07-31 13:07:26 -04004529 IPW_DEBUG_INFO("ESSID locked to '%s'\n",
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004530 escape_essid(priv->essid, priv->essid_len));
James Ketrenos43f66a62005-03-25 12:31:53 -06004531 else
4532 IPW_DEBUG_INFO("ESSID unlocked.\n");
4533 if (priv->config & CFG_STATIC_BSSID)
4534 IPW_DEBUG_INFO("BSSID locked to %d\n", priv->channel);
4535 else
4536 IPW_DEBUG_INFO("BSSID unlocked.\n");
4537 if (priv->capability & CAP_PRIVACY_ON)
4538 IPW_DEBUG_INFO("PRIVACY on\n");
4539 else
4540 IPW_DEBUG_INFO("PRIVACY off\n");
4541 IPW_DEBUG_INFO("RATE MASK: 0x%08X\n", priv->rates_mask);
4542}
4543#else
Jiri Benc8d45ff72005-08-25 20:09:39 -04004544#define ipw_debug_config(x) do {} while (0)
James Ketrenos43f66a62005-03-25 12:31:53 -06004545#endif
4546
4547static inline void ipw_set_fixed_rate(struct ipw_priv *priv,
4548 struct ieee80211_network *network)
4549{
4550 /* TODO: Verify that this works... */
4551 struct ipw_fixed_rate fr = {
4552 .tx_rates = priv->rates_mask
4553 };
4554 u32 reg;
4555 u16 mask = 0;
4556
Jeff Garzikbf794512005-07-31 13:07:26 -04004557 /* Identify 'current FW band' and match it with the fixed
James Ketrenos43f66a62005-03-25 12:31:53 -06004558 * Tx rates */
Jeff Garzikbf794512005-07-31 13:07:26 -04004559
James Ketrenos43f66a62005-03-25 12:31:53 -06004560 switch (priv->ieee->freq_band) {
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004561 case IEEE80211_52GHZ_BAND: /* A only */
James Ketrenos43f66a62005-03-25 12:31:53 -06004562 /* IEEE_A */
4563 if (priv->rates_mask & ~IEEE80211_OFDM_RATES_MASK) {
4564 /* Invalid fixed rate mask */
4565 fr.tx_rates = 0;
4566 break;
4567 }
Jeff Garzikbf794512005-07-31 13:07:26 -04004568
James Ketrenos43f66a62005-03-25 12:31:53 -06004569 fr.tx_rates >>= IEEE80211_OFDM_SHIFT_MASK_A;
4570 break;
4571
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004572 default: /* 2.4Ghz or Mixed */
James Ketrenos43f66a62005-03-25 12:31:53 -06004573 /* IEEE_B */
4574 if (network->mode == IEEE_B) {
4575 if (fr.tx_rates & ~IEEE80211_CCK_RATES_MASK) {
4576 /* Invalid fixed rate mask */
4577 fr.tx_rates = 0;
4578 }
4579 break;
Jeff Garzikbf794512005-07-31 13:07:26 -04004580 }
James Ketrenos43f66a62005-03-25 12:31:53 -06004581
4582 /* IEEE_G */
4583 if (fr.tx_rates & ~(IEEE80211_CCK_RATES_MASK |
4584 IEEE80211_OFDM_RATES_MASK)) {
4585 /* Invalid fixed rate mask */
4586 fr.tx_rates = 0;
4587 break;
4588 }
4589
4590 if (IEEE80211_OFDM_RATE_6MB_MASK & fr.tx_rates) {
4591 mask |= (IEEE80211_OFDM_RATE_6MB_MASK >> 1);
4592 fr.tx_rates &= ~IEEE80211_OFDM_RATE_6MB_MASK;
4593 }
Jeff Garzikbf794512005-07-31 13:07:26 -04004594
James Ketrenos43f66a62005-03-25 12:31:53 -06004595 if (IEEE80211_OFDM_RATE_9MB_MASK & fr.tx_rates) {
4596 mask |= (IEEE80211_OFDM_RATE_9MB_MASK >> 1);
4597 fr.tx_rates &= ~IEEE80211_OFDM_RATE_9MB_MASK;
4598 }
Jeff Garzikbf794512005-07-31 13:07:26 -04004599
James Ketrenos43f66a62005-03-25 12:31:53 -06004600 if (IEEE80211_OFDM_RATE_12MB_MASK & fr.tx_rates) {
4601 mask |= (IEEE80211_OFDM_RATE_12MB_MASK >> 1);
4602 fr.tx_rates &= ~IEEE80211_OFDM_RATE_12MB_MASK;
4603 }
Jeff Garzikbf794512005-07-31 13:07:26 -04004604
James Ketrenos43f66a62005-03-25 12:31:53 -06004605 fr.tx_rates |= mask;
4606 break;
4607 }
4608
4609 reg = ipw_read32(priv, IPW_MEM_FIXED_OVERRIDE);
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004610 ipw_write_reg32(priv, reg, *(u32 *) & fr);
James Ketrenos43f66a62005-03-25 12:31:53 -06004611}
4612
4613static int ipw_associate_network(struct ipw_priv *priv,
4614 struct ieee80211_network *network,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004615 struct ipw_supported_rates *rates, int roaming)
James Ketrenos43f66a62005-03-25 12:31:53 -06004616{
4617 int err;
4618
4619 if (priv->config & CFG_FIXED_RATE)
4620 ipw_set_fixed_rate(priv, network);
4621
4622 if (!(priv->config & CFG_STATIC_ESSID)) {
Jeff Garzikbf794512005-07-31 13:07:26 -04004623 priv->essid_len = min(network->ssid_len,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004624 (u8) IW_ESSID_MAX_SIZE);
James Ketrenos43f66a62005-03-25 12:31:53 -06004625 memcpy(priv->essid, network->ssid, priv->essid_len);
4626 }
4627
4628 network->last_associate = jiffies;
4629
4630 memset(&priv->assoc_request, 0, sizeof(priv->assoc_request));
4631 priv->assoc_request.channel = network->channel;
4632 if ((priv->capability & CAP_PRIVACY_ON) &&
4633 (priv->capability & CAP_SHARED_KEY)) {
4634 priv->assoc_request.auth_type = AUTH_SHARED_KEY;
4635 priv->assoc_request.auth_key = priv->sec.active_key;
4636 } else {
4637 priv->assoc_request.auth_type = AUTH_OPEN;
4638 priv->assoc_request.auth_key = 0;
4639 }
4640
Jeff Garzikbf794512005-07-31 13:07:26 -04004641 if (priv->capability & CAP_PRIVACY_ON)
James Ketrenos43f66a62005-03-25 12:31:53 -06004642 ipw_send_wep_keys(priv);
4643
Jeff Garzikbf794512005-07-31 13:07:26 -04004644 /*
4645 * It is valid for our ieee device to support multiple modes, but
4646 * when it comes to associating to a given network we have to choose
James Ketrenos43f66a62005-03-25 12:31:53 -06004647 * just one mode.
4648 */
4649 if (network->mode & priv->ieee->mode & IEEE_A)
4650 priv->assoc_request.ieee_mode = IPW_A_MODE;
4651 else if (network->mode & priv->ieee->mode & IEEE_G)
4652 priv->assoc_request.ieee_mode = IPW_G_MODE;
4653 else if (network->mode & priv->ieee->mode & IEEE_B)
4654 priv->assoc_request.ieee_mode = IPW_B_MODE;
4655
4656 IPW_DEBUG_ASSOC("%sssocation attempt: '%s', channel %d, "
4657 "802.11%c [%d], enc=%s%s%s%c%c\n",
4658 roaming ? "Rea" : "A",
Jeff Garzikbf794512005-07-31 13:07:26 -04004659 escape_essid(priv->essid, priv->essid_len),
4660 network->channel,
4661 ipw_modes[priv->assoc_request.ieee_mode],
4662 rates->num_rates,
James Ketrenos43f66a62005-03-25 12:31:53 -06004663 priv->capability & CAP_PRIVACY_ON ? "on " : "off",
Jeff Garzikbf794512005-07-31 13:07:26 -04004664 priv->capability & CAP_PRIVACY_ON ?
4665 (priv->capability & CAP_SHARED_KEY ? "(shared)" :
James Ketrenos43f66a62005-03-25 12:31:53 -06004666 "(open)") : "",
4667 priv->capability & CAP_PRIVACY_ON ? " key=" : "",
Jeff Garzikbf794512005-07-31 13:07:26 -04004668 priv->capability & CAP_PRIVACY_ON ?
James Ketrenos43f66a62005-03-25 12:31:53 -06004669 '1' + priv->sec.active_key : '.',
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004670 priv->capability & CAP_PRIVACY_ON ? '.' : ' ');
James Ketrenos43f66a62005-03-25 12:31:53 -06004671
4672 priv->assoc_request.beacon_interval = network->beacon_interval;
4673 if ((priv->ieee->iw_mode == IW_MODE_ADHOC) &&
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004674 (network->time_stamp[0] == 0) && (network->time_stamp[1] == 0)) {
James Ketrenos43f66a62005-03-25 12:31:53 -06004675 priv->assoc_request.assoc_type = HC_IBSS_START;
4676 priv->assoc_request.assoc_tsf_msw = 0;
4677 priv->assoc_request.assoc_tsf_lsw = 0;
4678 } else {
4679 if (unlikely(roaming))
4680 priv->assoc_request.assoc_type = HC_REASSOCIATE;
4681 else
4682 priv->assoc_request.assoc_type = HC_ASSOCIATE;
4683 priv->assoc_request.assoc_tsf_msw = network->time_stamp[1];
4684 priv->assoc_request.assoc_tsf_lsw = network->time_stamp[0];
4685 }
4686
4687 memcpy(&priv->assoc_request.bssid, network->bssid, ETH_ALEN);
4688
4689 if (priv->ieee->iw_mode == IW_MODE_ADHOC) {
4690 memset(&priv->assoc_request.dest, 0xFF, ETH_ALEN);
4691 priv->assoc_request.atim_window = network->atim_window;
4692 } else {
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004693 memcpy(&priv->assoc_request.dest, network->bssid, ETH_ALEN);
James Ketrenos43f66a62005-03-25 12:31:53 -06004694 priv->assoc_request.atim_window = 0;
4695 }
4696
4697 priv->assoc_request.capability = network->capability;
4698 priv->assoc_request.listen_interval = network->listen_interval;
Jeff Garzikbf794512005-07-31 13:07:26 -04004699
James Ketrenos43f66a62005-03-25 12:31:53 -06004700 err = ipw_send_ssid(priv, priv->essid, priv->essid_len);
4701 if (err) {
4702 IPW_DEBUG_HC("Attempt to send SSID command failed.\n");
4703 return err;
4704 }
4705
4706 rates->ieee_mode = priv->assoc_request.ieee_mode;
4707 rates->purpose = IPW_RATE_CONNECT;
4708 ipw_send_supported_rates(priv, rates);
Jeff Garzikbf794512005-07-31 13:07:26 -04004709
James Ketrenos43f66a62005-03-25 12:31:53 -06004710 if (priv->assoc_request.ieee_mode == IPW_G_MODE)
4711 priv->sys_config.dot11g_auto_detection = 1;
4712 else
4713 priv->sys_config.dot11g_auto_detection = 0;
4714 err = ipw_send_system_config(priv, &priv->sys_config);
4715 if (err) {
4716 IPW_DEBUG_HC("Attempt to send sys config command failed.\n");
4717 return err;
4718 }
Jeff Garzikbf794512005-07-31 13:07:26 -04004719
James Ketrenos43f66a62005-03-25 12:31:53 -06004720 IPW_DEBUG_ASSOC("Association sensitivity: %d\n", network->stats.rssi);
4721 err = ipw_set_sensitivity(priv, network->stats.rssi);
4722 if (err) {
4723 IPW_DEBUG_HC("Attempt to send associate command failed.\n");
4724 return err;
4725 }
4726
4727 /*
4728 * If preemption is enabled, it is possible for the association
4729 * to complete before we return from ipw_send_associate. Therefore
4730 * we have to be sure and update our priviate data first.
4731 */
4732 priv->channel = network->channel;
4733 memcpy(priv->bssid, network->bssid, ETH_ALEN);
Jeff Garzikbf794512005-07-31 13:07:26 -04004734 priv->status |= STATUS_ASSOCIATING;
James Ketrenos43f66a62005-03-25 12:31:53 -06004735 priv->status &= ~STATUS_SECURITY_UPDATED;
4736
4737 priv->assoc_network = network;
4738
4739 err = ipw_send_associate(priv, &priv->assoc_request);
4740 if (err) {
4741 IPW_DEBUG_HC("Attempt to send associate command failed.\n");
4742 return err;
4743 }
Jeff Garzikbf794512005-07-31 13:07:26 -04004744
4745 IPW_DEBUG(IPW_DL_STATE, "associating: '%s' " MAC_FMT " \n",
James Ketrenos43f66a62005-03-25 12:31:53 -06004746 escape_essid(priv->essid, priv->essid_len),
4747 MAC_ARG(priv->bssid));
4748
4749 return 0;
4750}
4751
4752static void ipw_roam(void *data)
4753{
4754 struct ipw_priv *priv = data;
4755 struct ieee80211_network *network = NULL;
4756 struct ipw_network_match match = {
4757 .network = priv->assoc_network
4758 };
4759
4760 /* The roaming process is as follows:
Jeff Garzikbf794512005-07-31 13:07:26 -04004761 *
4762 * 1. Missed beacon threshold triggers the roaming process by
James Ketrenos43f66a62005-03-25 12:31:53 -06004763 * setting the status ROAM bit and requesting a scan.
4764 * 2. When the scan completes, it schedules the ROAM work
4765 * 3. The ROAM work looks at all of the known networks for one that
4766 * is a better network than the currently associated. If none
4767 * found, the ROAM process is over (ROAM bit cleared)
4768 * 4. If a better network is found, a disassociation request is
4769 * sent.
4770 * 5. When the disassociation completes, the roam work is again
4771 * scheduled. The second time through, the driver is no longer
4772 * associated, and the newly selected network is sent an
Jeff Garzikbf794512005-07-31 13:07:26 -04004773 * association request.
James Ketrenos43f66a62005-03-25 12:31:53 -06004774 * 6. At this point ,the roaming process is complete and the ROAM
4775 * status bit is cleared.
4776 */
4777
4778 /* If we are no longer associated, and the roaming bit is no longer
4779 * set, then we are not actively roaming, so just return */
4780 if (!(priv->status & (STATUS_ASSOCIATED | STATUS_ROAMING)))
4781 return;
Jeff Garzikbf794512005-07-31 13:07:26 -04004782
James Ketrenos43f66a62005-03-25 12:31:53 -06004783 if (priv->status & STATUS_ASSOCIATED) {
Jeff Garzikbf794512005-07-31 13:07:26 -04004784 /* First pass through ROAM process -- look for a better
James Ketrenos43f66a62005-03-25 12:31:53 -06004785 * network */
4786 u8 rssi = priv->assoc_network->stats.rssi;
4787 priv->assoc_network->stats.rssi = -128;
4788 list_for_each_entry(network, &priv->ieee->network_list, list) {
4789 if (network != priv->assoc_network)
4790 ipw_best_network(priv, &match, network, 1);
4791 }
4792 priv->assoc_network->stats.rssi = rssi;
Jeff Garzikbf794512005-07-31 13:07:26 -04004793
James Ketrenos43f66a62005-03-25 12:31:53 -06004794 if (match.network == priv->assoc_network) {
4795 IPW_DEBUG_ASSOC("No better APs in this network to "
4796 "roam to.\n");
4797 priv->status &= ~STATUS_ROAMING;
4798 ipw_debug_config(priv);
4799 return;
4800 }
Jeff Garzikbf794512005-07-31 13:07:26 -04004801
James Ketrenos43f66a62005-03-25 12:31:53 -06004802 ipw_send_disassociate(priv, 1);
4803 priv->assoc_network = match.network;
4804
4805 return;
Jeff Garzikbf794512005-07-31 13:07:26 -04004806 }
James Ketrenos43f66a62005-03-25 12:31:53 -06004807
4808 /* Second pass through ROAM process -- request association */
4809 ipw_compatible_rates(priv, priv->assoc_network, &match.rates);
4810 ipw_associate_network(priv, priv->assoc_network, &match.rates, 1);
4811 priv->status &= ~STATUS_ROAMING;
4812}
4813
4814static void ipw_associate(void *data)
4815{
4816 struct ipw_priv *priv = data;
4817
4818 struct ieee80211_network *network = NULL;
4819 struct ipw_network_match match = {
4820 .network = NULL
4821 };
4822 struct ipw_supported_rates *rates;
4823 struct list_head *element;
4824
4825 if (!(priv->config & CFG_ASSOCIATE) &&
4826 !(priv->config & (CFG_STATIC_ESSID |
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004827 CFG_STATIC_CHANNEL | CFG_STATIC_BSSID))) {
James Ketrenos43f66a62005-03-25 12:31:53 -06004828 IPW_DEBUG_ASSOC("Not attempting association (associate=0)\n");
4829 return;
4830 }
4831
Jeff Garzikbf794512005-07-31 13:07:26 -04004832 list_for_each_entry(network, &priv->ieee->network_list, list)
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004833 ipw_best_network(priv, &match, network, 0);
James Ketrenos43f66a62005-03-25 12:31:53 -06004834
4835 network = match.network;
4836 rates = &match.rates;
4837
4838 if (network == NULL &&
4839 priv->ieee->iw_mode == IW_MODE_ADHOC &&
4840 priv->config & CFG_ADHOC_CREATE &&
4841 priv->config & CFG_STATIC_ESSID &&
4842 !list_empty(&priv->ieee->network_free_list)) {
4843 element = priv->ieee->network_free_list.next;
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004844 network = list_entry(element, struct ieee80211_network, list);
James Ketrenos43f66a62005-03-25 12:31:53 -06004845 ipw_adhoc_create(priv, network);
4846 rates = &priv->rates;
4847 list_del(element);
4848 list_add_tail(&network->list, &priv->ieee->network_list);
4849 }
Jeff Garzikbf794512005-07-31 13:07:26 -04004850
James Ketrenos43f66a62005-03-25 12:31:53 -06004851 /* If we reached the end of the list, then we don't have any valid
4852 * matching APs */
4853 if (!network) {
4854 ipw_debug_config(priv);
4855
Jeff Garzikbf794512005-07-31 13:07:26 -04004856 queue_delayed_work(priv->workqueue, &priv->request_scan,
James Ketrenos43f66a62005-03-25 12:31:53 -06004857 SCAN_INTERVAL);
Jeff Garzikbf794512005-07-31 13:07:26 -04004858
James Ketrenos43f66a62005-03-25 12:31:53 -06004859 return;
4860 }
4861
4862 ipw_associate_network(priv, network, rates, 0);
4863}
Jeff Garzikbf794512005-07-31 13:07:26 -04004864
4865static inline void ipw_handle_data_packet(struct ipw_priv *priv,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004866 struct ipw_rx_mem_buffer *rxb,
4867 struct ieee80211_rx_stats *stats)
James Ketrenos43f66a62005-03-25 12:31:53 -06004868{
4869 struct ipw_rx_packet *pkt = (struct ipw_rx_packet *)rxb->skb->data;
4870
4871 /* We received data from the HW, so stop the watchdog */
4872 priv->net_dev->trans_start = jiffies;
4873
Jeff Garzikbf794512005-07-31 13:07:26 -04004874 /* We only process data packets if the
James Ketrenos43f66a62005-03-25 12:31:53 -06004875 * interface is open */
Jeff Garzikbf794512005-07-31 13:07:26 -04004876 if (unlikely((pkt->u.frame.length + IPW_RX_FRAME_SIZE) >
James Ketrenos43f66a62005-03-25 12:31:53 -06004877 skb_tailroom(rxb->skb))) {
4878 priv->ieee->stats.rx_errors++;
4879 priv->wstats.discard.misc++;
4880 IPW_DEBUG_DROP("Corruption detected! Oh no!\n");
4881 return;
4882 } else if (unlikely(!netif_running(priv->net_dev))) {
4883 priv->ieee->stats.rx_dropped++;
4884 priv->wstats.discard.misc++;
4885 IPW_DEBUG_DROP("Dropping packet while interface is not up.\n");
4886 return;
4887 }
4888
4889 /* Advance skb->data to the start of the actual payload */
Jiri Bencaaa4d302005-06-07 14:58:41 +02004890 skb_reserve(rxb->skb, offsetof(struct ipw_rx_packet, u.frame.data));
James Ketrenos43f66a62005-03-25 12:31:53 -06004891
4892 /* Set the size of the skb to the size of the frame */
4893 skb_put(rxb->skb, pkt->u.frame.length);
4894
4895 IPW_DEBUG_RX("Rx packet of %d bytes.\n", rxb->skb->len);
4896
Jeff Garzikbf794512005-07-31 13:07:26 -04004897 if (!ieee80211_rx(priv->ieee, rxb->skb, stats))
James Ketrenos43f66a62005-03-25 12:31:53 -06004898 priv->ieee->stats.rx_errors++;
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004899 else /* ieee80211_rx succeeded, so it now owns the SKB */
James Ketrenos43f66a62005-03-25 12:31:53 -06004900 rxb->skb = NULL;
4901}
4902
James Ketrenos43f66a62005-03-25 12:31:53 -06004903/*
4904 * Main entry function for recieving a packet with 80211 headers. This
4905 * should be called when ever the FW has notified us that there is a new
4906 * skb in the recieve queue.
4907 */
4908static void ipw_rx(struct ipw_priv *priv)
4909{
4910 struct ipw_rx_mem_buffer *rxb;
4911 struct ipw_rx_packet *pkt;
James Ketrenos0dacca12005-09-21 12:23:41 -05004912 struct ieee80211_hdr_4addr *header;
James Ketrenos43f66a62005-03-25 12:31:53 -06004913 u32 r, w, i;
4914 u8 network_packet;
4915
4916 r = ipw_read32(priv, CX2_RX_READ_INDEX);
4917 w = ipw_read32(priv, CX2_RX_WRITE_INDEX);
4918 i = (priv->rxq->processed + 1) % RX_QUEUE_SIZE;
4919
4920 while (i != r) {
4921 rxb = priv->rxq->queue[i];
4922#ifdef CONFIG_IPW_DEBUG
4923 if (unlikely(rxb == NULL)) {
4924 printk(KERN_CRIT "Queue not allocated!\n");
4925 break;
4926 }
4927#endif
4928 priv->rxq->queue[i] = NULL;
4929
4930 pci_dma_sync_single_for_cpu(priv->pci_dev, rxb->dma_addr,
Jeff Garzikbf794512005-07-31 13:07:26 -04004931 CX2_RX_BUF_SIZE,
James Ketrenos43f66a62005-03-25 12:31:53 -06004932 PCI_DMA_FROMDEVICE);
4933
4934 pkt = (struct ipw_rx_packet *)rxb->skb->data;
4935 IPW_DEBUG_RX("Packet: type=%02X seq=%02X bits=%02X\n",
4936 pkt->header.message_type,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004937 pkt->header.rx_seq_num, pkt->header.control_bits);
James Ketrenos43f66a62005-03-25 12:31:53 -06004938
4939 switch (pkt->header.message_type) {
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004940 case RX_FRAME_TYPE: /* 802.11 frame */ {
4941 struct ieee80211_rx_stats stats = {
4942 .rssi = pkt->u.frame.rssi_dbm -
4943 IPW_RSSI_TO_DBM,
4944 .signal = pkt->u.frame.signal,
4945 .rate = pkt->u.frame.rate,
4946 .mac_time = jiffies,
4947 .received_channel =
4948 pkt->u.frame.received_channel,
4949 .freq =
4950 (pkt->u.frame.
4951 control & (1 << 0)) ?
4952 IEEE80211_24GHZ_BAND :
4953 IEEE80211_52GHZ_BAND,
4954 .len = pkt->u.frame.length,
4955 };
James Ketrenos43f66a62005-03-25 12:31:53 -06004956
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004957 if (stats.rssi != 0)
4958 stats.mask |= IEEE80211_STATMASK_RSSI;
4959 if (stats.signal != 0)
4960 stats.mask |= IEEE80211_STATMASK_SIGNAL;
4961 if (stats.rate != 0)
4962 stats.mask |= IEEE80211_STATMASK_RATE;
James Ketrenos43f66a62005-03-25 12:31:53 -06004963
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004964 priv->rx_packets++;
James Ketrenos43f66a62005-03-25 12:31:53 -06004965
4966#ifdef CONFIG_IPW_PROMISC
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004967 if (priv->ieee->iw_mode == IW_MODE_MONITOR) {
4968 ipw_handle_data_packet(priv, rxb,
4969 &stats);
4970 break;
4971 }
James Ketrenos43f66a62005-03-25 12:31:53 -06004972#endif
Jeff Garzikbf794512005-07-31 13:07:26 -04004973
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004974 header =
James Ketrenos0dacca12005-09-21 12:23:41 -05004975 (struct ieee80211_hdr_4addr *)(rxb->skb->
4976 data +
4977 IPW_RX_FRAME_SIZE);
James Ketrenos43f66a62005-03-25 12:31:53 -06004978 /* TODO: Check Ad-Hoc dest/source and make sure
4979 * that we are actually parsing these packets
Jeff Garzikbf794512005-07-31 13:07:26 -04004980 * correctly -- we should probably use the
James Ketrenos43f66a62005-03-25 12:31:53 -06004981 * frame control of the packet and disregard
4982 * the current iw_mode */
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004983 switch (priv->ieee->iw_mode) {
4984 case IW_MODE_ADHOC:
4985 network_packet =
4986 !memcmp(header->addr1,
4987 priv->net_dev->dev_addr,
4988 ETH_ALEN) ||
4989 !memcmp(header->addr3,
4990 priv->bssid, ETH_ALEN) ||
4991 is_broadcast_ether_addr(header->
4992 addr1)
4993 || is_multicast_ether_addr(header->
4994 addr1);
4995 break;
James Ketrenos43f66a62005-03-25 12:31:53 -06004996
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004997 case IW_MODE_INFRA:
4998 default:
4999 network_packet =
5000 !memcmp(header->addr3,
5001 priv->bssid, ETH_ALEN) ||
5002 !memcmp(header->addr1,
5003 priv->net_dev->dev_addr,
5004 ETH_ALEN) ||
5005 is_broadcast_ether_addr(header->
5006 addr1)
5007 || is_multicast_ether_addr(header->
5008 addr1);
5009 break;
5010 }
5011
5012 if (network_packet && priv->assoc_network) {
5013 priv->assoc_network->stats.rssi =
5014 stats.rssi;
5015 average_add(&priv->average_rssi,
5016 stats.rssi);
5017 priv->last_rx_rssi = stats.rssi;
5018 }
5019
5020 IPW_DEBUG_RX("Frame: len=%u\n",
5021 pkt->u.frame.length);
5022
5023 if (pkt->u.frame.length < frame_hdr_len(header)) {
5024 IPW_DEBUG_DROP
5025 ("Received packet is too small. "
5026 "Dropping.\n");
5027 priv->ieee->stats.rx_errors++;
5028 priv->wstats.discard.misc++;
5029 break;
5030 }
5031
5032 switch (WLAN_FC_GET_TYPE(header->frame_ctl)) {
5033 case IEEE80211_FTYPE_MGMT:
5034 ieee80211_rx_mgt(priv->ieee, header,
5035 &stats);
5036 if (priv->ieee->iw_mode == IW_MODE_ADHOC
5037 &&
5038 ((WLAN_FC_GET_STYPE
5039 (header->frame_ctl) ==
5040 IEEE80211_STYPE_PROBE_RESP)
5041 ||
5042 (WLAN_FC_GET_STYPE
5043 (header->frame_ctl) ==
5044 IEEE80211_STYPE_BEACON))
5045 && !memcmp(header->addr3,
5046 priv->bssid, ETH_ALEN))
5047 ipw_add_station(priv,
5048 header->addr2);
5049 break;
5050
5051 case IEEE80211_FTYPE_CTL:
5052 break;
5053
5054 case IEEE80211_FTYPE_DATA:
5055 if (network_packet)
5056 ipw_handle_data_packet(priv,
5057 rxb,
5058 &stats);
5059 else
5060 IPW_DEBUG_DROP("Dropping: "
5061 MAC_FMT ", "
5062 MAC_FMT ", "
5063 MAC_FMT "\n",
5064 MAC_ARG(header->
5065 addr1),
5066 MAC_ARG(header->
5067 addr2),
5068 MAC_ARG(header->
5069 addr3));
5070 break;
5071 }
James Ketrenos43f66a62005-03-25 12:31:53 -06005072 break;
5073 }
Jeff Garzikbf794512005-07-31 13:07:26 -04005074
Jeff Garzik0edd5b42005-09-07 00:48:31 -04005075 case RX_HOST_NOTIFICATION_TYPE:{
5076 IPW_DEBUG_RX
5077 ("Notification: subtype=%02X flags=%02X size=%d\n",
James Ketrenos43f66a62005-03-25 12:31:53 -06005078 pkt->u.notification.subtype,
5079 pkt->u.notification.flags,
5080 pkt->u.notification.size);
Jeff Garzik0edd5b42005-09-07 00:48:31 -04005081 ipw_rx_notification(priv, &pkt->u.notification);
5082 break;
5083 }
James Ketrenos43f66a62005-03-25 12:31:53 -06005084
5085 default:
5086 IPW_DEBUG_RX("Bad Rx packet of type %d\n",
5087 pkt->header.message_type);
5088 break;
5089 }
Jeff Garzikbf794512005-07-31 13:07:26 -04005090
5091 /* For now we just don't re-use anything. We can tweak this
5092 * later to try and re-use notification packets and SKBs that
James Ketrenos43f66a62005-03-25 12:31:53 -06005093 * fail to Rx correctly */
5094 if (rxb->skb != NULL) {
5095 dev_kfree_skb_any(rxb->skb);
5096 rxb->skb = NULL;
5097 }
Jeff Garzikbf794512005-07-31 13:07:26 -04005098
James Ketrenos43f66a62005-03-25 12:31:53 -06005099 pci_unmap_single(priv->pci_dev, rxb->dma_addr,
5100 CX2_RX_BUF_SIZE, PCI_DMA_FROMDEVICE);
5101 list_add_tail(&rxb->list, &priv->rxq->rx_used);
Jeff Garzikbf794512005-07-31 13:07:26 -04005102
James Ketrenos43f66a62005-03-25 12:31:53 -06005103 i = (i + 1) % RX_QUEUE_SIZE;
5104 }
5105
5106 /* Backtrack one entry */
5107 priv->rxq->processed = (i ? i : RX_QUEUE_SIZE) - 1;
5108
5109 ipw_rx_queue_restock(priv);
5110}
5111
5112static void ipw_abort_scan(struct ipw_priv *priv)
5113{
5114 int err;
5115
5116 if (priv->status & STATUS_SCAN_ABORTING) {
5117 IPW_DEBUG_HC("Ignoring concurrent scan abort request.\n");
5118 return;
5119 }
5120 priv->status |= STATUS_SCAN_ABORTING;
5121
5122 err = ipw_send_scan_abort(priv);
Jeff Garzikbf794512005-07-31 13:07:26 -04005123 if (err)
James Ketrenos43f66a62005-03-25 12:31:53 -06005124 IPW_DEBUG_HC("Request to abort scan failed.\n");
5125}
5126
5127static int ipw_request_scan(struct ipw_priv *priv)
5128{
5129 struct ipw_scan_request_ext scan;
5130 int channel_index = 0;
5131 int i, err, scan_type;
Jeff Garzikbf794512005-07-31 13:07:26 -04005132
James Ketrenos43f66a62005-03-25 12:31:53 -06005133 if (priv->status & STATUS_EXIT_PENDING) {
5134 IPW_DEBUG_SCAN("Aborting scan due to device shutdown\n");
5135 priv->status |= STATUS_SCAN_PENDING;
5136 return 0;
5137 }
5138
5139 if (priv->status & STATUS_SCANNING) {
5140 IPW_DEBUG_HC("Concurrent scan requested. Aborting first.\n");
5141 priv->status |= STATUS_SCAN_PENDING;
5142 ipw_abort_scan(priv);
5143 return 0;
5144 }
Jeff Garzikbf794512005-07-31 13:07:26 -04005145
James Ketrenos43f66a62005-03-25 12:31:53 -06005146 if (priv->status & STATUS_SCAN_ABORTING) {
5147 IPW_DEBUG_HC("Scan request while abort pending. Queuing.\n");
5148 priv->status |= STATUS_SCAN_PENDING;
5149 return 0;
5150 }
5151
5152 if (priv->status & STATUS_RF_KILL_MASK) {
5153 IPW_DEBUG_HC("Aborting scan due to RF Kill activation\n");
5154 priv->status |= STATUS_SCAN_PENDING;
5155 return 0;
5156 }
5157
5158 memset(&scan, 0, sizeof(scan));
5159
5160 scan.dwell_time[IPW_SCAN_ACTIVE_BROADCAST_SCAN] = 20;
5161 scan.dwell_time[IPW_SCAN_ACTIVE_BROADCAST_AND_DIRECT_SCAN] = 20;
5162 scan.dwell_time[IPW_SCAN_PASSIVE_FULL_DWELL_SCAN] = 20;
5163
5164 scan.full_scan_index = ieee80211_get_scans(priv->ieee);
5165 /* If we are roaming, then make this a directed scan for the current
Jeff Garzikbf794512005-07-31 13:07:26 -04005166 * network. Otherwise, ensure that every other scan is a fast
James Ketrenos43f66a62005-03-25 12:31:53 -06005167 * channel hop scan */
Jeff Garzik0edd5b42005-09-07 00:48:31 -04005168 if ((priv->status & STATUS_ROAMING)
5169 || (!(priv->status & STATUS_ASSOCIATED)
5170 && (priv->config & CFG_STATIC_ESSID)
5171 && (scan.full_scan_index % 2))) {
James Ketrenos43f66a62005-03-25 12:31:53 -06005172 err = ipw_send_ssid(priv, priv->essid, priv->essid_len);
5173 if (err) {
5174 IPW_DEBUG_HC("Attempt to send SSID command failed.\n");
5175 return err;
5176 }
Jeff Garzikbf794512005-07-31 13:07:26 -04005177
James Ketrenos43f66a62005-03-25 12:31:53 -06005178 scan_type = IPW_SCAN_ACTIVE_BROADCAST_AND_DIRECT_SCAN;
5179 } else {
5180 scan_type = IPW_SCAN_ACTIVE_BROADCAST_SCAN;
5181 }
Jeff Garzikbf794512005-07-31 13:07:26 -04005182
Jeff Garzik0edd5b42005-09-07 00:48:31 -04005183 if (priv->ieee->freq_band & IEEE80211_52GHZ_BAND) {
James Ketrenos43f66a62005-03-25 12:31:53 -06005184 int start = channel_index;
5185 for (i = 0; i < MAX_A_CHANNELS; i++) {
5186 if (band_a_active_channel[i] == 0)
5187 break;
5188 if ((priv->status & STATUS_ASSOCIATED) &&
5189 band_a_active_channel[i] == priv->channel)
5190 continue;
5191 channel_index++;
Jeff Garzikbf794512005-07-31 13:07:26 -04005192 scan.channels_list[channel_index] =
Jeff Garzik0edd5b42005-09-07 00:48:31 -04005193 band_a_active_channel[i];
James Ketrenos43f66a62005-03-25 12:31:53 -06005194 ipw_set_scan_type(&scan, channel_index, scan_type);
5195 }
Jeff Garzikbf794512005-07-31 13:07:26 -04005196
James Ketrenos43f66a62005-03-25 12:31:53 -06005197 if (start != channel_index) {
Jeff Garzik0edd5b42005-09-07 00:48:31 -04005198 scan.channels_list[start] = (u8) (IPW_A_MODE << 6) |
5199 (channel_index - start);
James Ketrenos43f66a62005-03-25 12:31:53 -06005200 channel_index++;
5201 }
5202 }
5203
Jeff Garzik0edd5b42005-09-07 00:48:31 -04005204 if (priv->ieee->freq_band & IEEE80211_24GHZ_BAND) {
James Ketrenos43f66a62005-03-25 12:31:53 -06005205 int start = channel_index;
5206 for (i = 0; i < MAX_B_CHANNELS; i++) {
5207 if (band_b_active_channel[i] == 0)
5208 break;
5209 if ((priv->status & STATUS_ASSOCIATED) &&
5210 band_b_active_channel[i] == priv->channel)
5211 continue;
5212 channel_index++;
Jeff Garzikbf794512005-07-31 13:07:26 -04005213 scan.channels_list[channel_index] =
Jeff Garzik0edd5b42005-09-07 00:48:31 -04005214 band_b_active_channel[i];
James Ketrenos43f66a62005-03-25 12:31:53 -06005215 ipw_set_scan_type(&scan, channel_index, scan_type);
5216 }
5217
5218 if (start != channel_index) {
Jeff Garzik0edd5b42005-09-07 00:48:31 -04005219 scan.channels_list[start] = (u8) (IPW_B_MODE << 6) |
5220 (channel_index - start);
James Ketrenos43f66a62005-03-25 12:31:53 -06005221 }
5222 }
Jeff Garzikbf794512005-07-31 13:07:26 -04005223
James Ketrenos43f66a62005-03-25 12:31:53 -06005224 err = ipw_send_scan_request_ext(priv, &scan);
5225 if (err) {
Jeff Garzik0edd5b42005-09-07 00:48:31 -04005226 IPW_DEBUG_HC("Sending scan command failed: %08X\n", err);
James Ketrenos43f66a62005-03-25 12:31:53 -06005227 return -EIO;
5228 }
5229
5230 priv->status |= STATUS_SCANNING;
5231 priv->status &= ~STATUS_SCAN_PENDING;
5232
5233 return 0;
5234}
5235
5236/*
5237 * This file defines the Wireless Extension handlers. It does not
5238 * define any methods of hardware manipulation and relies on the
5239 * functions defined in ipw_main to provide the HW interaction.
Jeff Garzikbf794512005-07-31 13:07:26 -04005240 *
5241 * The exception to this is the use of the ipw_get_ordinal()
James Ketrenos43f66a62005-03-25 12:31:53 -06005242 * function used to poll the hardware vs. making unecessary calls.
5243 *
5244 */
5245
Jeff Garzikbf794512005-07-31 13:07:26 -04005246static int ipw_wx_get_name(struct net_device *dev,
5247 struct iw_request_info *info,
James Ketrenos43f66a62005-03-25 12:31:53 -06005248 union iwreq_data *wrqu, char *extra)
5249{
5250 struct ipw_priv *priv = ieee80211_priv(dev);
5251 if (!(priv->status & STATUS_ASSOCIATED))
5252 strcpy(wrqu->name, "unassociated");
Jeff Garzikbf794512005-07-31 13:07:26 -04005253 else
James Ketrenos43f66a62005-03-25 12:31:53 -06005254 snprintf(wrqu->name, IFNAMSIZ, "IEEE 802.11%c",
5255 ipw_modes[priv->assoc_request.ieee_mode]);
5256 IPW_DEBUG_WX("Name: %s\n", wrqu->name);
5257 return 0;
5258}
5259
5260static int ipw_set_channel(struct ipw_priv *priv, u8 channel)
5261{
5262 if (channel == 0) {
5263 IPW_DEBUG_INFO("Setting channel to ANY (0)\n");
5264 priv->config &= ~CFG_STATIC_CHANNEL;
5265 if (!(priv->status & (STATUS_SCANNING | STATUS_ASSOCIATED |
5266 STATUS_ASSOCIATING))) {
5267 IPW_DEBUG_ASSOC("Attempting to associate with new "
5268 "parameters.\n");
5269 ipw_associate(priv);
5270 }
5271
5272 return 0;
5273 }
5274
5275 priv->config |= CFG_STATIC_CHANNEL;
5276
5277 if (priv->channel == channel) {
Jeff Garzik0edd5b42005-09-07 00:48:31 -04005278 IPW_DEBUG_INFO("Request to set channel to current value (%d)\n",
5279 channel);
James Ketrenos43f66a62005-03-25 12:31:53 -06005280 return 0;
5281 }
5282
5283 IPW_DEBUG_INFO("Setting channel to %i\n", (int)channel);
5284 priv->channel = channel;
5285
5286 /* If we are currently associated, or trying to associate
5287 * then see if this is a new channel (causing us to disassociate) */
5288 if (priv->status & (STATUS_ASSOCIATED | STATUS_ASSOCIATING)) {
5289 IPW_DEBUG_ASSOC("Disassociating due to channel change.\n");
5290 ipw_disassociate(priv);
5291 } else {
5292 ipw_associate(priv);
5293 }
5294
5295 return 0;
5296}
5297
Jeff Garzikbf794512005-07-31 13:07:26 -04005298static int ipw_wx_set_freq(struct net_device *dev,
5299 struct iw_request_info *info,
5300 union iwreq_data *wrqu, char *extra)
James Ketrenos43f66a62005-03-25 12:31:53 -06005301{
5302 struct ipw_priv *priv = ieee80211_priv(dev);
5303 struct iw_freq *fwrq = &wrqu->freq;
Jeff Garzikbf794512005-07-31 13:07:26 -04005304
James Ketrenos43f66a62005-03-25 12:31:53 -06005305 /* if setting by freq convert to channel */
5306 if (fwrq->e == 1) {
Jeff Garzik0edd5b42005-09-07 00:48:31 -04005307 if ((fwrq->m >= (int)2.412e8 && fwrq->m <= (int)2.487e8)) {
James Ketrenos43f66a62005-03-25 12:31:53 -06005308 int f = fwrq->m / 100000;
5309 int c = 0;
Jeff Garzikbf794512005-07-31 13:07:26 -04005310
James Ketrenos43f66a62005-03-25 12:31:53 -06005311 while ((c < REG_MAX_CHANNEL) &&
5312 (f != ipw_frequencies[c]))
5313 c++;
Jeff Garzikbf794512005-07-31 13:07:26 -04005314
James Ketrenos43f66a62005-03-25 12:31:53 -06005315 /* hack to fall through */
5316 fwrq->e = 0;
5317 fwrq->m = c + 1;
5318 }
5319 }
Jeff Garzikbf794512005-07-31 13:07:26 -04005320
5321 if (fwrq->e > 0 || fwrq->m > 1000)
James Ketrenos43f66a62005-03-25 12:31:53 -06005322 return -EOPNOTSUPP;
5323
5324 IPW_DEBUG_WX("SET Freq/Channel -> %d \n", fwrq->m);
Jeff Garzik0edd5b42005-09-07 00:48:31 -04005325 return ipw_set_channel(priv, (u8) fwrq->m);
James Ketrenos43f66a62005-03-25 12:31:53 -06005326}
5327
Jeff Garzikbf794512005-07-31 13:07:26 -04005328static int ipw_wx_get_freq(struct net_device *dev,
5329 struct iw_request_info *info,
James Ketrenos43f66a62005-03-25 12:31:53 -06005330 union iwreq_data *wrqu, char *extra)
5331{
5332 struct ipw_priv *priv = ieee80211_priv(dev);
5333
5334 wrqu->freq.e = 0;
5335
5336 /* If we are associated, trying to associate, or have a statically
5337 * configured CHANNEL then return that; otherwise return ANY */
5338 if (priv->config & CFG_STATIC_CHANNEL ||
5339 priv->status & (STATUS_ASSOCIATING | STATUS_ASSOCIATED))
5340 wrqu->freq.m = priv->channel;
Jeff Garzikbf794512005-07-31 13:07:26 -04005341 else
James Ketrenos43f66a62005-03-25 12:31:53 -06005342 wrqu->freq.m = 0;
5343
5344 IPW_DEBUG_WX("GET Freq/Channel -> %d \n", priv->channel);
5345 return 0;
5346}
5347
Jeff Garzikbf794512005-07-31 13:07:26 -04005348static int ipw_wx_set_mode(struct net_device *dev,
5349 struct iw_request_info *info,
James Ketrenos43f66a62005-03-25 12:31:53 -06005350 union iwreq_data *wrqu, char *extra)
5351{
5352 struct ipw_priv *priv = ieee80211_priv(dev);
5353 int err = 0;
5354
5355 IPW_DEBUG_WX("Set MODE: %d\n", wrqu->mode);
5356
5357 if (wrqu->mode == priv->ieee->iw_mode)
5358 return 0;
5359
5360 switch (wrqu->mode) {
5361#ifdef CONFIG_IPW_PROMISC
5362 case IW_MODE_MONITOR:
5363#endif
5364 case IW_MODE_ADHOC:
5365 case IW_MODE_INFRA:
5366 break;
5367 case IW_MODE_AUTO:
5368 wrqu->mode = IW_MODE_INFRA;
5369 break;
5370 default:
5371 return -EINVAL;
5372 }
5373
5374#ifdef CONFIG_IPW_PROMISC
Jeff Garzikbf794512005-07-31 13:07:26 -04005375 if (priv->ieee->iw_mode == IW_MODE_MONITOR)
James Ketrenos43f66a62005-03-25 12:31:53 -06005376 priv->net_dev->type = ARPHRD_ETHER;
Jeff Garzikbf794512005-07-31 13:07:26 -04005377
5378 if (wrqu->mode == IW_MODE_MONITOR)
James Ketrenos43f66a62005-03-25 12:31:53 -06005379 priv->net_dev->type = ARPHRD_IEEE80211;
Jeff Garzik0edd5b42005-09-07 00:48:31 -04005380#endif /* CONFIG_IPW_PROMISC */
Jeff Garzikbf794512005-07-31 13:07:26 -04005381
James Ketrenos43f66a62005-03-25 12:31:53 -06005382#ifdef CONFIG_PM
Jeff Garzikbf794512005-07-31 13:07:26 -04005383 /* Free the existing firmware and reset the fw_loaded
James Ketrenos43f66a62005-03-25 12:31:53 -06005384 * flag so ipw_load() will bring in the new firmawre */
5385 if (fw_loaded) {
5386 fw_loaded = 0;
5387 }
5388
5389 release_firmware(bootfw);
5390 release_firmware(ucode);
5391 release_firmware(firmware);
5392 bootfw = ucode = firmware = NULL;
5393#endif
5394
5395 priv->ieee->iw_mode = wrqu->mode;
5396 ipw_adapter_restart(priv);
Jeff Garzikbf794512005-07-31 13:07:26 -04005397
Jeff Garzik0edd5b42005-09-07 00:48:31 -04005398 return err;
James Ketrenos43f66a62005-03-25 12:31:53 -06005399}
5400
Jeff Garzikbf794512005-07-31 13:07:26 -04005401static int ipw_wx_get_mode(struct net_device *dev,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04005402 struct iw_request_info *info,
5403 union iwreq_data *wrqu, char *extra)
James Ketrenos43f66a62005-03-25 12:31:53 -06005404{
5405 struct ipw_priv *priv = ieee80211_priv(dev);
5406
5407 wrqu->mode = priv->ieee->iw_mode;
5408 IPW_DEBUG_WX("Get MODE -> %d\n", wrqu->mode);
5409
5410 return 0;
5411}
5412
James Ketrenos43f66a62005-03-25 12:31:53 -06005413#define DEFAULT_RTS_THRESHOLD 2304U
5414#define MIN_RTS_THRESHOLD 1U
5415#define MAX_RTS_THRESHOLD 2304U
5416#define DEFAULT_BEACON_INTERVAL 100U
5417#define DEFAULT_SHORT_RETRY_LIMIT 7U
5418#define DEFAULT_LONG_RETRY_LIMIT 4U
5419
5420/* Values are in microsecond */
5421static const s32 timeout_duration[] = {
5422 350000,
5423 250000,
5424 75000,
5425 37000,
5426 25000,
5427};
5428
5429static const s32 period_duration[] = {
5430 400000,
5431 700000,
5432 1000000,
5433 1000000,
5434 1000000
5435};
5436
Jeff Garzikbf794512005-07-31 13:07:26 -04005437static int ipw_wx_get_range(struct net_device *dev,
5438 struct iw_request_info *info,
James Ketrenos43f66a62005-03-25 12:31:53 -06005439 union iwreq_data *wrqu, char *extra)
5440{
5441 struct ipw_priv *priv = ieee80211_priv(dev);
5442 struct iw_range *range = (struct iw_range *)extra;
5443 u16 val;
5444 int i;
5445
5446 wrqu->data.length = sizeof(*range);
5447 memset(range, 0, sizeof(*range));
5448
5449 /* 54Mbs == ~27 Mb/s real (802.11g) */
Jeff Garzikbf794512005-07-31 13:07:26 -04005450 range->throughput = 27 * 1000 * 1000;
James Ketrenos43f66a62005-03-25 12:31:53 -06005451
5452 range->max_qual.qual = 100;
5453 /* TODO: Find real max RSSI and stick here */
5454 range->max_qual.level = 0;
5455 range->max_qual.noise = 0;
Jeff Garzik0edd5b42005-09-07 00:48:31 -04005456 range->max_qual.updated = 7; /* Updated all three */
James Ketrenos43f66a62005-03-25 12:31:53 -06005457
5458 range->avg_qual.qual = 70;
5459 /* TODO: Find real 'good' to 'bad' threshol value for RSSI */
Jeff Garzik0edd5b42005-09-07 00:48:31 -04005460 range->avg_qual.level = 0; /* FIXME to real average level */
James Ketrenos43f66a62005-03-25 12:31:53 -06005461 range->avg_qual.noise = 0;
Jeff Garzik0edd5b42005-09-07 00:48:31 -04005462 range->avg_qual.updated = 7; /* Updated all three */
James Ketrenos43f66a62005-03-25 12:31:53 -06005463
Jeff Garzik0edd5b42005-09-07 00:48:31 -04005464 range->num_bitrates = min(priv->rates.num_rates, (u8) IW_MAX_BITRATES);
James Ketrenos43f66a62005-03-25 12:31:53 -06005465
Jeff Garzikbf794512005-07-31 13:07:26 -04005466 for (i = 0; i < range->num_bitrates; i++)
5467 range->bitrate[i] = (priv->rates.supported_rates[i] & 0x7F) *
Jeff Garzik0edd5b42005-09-07 00:48:31 -04005468 500000;
Jeff Garzikbf794512005-07-31 13:07:26 -04005469
James Ketrenos43f66a62005-03-25 12:31:53 -06005470 range->max_rts = DEFAULT_RTS_THRESHOLD;
5471 range->min_frag = MIN_FRAG_THRESHOLD;
5472 range->max_frag = MAX_FRAG_THRESHOLD;
5473
5474 range->encoding_size[0] = 5;
Jeff Garzikbf794512005-07-31 13:07:26 -04005475 range->encoding_size[1] = 13;
James Ketrenos43f66a62005-03-25 12:31:53 -06005476 range->num_encoding_sizes = 2;
5477 range->max_encoding_tokens = WEP_KEYS;
5478
5479 /* Set the Wireless Extension versions */
5480 range->we_version_compiled = WIRELESS_EXT;
5481 range->we_version_source = 16;
5482
Jeff Garzik0edd5b42005-09-07 00:48:31 -04005483 range->num_channels = FREQ_COUNT;
James Ketrenos43f66a62005-03-25 12:31:53 -06005484
5485 val = 0;
5486 for (i = 0; i < FREQ_COUNT; i++) {
5487 range->freq[val].i = i + 1;
5488 range->freq[val].m = ipw_frequencies[i] * 100000;
5489 range->freq[val].e = 1;
5490 val++;
5491
5492 if (val == IW_MAX_FREQUENCIES)
5493 break;
5494 }
5495 range->num_frequency = val;
5496
5497 IPW_DEBUG_WX("GET Range\n");
5498 return 0;
5499}
5500
Jeff Garzikbf794512005-07-31 13:07:26 -04005501static int ipw_wx_set_wap(struct net_device *dev,
5502 struct iw_request_info *info,
James Ketrenos43f66a62005-03-25 12:31:53 -06005503 union iwreq_data *wrqu, char *extra)
5504{
5505 struct ipw_priv *priv = ieee80211_priv(dev);
5506
5507 static const unsigned char any[] = {
5508 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
5509 };
5510 static const unsigned char off[] = {
5511 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
5512 };
5513
Jeff Garzikbf794512005-07-31 13:07:26 -04005514 if (wrqu->ap_addr.sa_family != ARPHRD_ETHER)
James Ketrenos43f66a62005-03-25 12:31:53 -06005515 return -EINVAL;
5516
5517 if (!memcmp(any, wrqu->ap_addr.sa_data, ETH_ALEN) ||
5518 !memcmp(off, wrqu->ap_addr.sa_data, ETH_ALEN)) {
5519 /* we disable mandatory BSSID association */
5520 IPW_DEBUG_WX("Setting AP BSSID to ANY\n");
5521 priv->config &= ~CFG_STATIC_BSSID;
5522 if (!(priv->status & (STATUS_SCANNING | STATUS_ASSOCIATED |
5523 STATUS_ASSOCIATING))) {
5524 IPW_DEBUG_ASSOC("Attempting to associate with new "
5525 "parameters.\n");
5526 ipw_associate(priv);
5527 }
5528
5529 return 0;
5530 }
5531
5532 priv->config |= CFG_STATIC_BSSID;
5533 if (!memcmp(priv->bssid, wrqu->ap_addr.sa_data, ETH_ALEN)) {
5534 IPW_DEBUG_WX("BSSID set to current BSSID.\n");
5535 return 0;
5536 }
5537
5538 IPW_DEBUG_WX("Setting mandatory BSSID to " MAC_FMT "\n",
5539 MAC_ARG(wrqu->ap_addr.sa_data));
5540
5541 memcpy(priv->bssid, wrqu->ap_addr.sa_data, ETH_ALEN);
5542
5543 /* If we are currently associated, or trying to associate
5544 * then see if this is a new BSSID (causing us to disassociate) */
5545 if (priv->status & (STATUS_ASSOCIATED | STATUS_ASSOCIATING)) {
5546 IPW_DEBUG_ASSOC("Disassociating due to BSSID change.\n");
5547 ipw_disassociate(priv);
5548 } else {
5549 ipw_associate(priv);
5550 }
5551
5552 return 0;
5553}
5554
Jeff Garzikbf794512005-07-31 13:07:26 -04005555static int ipw_wx_get_wap(struct net_device *dev,
5556 struct iw_request_info *info,
James Ketrenos43f66a62005-03-25 12:31:53 -06005557 union iwreq_data *wrqu, char *extra)
5558{
5559 struct ipw_priv *priv = ieee80211_priv(dev);
5560 /* If we are associated, trying to associate, or have a statically
5561 * configured BSSID then return that; otherwise return ANY */
Jeff Garzikbf794512005-07-31 13:07:26 -04005562 if (priv->config & CFG_STATIC_BSSID ||
James Ketrenos43f66a62005-03-25 12:31:53 -06005563 priv->status & (STATUS_ASSOCIATED | STATUS_ASSOCIATING)) {
5564 wrqu->ap_addr.sa_family = ARPHRD_ETHER;
5565 memcpy(wrqu->ap_addr.sa_data, &priv->bssid, ETH_ALEN);
5566 } else
5567 memset(wrqu->ap_addr.sa_data, 0, ETH_ALEN);
5568
5569 IPW_DEBUG_WX("Getting WAP BSSID: " MAC_FMT "\n",
5570 MAC_ARG(wrqu->ap_addr.sa_data));
5571 return 0;
5572}
5573
Jeff Garzikbf794512005-07-31 13:07:26 -04005574static int ipw_wx_set_essid(struct net_device *dev,
5575 struct iw_request_info *info,
James Ketrenos43f66a62005-03-25 12:31:53 -06005576 union iwreq_data *wrqu, char *extra)
5577{
5578 struct ipw_priv *priv = ieee80211_priv(dev);
Jeff Garzik0edd5b42005-09-07 00:48:31 -04005579 char *essid = ""; /* ANY */
James Ketrenos43f66a62005-03-25 12:31:53 -06005580 int length = 0;
Jeff Garzikbf794512005-07-31 13:07:26 -04005581
James Ketrenos43f66a62005-03-25 12:31:53 -06005582 if (wrqu->essid.flags && wrqu->essid.length) {
5583 length = wrqu->essid.length - 1;
5584 essid = extra;
5585 }
5586 if (length == 0) {
5587 IPW_DEBUG_WX("Setting ESSID to ANY\n");
5588 priv->config &= ~CFG_STATIC_ESSID;
5589 if (!(priv->status & (STATUS_SCANNING | STATUS_ASSOCIATED |
5590 STATUS_ASSOCIATING))) {
5591 IPW_DEBUG_ASSOC("Attempting to associate with new "
5592 "parameters.\n");
5593 ipw_associate(priv);
5594 }
5595
5596 return 0;
5597 }
5598
5599 length = min(length, IW_ESSID_MAX_SIZE);
5600
5601 priv->config |= CFG_STATIC_ESSID;
5602
5603 if (priv->essid_len == length && !memcmp(priv->essid, extra, length)) {
5604 IPW_DEBUG_WX("ESSID set to current ESSID.\n");
5605 return 0;
5606 }
5607
5608 IPW_DEBUG_WX("Setting ESSID: '%s' (%d)\n", escape_essid(essid, length),
5609 length);
5610
5611 priv->essid_len = length;
5612 memcpy(priv->essid, essid, priv->essid_len);
Jeff Garzikbf794512005-07-31 13:07:26 -04005613
James Ketrenos43f66a62005-03-25 12:31:53 -06005614 /* If we are currently associated, or trying to associate
5615 * then see if this is a new ESSID (causing us to disassociate) */
5616 if (priv->status & (STATUS_ASSOCIATED | STATUS_ASSOCIATING)) {
5617 IPW_DEBUG_ASSOC("Disassociating due to ESSID change.\n");
5618 ipw_disassociate(priv);
5619 } else {
5620 ipw_associate(priv);
5621 }
5622
5623 return 0;
5624}
5625
Jeff Garzikbf794512005-07-31 13:07:26 -04005626static int ipw_wx_get_essid(struct net_device *dev,
5627 struct iw_request_info *info,
James Ketrenos43f66a62005-03-25 12:31:53 -06005628 union iwreq_data *wrqu, char *extra)
5629{
5630 struct ipw_priv *priv = ieee80211_priv(dev);
5631
5632 /* If we are associated, trying to associate, or have a statically
5633 * configured ESSID then return that; otherwise return ANY */
5634 if (priv->config & CFG_STATIC_ESSID ||
Jeff Garzikbf794512005-07-31 13:07:26 -04005635 priv->status & (STATUS_ASSOCIATED | STATUS_ASSOCIATING)) {
5636 IPW_DEBUG_WX("Getting essid: '%s'\n",
James Ketrenos43f66a62005-03-25 12:31:53 -06005637 escape_essid(priv->essid, priv->essid_len));
Jeff Garzikbf794512005-07-31 13:07:26 -04005638 memcpy(extra, priv->essid, priv->essid_len);
James Ketrenos43f66a62005-03-25 12:31:53 -06005639 wrqu->essid.length = priv->essid_len;
Jeff Garzik0edd5b42005-09-07 00:48:31 -04005640 wrqu->essid.flags = 1; /* active */
James Ketrenos43f66a62005-03-25 12:31:53 -06005641 } else {
5642 IPW_DEBUG_WX("Getting essid: ANY\n");
5643 wrqu->essid.length = 0;
Jeff Garzik0edd5b42005-09-07 00:48:31 -04005644 wrqu->essid.flags = 0; /* active */
James Ketrenos43f66a62005-03-25 12:31:53 -06005645 }
5646
5647 return 0;
5648}
5649
Jeff Garzikbf794512005-07-31 13:07:26 -04005650static int ipw_wx_set_nick(struct net_device *dev,
5651 struct iw_request_info *info,
James Ketrenos43f66a62005-03-25 12:31:53 -06005652 union iwreq_data *wrqu, char *extra)
Jeff Garzikbf794512005-07-31 13:07:26 -04005653{
James Ketrenos43f66a62005-03-25 12:31:53 -06005654 struct ipw_priv *priv = ieee80211_priv(dev);
5655
5656 IPW_DEBUG_WX("Setting nick to '%s'\n", extra);
5657 if (wrqu->data.length > IW_ESSID_MAX_SIZE)
5658 return -E2BIG;
5659
Jeff Garzik0edd5b42005-09-07 00:48:31 -04005660 wrqu->data.length = min((size_t) wrqu->data.length, sizeof(priv->nick));
James Ketrenos43f66a62005-03-25 12:31:53 -06005661 memset(priv->nick, 0, sizeof(priv->nick));
Jeff Garzik0edd5b42005-09-07 00:48:31 -04005662 memcpy(priv->nick, extra, wrqu->data.length);
James Ketrenos43f66a62005-03-25 12:31:53 -06005663 IPW_DEBUG_TRACE("<<\n");
5664 return 0;
5665
5666}
5667
Jeff Garzikbf794512005-07-31 13:07:26 -04005668static int ipw_wx_get_nick(struct net_device *dev,
5669 struct iw_request_info *info,
James Ketrenos43f66a62005-03-25 12:31:53 -06005670 union iwreq_data *wrqu, char *extra)
Jeff Garzikbf794512005-07-31 13:07:26 -04005671{
James Ketrenos43f66a62005-03-25 12:31:53 -06005672 struct ipw_priv *priv = ieee80211_priv(dev);
5673 IPW_DEBUG_WX("Getting nick\n");
5674 wrqu->data.length = strlen(priv->nick) + 1;
5675 memcpy(extra, priv->nick, wrqu->data.length);
Jeff Garzik0edd5b42005-09-07 00:48:31 -04005676 wrqu->data.flags = 1; /* active */
James Ketrenos43f66a62005-03-25 12:31:53 -06005677 return 0;
5678}
5679
James Ketrenos43f66a62005-03-25 12:31:53 -06005680static int ipw_wx_set_rate(struct net_device *dev,
5681 struct iw_request_info *info,
5682 union iwreq_data *wrqu, char *extra)
Jeff Garzikbf794512005-07-31 13:07:26 -04005683{
James Ketrenos43f66a62005-03-25 12:31:53 -06005684 IPW_DEBUG_WX("0x%p, 0x%p, 0x%p\n", dev, info, wrqu);
Jeff Garzikbf794512005-07-31 13:07:26 -04005685 return -EOPNOTSUPP;
James Ketrenos43f66a62005-03-25 12:31:53 -06005686}
5687
Jeff Garzikbf794512005-07-31 13:07:26 -04005688static int ipw_wx_get_rate(struct net_device *dev,
5689 struct iw_request_info *info,
James Ketrenos43f66a62005-03-25 12:31:53 -06005690 union iwreq_data *wrqu, char *extra)
Jeff Garzikbf794512005-07-31 13:07:26 -04005691{
Jeff Garzik0edd5b42005-09-07 00:48:31 -04005692 struct ipw_priv *priv = ieee80211_priv(dev);
James Ketrenos43f66a62005-03-25 12:31:53 -06005693 wrqu->bitrate.value = priv->last_rate;
5694
5695 IPW_DEBUG_WX("GET Rate -> %d \n", wrqu->bitrate.value);
5696 return 0;
5697}
5698
Jeff Garzikbf794512005-07-31 13:07:26 -04005699static int ipw_wx_set_rts(struct net_device *dev,
5700 struct iw_request_info *info,
James Ketrenos43f66a62005-03-25 12:31:53 -06005701 union iwreq_data *wrqu, char *extra)
Jeff Garzikbf794512005-07-31 13:07:26 -04005702{
James Ketrenos43f66a62005-03-25 12:31:53 -06005703 struct ipw_priv *priv = ieee80211_priv(dev);
5704
5705 if (wrqu->rts.disabled)
5706 priv->rts_threshold = DEFAULT_RTS_THRESHOLD;
5707 else {
5708 if (wrqu->rts.value < MIN_RTS_THRESHOLD ||
5709 wrqu->rts.value > MAX_RTS_THRESHOLD)
5710 return -EINVAL;
Jeff Garzikbf794512005-07-31 13:07:26 -04005711
James Ketrenos43f66a62005-03-25 12:31:53 -06005712 priv->rts_threshold = wrqu->rts.value;
5713 }
5714
5715 ipw_send_rts_threshold(priv, priv->rts_threshold);
5716 IPW_DEBUG_WX("SET RTS Threshold -> %d \n", priv->rts_threshold);
5717 return 0;
5718}
5719
Jeff Garzikbf794512005-07-31 13:07:26 -04005720static int ipw_wx_get_rts(struct net_device *dev,
5721 struct iw_request_info *info,
James Ketrenos43f66a62005-03-25 12:31:53 -06005722 union iwreq_data *wrqu, char *extra)
Jeff Garzikbf794512005-07-31 13:07:26 -04005723{
James Ketrenos43f66a62005-03-25 12:31:53 -06005724 struct ipw_priv *priv = ieee80211_priv(dev);
5725 wrqu->rts.value = priv->rts_threshold;
5726 wrqu->rts.fixed = 0; /* no auto select */
Jeff Garzik0edd5b42005-09-07 00:48:31 -04005727 wrqu->rts.disabled = (wrqu->rts.value == DEFAULT_RTS_THRESHOLD);
James Ketrenos43f66a62005-03-25 12:31:53 -06005728
5729 IPW_DEBUG_WX("GET RTS Threshold -> %d \n", wrqu->rts.value);
5730 return 0;
5731}
5732
Jeff Garzikbf794512005-07-31 13:07:26 -04005733static int ipw_wx_set_txpow(struct net_device *dev,
5734 struct iw_request_info *info,
James Ketrenos43f66a62005-03-25 12:31:53 -06005735 union iwreq_data *wrqu, char *extra)
Jeff Garzikbf794512005-07-31 13:07:26 -04005736{
James Ketrenos43f66a62005-03-25 12:31:53 -06005737 struct ipw_priv *priv = ieee80211_priv(dev);
5738 struct ipw_tx_power tx_power;
5739 int i;
5740
5741 if (ipw_radio_kill_sw(priv, wrqu->power.disabled))
5742 return -EINPROGRESS;
5743
5744 if (wrqu->power.flags != IW_TXPOW_DBM)
5745 return -EINVAL;
5746
Jeff Garzik0edd5b42005-09-07 00:48:31 -04005747 if ((wrqu->power.value > 20) || (wrqu->power.value < -12))
James Ketrenos43f66a62005-03-25 12:31:53 -06005748 return -EINVAL;
5749
5750 priv->tx_power = wrqu->power.value;
5751
5752 memset(&tx_power, 0, sizeof(tx_power));
5753
5754 /* configure device for 'G' band */
5755 tx_power.ieee_mode = IPW_G_MODE;
5756 tx_power.num_channels = 11;
5757 for (i = 0; i < 11; i++) {
5758 tx_power.channels_tx_power[i].channel_number = i + 1;
5759 tx_power.channels_tx_power[i].tx_power = priv->tx_power;
5760 }
5761 if (ipw_send_tx_power(priv, &tx_power))
5762 goto error;
5763
5764 /* configure device to also handle 'B' band */
5765 tx_power.ieee_mode = IPW_B_MODE;
5766 if (ipw_send_tx_power(priv, &tx_power))
5767 goto error;
5768
5769 return 0;
5770
Jeff Garzik0edd5b42005-09-07 00:48:31 -04005771 error:
James Ketrenos43f66a62005-03-25 12:31:53 -06005772 return -EIO;
5773}
5774
Jeff Garzikbf794512005-07-31 13:07:26 -04005775static int ipw_wx_get_txpow(struct net_device *dev,
5776 struct iw_request_info *info,
James Ketrenos43f66a62005-03-25 12:31:53 -06005777 union iwreq_data *wrqu, char *extra)
Jeff Garzikbf794512005-07-31 13:07:26 -04005778{
James Ketrenos43f66a62005-03-25 12:31:53 -06005779 struct ipw_priv *priv = ieee80211_priv(dev);
5780
5781 wrqu->power.value = priv->tx_power;
5782 wrqu->power.fixed = 1;
5783 wrqu->power.flags = IW_TXPOW_DBM;
5784 wrqu->power.disabled = (priv->status & STATUS_RF_KILL_MASK) ? 1 : 0;
5785
Jeff Garzikbf794512005-07-31 13:07:26 -04005786 IPW_DEBUG_WX("GET TX Power -> %s %d \n",
Jeff Garzik0edd5b42005-09-07 00:48:31 -04005787 wrqu->power.disabled ? "ON" : "OFF", wrqu->power.value);
James Ketrenos43f66a62005-03-25 12:31:53 -06005788
5789 return 0;
5790}
5791
Jeff Garzikbf794512005-07-31 13:07:26 -04005792static int ipw_wx_set_frag(struct net_device *dev,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04005793 struct iw_request_info *info,
5794 union iwreq_data *wrqu, char *extra)
James Ketrenos43f66a62005-03-25 12:31:53 -06005795{
5796 struct ipw_priv *priv = ieee80211_priv(dev);
5797
5798 if (wrqu->frag.disabled)
5799 priv->ieee->fts = DEFAULT_FTS;
5800 else {
5801 if (wrqu->frag.value < MIN_FRAG_THRESHOLD ||
5802 wrqu->frag.value > MAX_FRAG_THRESHOLD)
5803 return -EINVAL;
Jeff Garzikbf794512005-07-31 13:07:26 -04005804
James Ketrenos43f66a62005-03-25 12:31:53 -06005805 priv->ieee->fts = wrqu->frag.value & ~0x1;
5806 }
5807
5808 ipw_send_frag_threshold(priv, wrqu->frag.value);
5809 IPW_DEBUG_WX("SET Frag Threshold -> %d \n", wrqu->frag.value);
5810 return 0;
5811}
5812
Jeff Garzikbf794512005-07-31 13:07:26 -04005813static int ipw_wx_get_frag(struct net_device *dev,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04005814 struct iw_request_info *info,
5815 union iwreq_data *wrqu, char *extra)
James Ketrenos43f66a62005-03-25 12:31:53 -06005816{
5817 struct ipw_priv *priv = ieee80211_priv(dev);
5818 wrqu->frag.value = priv->ieee->fts;
5819 wrqu->frag.fixed = 0; /* no auto select */
Jeff Garzik0edd5b42005-09-07 00:48:31 -04005820 wrqu->frag.disabled = (wrqu->frag.value == DEFAULT_FTS);
James Ketrenos43f66a62005-03-25 12:31:53 -06005821
5822 IPW_DEBUG_WX("GET Frag Threshold -> %d \n", wrqu->frag.value);
5823
5824 return 0;
5825}
5826
Jeff Garzikbf794512005-07-31 13:07:26 -04005827static int ipw_wx_set_retry(struct net_device *dev,
5828 struct iw_request_info *info,
James Ketrenos43f66a62005-03-25 12:31:53 -06005829 union iwreq_data *wrqu, char *extra)
Jeff Garzikbf794512005-07-31 13:07:26 -04005830{
James Ketrenos43f66a62005-03-25 12:31:53 -06005831 IPW_DEBUG_WX("0x%p, 0x%p, 0x%p\n", dev, info, wrqu);
Jeff Garzikbf794512005-07-31 13:07:26 -04005832 return -EOPNOTSUPP;
James Ketrenos43f66a62005-03-25 12:31:53 -06005833}
5834
Jeff Garzikbf794512005-07-31 13:07:26 -04005835static int ipw_wx_get_retry(struct net_device *dev,
5836 struct iw_request_info *info,
James Ketrenos43f66a62005-03-25 12:31:53 -06005837 union iwreq_data *wrqu, char *extra)
Jeff Garzikbf794512005-07-31 13:07:26 -04005838{
James Ketrenos43f66a62005-03-25 12:31:53 -06005839 IPW_DEBUG_WX("0x%p, 0x%p, 0x%p\n", dev, info, wrqu);
Jeff Garzikbf794512005-07-31 13:07:26 -04005840 return -EOPNOTSUPP;
James Ketrenos43f66a62005-03-25 12:31:53 -06005841}
5842
Jeff Garzikbf794512005-07-31 13:07:26 -04005843static int ipw_wx_set_scan(struct net_device *dev,
5844 struct iw_request_info *info,
James Ketrenos43f66a62005-03-25 12:31:53 -06005845 union iwreq_data *wrqu, char *extra)
5846{
5847 struct ipw_priv *priv = ieee80211_priv(dev);
5848 IPW_DEBUG_WX("Start scan\n");
5849 if (ipw_request_scan(priv))
5850 return -EIO;
5851 return 0;
5852}
5853
Jeff Garzikbf794512005-07-31 13:07:26 -04005854static int ipw_wx_get_scan(struct net_device *dev,
5855 struct iw_request_info *info,
James Ketrenos43f66a62005-03-25 12:31:53 -06005856 union iwreq_data *wrqu, char *extra)
Jeff Garzikbf794512005-07-31 13:07:26 -04005857{
James Ketrenos43f66a62005-03-25 12:31:53 -06005858 struct ipw_priv *priv = ieee80211_priv(dev);
5859 return ieee80211_wx_get_scan(priv->ieee, info, wrqu, extra);
5860}
5861
Jeff Garzikbf794512005-07-31 13:07:26 -04005862static int ipw_wx_set_encode(struct net_device *dev,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04005863 struct iw_request_info *info,
5864 union iwreq_data *wrqu, char *key)
James Ketrenos43f66a62005-03-25 12:31:53 -06005865{
5866 struct ipw_priv *priv = ieee80211_priv(dev);
5867 return ieee80211_wx_set_encode(priv->ieee, info, wrqu, key);
5868}
5869
Jeff Garzikbf794512005-07-31 13:07:26 -04005870static int ipw_wx_get_encode(struct net_device *dev,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04005871 struct iw_request_info *info,
5872 union iwreq_data *wrqu, char *key)
James Ketrenos43f66a62005-03-25 12:31:53 -06005873{
5874 struct ipw_priv *priv = ieee80211_priv(dev);
5875 return ieee80211_wx_get_encode(priv->ieee, info, wrqu, key);
5876}
5877
Jeff Garzikbf794512005-07-31 13:07:26 -04005878static int ipw_wx_set_power(struct net_device *dev,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04005879 struct iw_request_info *info,
5880 union iwreq_data *wrqu, char *extra)
James Ketrenos43f66a62005-03-25 12:31:53 -06005881{
5882 struct ipw_priv *priv = ieee80211_priv(dev);
5883 int err;
5884
5885 if (wrqu->power.disabled) {
5886 priv->power_mode = IPW_POWER_LEVEL(priv->power_mode);
5887 err = ipw_send_power_mode(priv, IPW_POWER_MODE_CAM);
5888 if (err) {
5889 IPW_DEBUG_WX("failed setting power mode.\n");
5890 return err;
5891 }
5892
5893 IPW_DEBUG_WX("SET Power Management Mode -> off\n");
5894
5895 return 0;
Jeff Garzikbf794512005-07-31 13:07:26 -04005896 }
James Ketrenos43f66a62005-03-25 12:31:53 -06005897
5898 switch (wrqu->power.flags & IW_POWER_MODE) {
Jeff Garzik0edd5b42005-09-07 00:48:31 -04005899 case IW_POWER_ON: /* If not specified */
5900 case IW_POWER_MODE: /* If set all mask */
5901 case IW_POWER_ALL_R: /* If explicitely state all */
James Ketrenos43f66a62005-03-25 12:31:53 -06005902 break;
Jeff Garzik0edd5b42005-09-07 00:48:31 -04005903 default: /* Otherwise we don't support it */
James Ketrenos43f66a62005-03-25 12:31:53 -06005904 IPW_DEBUG_WX("SET PM Mode: %X not supported.\n",
5905 wrqu->power.flags);
Jeff Garzikbf794512005-07-31 13:07:26 -04005906 return -EOPNOTSUPP;
James Ketrenos43f66a62005-03-25 12:31:53 -06005907 }
Jeff Garzikbf794512005-07-31 13:07:26 -04005908
James Ketrenos43f66a62005-03-25 12:31:53 -06005909 /* If the user hasn't specified a power management mode yet, default
5910 * to BATTERY */
Jeff Garzik0edd5b42005-09-07 00:48:31 -04005911 if (IPW_POWER_LEVEL(priv->power_mode) == IPW_POWER_AC)
James Ketrenos43f66a62005-03-25 12:31:53 -06005912 priv->power_mode = IPW_POWER_ENABLED | IPW_POWER_BATTERY;
Jeff Garzikbf794512005-07-31 13:07:26 -04005913 else
James Ketrenos43f66a62005-03-25 12:31:53 -06005914 priv->power_mode = IPW_POWER_ENABLED | priv->power_mode;
5915 err = ipw_send_power_mode(priv, IPW_POWER_LEVEL(priv->power_mode));
5916 if (err) {
5917 IPW_DEBUG_WX("failed setting power mode.\n");
5918 return err;
5919 }
5920
Jeff Garzik0edd5b42005-09-07 00:48:31 -04005921 IPW_DEBUG_WX("SET Power Management Mode -> 0x%02X\n", priv->power_mode);
Jeff Garzikbf794512005-07-31 13:07:26 -04005922
James Ketrenos43f66a62005-03-25 12:31:53 -06005923 return 0;
5924}
5925
Jeff Garzikbf794512005-07-31 13:07:26 -04005926static int ipw_wx_get_power(struct net_device *dev,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04005927 struct iw_request_info *info,
5928 union iwreq_data *wrqu, char *extra)
James Ketrenos43f66a62005-03-25 12:31:53 -06005929{
5930 struct ipw_priv *priv = ieee80211_priv(dev);
5931
5932 if (!(priv->power_mode & IPW_POWER_ENABLED)) {
5933 wrqu->power.disabled = 1;
5934 } else {
5935 wrqu->power.disabled = 0;
5936 }
5937
5938 IPW_DEBUG_WX("GET Power Management Mode -> %02X\n", priv->power_mode);
Jeff Garzikbf794512005-07-31 13:07:26 -04005939
James Ketrenos43f66a62005-03-25 12:31:53 -06005940 return 0;
5941}
5942
Jeff Garzikbf794512005-07-31 13:07:26 -04005943static int ipw_wx_set_powermode(struct net_device *dev,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04005944 struct iw_request_info *info,
5945 union iwreq_data *wrqu, char *extra)
James Ketrenos43f66a62005-03-25 12:31:53 -06005946{
5947 struct ipw_priv *priv = ieee80211_priv(dev);
5948 int mode = *(int *)extra;
5949 int err;
Jeff Garzikbf794512005-07-31 13:07:26 -04005950
James Ketrenos43f66a62005-03-25 12:31:53 -06005951 if ((mode < 1) || (mode > IPW_POWER_LIMIT)) {
5952 mode = IPW_POWER_AC;
5953 priv->power_mode = mode;
5954 } else {
5955 priv->power_mode = IPW_POWER_ENABLED | mode;
5956 }
Jeff Garzikbf794512005-07-31 13:07:26 -04005957
James Ketrenos43f66a62005-03-25 12:31:53 -06005958 if (priv->power_mode != mode) {
5959 err = ipw_send_power_mode(priv, mode);
Jeff Garzikbf794512005-07-31 13:07:26 -04005960
James Ketrenos43f66a62005-03-25 12:31:53 -06005961 if (err) {
5962 IPW_DEBUG_WX("failed setting power mode.\n");
5963 return err;
5964 }
5965 }
Jeff Garzikbf794512005-07-31 13:07:26 -04005966
James Ketrenos43f66a62005-03-25 12:31:53 -06005967 return 0;
5968}
5969
5970#define MAX_WX_STRING 80
Jeff Garzikbf794512005-07-31 13:07:26 -04005971static int ipw_wx_get_powermode(struct net_device *dev,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04005972 struct iw_request_info *info,
5973 union iwreq_data *wrqu, char *extra)
James Ketrenos43f66a62005-03-25 12:31:53 -06005974{
5975 struct ipw_priv *priv = ieee80211_priv(dev);
5976 int level = IPW_POWER_LEVEL(priv->power_mode);
5977 char *p = extra;
5978
5979 p += snprintf(p, MAX_WX_STRING, "Power save level: %d ", level);
5980
5981 switch (level) {
5982 case IPW_POWER_AC:
5983 p += snprintf(p, MAX_WX_STRING - (p - extra), "(AC)");
5984 break;
5985 case IPW_POWER_BATTERY:
5986 p += snprintf(p, MAX_WX_STRING - (p - extra), "(BATTERY)");
5987 break;
5988 default:
5989 p += snprintf(p, MAX_WX_STRING - (p - extra),
Jeff Garzikbf794512005-07-31 13:07:26 -04005990 "(Timeout %dms, Period %dms)",
James Ketrenos43f66a62005-03-25 12:31:53 -06005991 timeout_duration[level - 1] / 1000,
5992 period_duration[level - 1] / 1000);
5993 }
5994
5995 if (!(priv->power_mode & IPW_POWER_ENABLED))
Jeff Garzik0edd5b42005-09-07 00:48:31 -04005996 p += snprintf(p, MAX_WX_STRING - (p - extra), " OFF");
James Ketrenos43f66a62005-03-25 12:31:53 -06005997
5998 wrqu->data.length = p - extra + 1;
5999
6000 return 0;
6001}
6002
6003static int ipw_wx_set_wireless_mode(struct net_device *dev,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04006004 struct iw_request_info *info,
6005 union iwreq_data *wrqu, char *extra)
James Ketrenos43f66a62005-03-25 12:31:53 -06006006{
Jeff Garzik0edd5b42005-09-07 00:48:31 -04006007 struct ipw_priv *priv = ieee80211_priv(dev);
James Ketrenos43f66a62005-03-25 12:31:53 -06006008 int mode = *(int *)extra;
6009 u8 band = 0, modulation = 0;
6010
6011 if (mode == 0 || mode & ~IEEE_MODE_MASK) {
Jeff Garzik0edd5b42005-09-07 00:48:31 -04006012 IPW_WARNING("Attempt to set invalid wireless mode: %d\n", mode);
James Ketrenos43f66a62005-03-25 12:31:53 -06006013 return -EINVAL;
6014 }
Jeff Garzikbf794512005-07-31 13:07:26 -04006015
James Ketrenos43f66a62005-03-25 12:31:53 -06006016 if (priv->adapter == IPW_2915ABG) {
James Ketrenosa33a1982005-09-14 14:28:59 -05006017 priv->ieee->abg_true = 1;
James Ketrenos43f66a62005-03-25 12:31:53 -06006018 if (mode & IEEE_A) {
6019 band |= IEEE80211_52GHZ_BAND;
6020 modulation |= IEEE80211_OFDM_MODULATION;
6021 } else
James Ketrenosa33a1982005-09-14 14:28:59 -05006022 priv->ieee->abg_true = 0;
James Ketrenos43f66a62005-03-25 12:31:53 -06006023 } else {
6024 if (mode & IEEE_A) {
6025 IPW_WARNING("Attempt to set 2200BG into "
6026 "802.11a mode\n");
6027 return -EINVAL;
6028 }
6029
James Ketrenosa33a1982005-09-14 14:28:59 -05006030 priv->ieee->abg_true = 0;
James Ketrenos43f66a62005-03-25 12:31:53 -06006031 }
6032
6033 if (mode & IEEE_B) {
6034 band |= IEEE80211_24GHZ_BAND;
6035 modulation |= IEEE80211_CCK_MODULATION;
6036 } else
James Ketrenosa33a1982005-09-14 14:28:59 -05006037 priv->ieee->abg_true = 0;
Jeff Garzikbf794512005-07-31 13:07:26 -04006038
James Ketrenos43f66a62005-03-25 12:31:53 -06006039 if (mode & IEEE_G) {
6040 band |= IEEE80211_24GHZ_BAND;
6041 modulation |= IEEE80211_OFDM_MODULATION;
6042 } else
James Ketrenosa33a1982005-09-14 14:28:59 -05006043 priv->ieee->abg_true = 0;
James Ketrenos43f66a62005-03-25 12:31:53 -06006044
6045 priv->ieee->mode = mode;
6046 priv->ieee->freq_band = band;
6047 priv->ieee->modulation = modulation;
Jeff Garzik0edd5b42005-09-07 00:48:31 -04006048 init_supported_rates(priv, &priv->rates);
James Ketrenos43f66a62005-03-25 12:31:53 -06006049
6050 /* If we are currently associated, or trying to associate
Jeff Garzik0edd5b42005-09-07 00:48:31 -04006051 * then see if this is a new configuration (causing us to
James Ketrenos43f66a62005-03-25 12:31:53 -06006052 * disassociate) */
Jeff Garzik0edd5b42005-09-07 00:48:31 -04006053 if (priv->status & (STATUS_ASSOCIATED | STATUS_ASSOCIATING)) {
Jeff Garzikbf794512005-07-31 13:07:26 -04006054 /* The resulting association will trigger
James Ketrenos43f66a62005-03-25 12:31:53 -06006055 * the new rates to be sent to the device */
Jeff Garzik0edd5b42005-09-07 00:48:31 -04006056 IPW_DEBUG_ASSOC("Disassociating due to mode change.\n");
6057 ipw_disassociate(priv);
James Ketrenos43f66a62005-03-25 12:31:53 -06006058 } else
6059 ipw_send_supported_rates(priv, &priv->rates);
6060
Jeff Garzikbf794512005-07-31 13:07:26 -04006061 IPW_DEBUG_WX("PRIV SET MODE: %c%c%c\n",
James Ketrenos43f66a62005-03-25 12:31:53 -06006062 mode & IEEE_A ? 'a' : '.',
Jeff Garzik0edd5b42005-09-07 00:48:31 -04006063 mode & IEEE_B ? 'b' : '.', mode & IEEE_G ? 'g' : '.');
James Ketrenos43f66a62005-03-25 12:31:53 -06006064 return 0;
6065}
6066
6067static int ipw_wx_get_wireless_mode(struct net_device *dev,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04006068 struct iw_request_info *info,
6069 union iwreq_data *wrqu, char *extra)
James Ketrenos43f66a62005-03-25 12:31:53 -06006070{
Jeff Garzik0edd5b42005-09-07 00:48:31 -04006071 struct ipw_priv *priv = ieee80211_priv(dev);
James Ketrenos43f66a62005-03-25 12:31:53 -06006072
6073 switch (priv->ieee->freq_band) {
6074 case IEEE80211_24GHZ_BAND:
6075 switch (priv->ieee->modulation) {
6076 case IEEE80211_CCK_MODULATION:
6077 strncpy(extra, "802.11b (2)", MAX_WX_STRING);
6078 break;
Jeff Garzikbf794512005-07-31 13:07:26 -04006079 case IEEE80211_OFDM_MODULATION:
James Ketrenos43f66a62005-03-25 12:31:53 -06006080 strncpy(extra, "802.11g (4)", MAX_WX_STRING);
6081 break;
6082 default:
6083 strncpy(extra, "802.11bg (6)", MAX_WX_STRING);
6084 break;
6085 }
6086 break;
6087
Jeff Garzikbf794512005-07-31 13:07:26 -04006088 case IEEE80211_52GHZ_BAND:
James Ketrenos43f66a62005-03-25 12:31:53 -06006089 strncpy(extra, "802.11a (1)", MAX_WX_STRING);
6090 break;
6091
Jeff Garzik0edd5b42005-09-07 00:48:31 -04006092 default: /* Mixed Band */
James Ketrenos43f66a62005-03-25 12:31:53 -06006093 switch (priv->ieee->modulation) {
6094 case IEEE80211_CCK_MODULATION:
6095 strncpy(extra, "802.11ab (3)", MAX_WX_STRING);
6096 break;
Jeff Garzikbf794512005-07-31 13:07:26 -04006097 case IEEE80211_OFDM_MODULATION:
James Ketrenos43f66a62005-03-25 12:31:53 -06006098 strncpy(extra, "802.11ag (5)", MAX_WX_STRING);
6099 break;
6100 default:
6101 strncpy(extra, "802.11abg (7)", MAX_WX_STRING);
6102 break;
6103 }
6104 break;
Jeff Garzikbf794512005-07-31 13:07:26 -04006105 }
6106
James Ketrenos43f66a62005-03-25 12:31:53 -06006107 IPW_DEBUG_WX("PRIV GET MODE: %s\n", extra);
6108
Jeff Garzik0edd5b42005-09-07 00:48:31 -04006109 wrqu->data.length = strlen(extra) + 1;
James Ketrenos43f66a62005-03-25 12:31:53 -06006110
Jeff Garzik0edd5b42005-09-07 00:48:31 -04006111 return 0;
James Ketrenos43f66a62005-03-25 12:31:53 -06006112}
6113
6114#ifdef CONFIG_IPW_PROMISC
Jeff Garzikbf794512005-07-31 13:07:26 -04006115static int ipw_wx_set_promisc(struct net_device *dev,
6116 struct iw_request_info *info,
James Ketrenos43f66a62005-03-25 12:31:53 -06006117 union iwreq_data *wrqu, char *extra)
Jeff Garzikbf794512005-07-31 13:07:26 -04006118{
James Ketrenos43f66a62005-03-25 12:31:53 -06006119 struct ipw_priv *priv = ieee80211_priv(dev);
6120 int *parms = (int *)extra;
6121 int enable = (parms[0] > 0);
6122
6123 IPW_DEBUG_WX("SET PROMISC: %d %d\n", enable, parms[1]);
6124 if (enable) {
6125 if (priv->ieee->iw_mode != IW_MODE_MONITOR) {
6126 priv->net_dev->type = ARPHRD_IEEE80211;
6127 ipw_adapter_restart(priv);
6128 }
Jeff Garzikbf794512005-07-31 13:07:26 -04006129
James Ketrenos43f66a62005-03-25 12:31:53 -06006130 ipw_set_channel(priv, parms[1]);
6131 } else {
6132 if (priv->ieee->iw_mode != IW_MODE_MONITOR)
6133 return 0;
6134 priv->net_dev->type = ARPHRD_ETHER;
6135 ipw_adapter_restart(priv);
6136 }
6137 return 0;
6138}
6139
Jeff Garzikbf794512005-07-31 13:07:26 -04006140static int ipw_wx_reset(struct net_device *dev,
6141 struct iw_request_info *info,
James Ketrenos43f66a62005-03-25 12:31:53 -06006142 union iwreq_data *wrqu, char *extra)
Jeff Garzikbf794512005-07-31 13:07:26 -04006143{
James Ketrenos43f66a62005-03-25 12:31:53 -06006144 struct ipw_priv *priv = ieee80211_priv(dev);
6145 IPW_DEBUG_WX("RESET\n");
6146 ipw_adapter_restart(priv);
6147 return 0;
6148}
Jeff Garzik0edd5b42005-09-07 00:48:31 -04006149#endif // CONFIG_IPW_PROMISC
James Ketrenos43f66a62005-03-25 12:31:53 -06006150
6151/* Rebase the WE IOCTLs to zero for the handler array */
6152#define IW_IOCTL(x) [(x)-SIOCSIWCOMMIT]
Jeff Garzik0edd5b42005-09-07 00:48:31 -04006153static iw_handler ipw_wx_handlers[] = {
6154 IW_IOCTL(SIOCGIWNAME) = ipw_wx_get_name,
6155 IW_IOCTL(SIOCSIWFREQ) = ipw_wx_set_freq,
6156 IW_IOCTL(SIOCGIWFREQ) = ipw_wx_get_freq,
6157 IW_IOCTL(SIOCSIWMODE) = ipw_wx_set_mode,
6158 IW_IOCTL(SIOCGIWMODE) = ipw_wx_get_mode,
6159 IW_IOCTL(SIOCGIWRANGE) = ipw_wx_get_range,
6160 IW_IOCTL(SIOCSIWAP) = ipw_wx_set_wap,
6161 IW_IOCTL(SIOCGIWAP) = ipw_wx_get_wap,
6162 IW_IOCTL(SIOCSIWSCAN) = ipw_wx_set_scan,
6163 IW_IOCTL(SIOCGIWSCAN) = ipw_wx_get_scan,
6164 IW_IOCTL(SIOCSIWESSID) = ipw_wx_set_essid,
6165 IW_IOCTL(SIOCGIWESSID) = ipw_wx_get_essid,
6166 IW_IOCTL(SIOCSIWNICKN) = ipw_wx_set_nick,
6167 IW_IOCTL(SIOCGIWNICKN) = ipw_wx_get_nick,
6168 IW_IOCTL(SIOCSIWRATE) = ipw_wx_set_rate,
6169 IW_IOCTL(SIOCGIWRATE) = ipw_wx_get_rate,
6170 IW_IOCTL(SIOCSIWRTS) = ipw_wx_set_rts,
6171 IW_IOCTL(SIOCGIWRTS) = ipw_wx_get_rts,
6172 IW_IOCTL(SIOCSIWFRAG) = ipw_wx_set_frag,
6173 IW_IOCTL(SIOCGIWFRAG) = ipw_wx_get_frag,
6174 IW_IOCTL(SIOCSIWTXPOW) = ipw_wx_set_txpow,
6175 IW_IOCTL(SIOCGIWTXPOW) = ipw_wx_get_txpow,
6176 IW_IOCTL(SIOCSIWRETRY) = ipw_wx_set_retry,
6177 IW_IOCTL(SIOCGIWRETRY) = ipw_wx_get_retry,
6178 IW_IOCTL(SIOCSIWENCODE) = ipw_wx_set_encode,
6179 IW_IOCTL(SIOCGIWENCODE) = ipw_wx_get_encode,
6180 IW_IOCTL(SIOCSIWPOWER) = ipw_wx_set_power,
6181 IW_IOCTL(SIOCGIWPOWER) = ipw_wx_get_power,
James Ketrenos43f66a62005-03-25 12:31:53 -06006182};
6183
6184#define IPW_PRIV_SET_POWER SIOCIWFIRSTPRIV
6185#define IPW_PRIV_GET_POWER SIOCIWFIRSTPRIV+1
6186#define IPW_PRIV_SET_MODE SIOCIWFIRSTPRIV+2
6187#define IPW_PRIV_GET_MODE SIOCIWFIRSTPRIV+3
6188#define IPW_PRIV_SET_PROMISC SIOCIWFIRSTPRIV+4
6189#define IPW_PRIV_RESET SIOCIWFIRSTPRIV+5
6190
Jeff Garzikbf794512005-07-31 13:07:26 -04006191static struct iw_priv_args ipw_priv_args[] = {
James Ketrenos43f66a62005-03-25 12:31:53 -06006192 {
Jeff Garzik0edd5b42005-09-07 00:48:31 -04006193 .cmd = IPW_PRIV_SET_POWER,
6194 .set_args = IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
6195 .name = "set_power"},
James Ketrenos43f66a62005-03-25 12:31:53 -06006196 {
Jeff Garzik0edd5b42005-09-07 00:48:31 -04006197 .cmd = IPW_PRIV_GET_POWER,
6198 .get_args = IW_PRIV_TYPE_CHAR | IW_PRIV_SIZE_FIXED | MAX_WX_STRING,
6199 .name = "get_power"},
James Ketrenos43f66a62005-03-25 12:31:53 -06006200 {
Jeff Garzik0edd5b42005-09-07 00:48:31 -04006201 .cmd = IPW_PRIV_SET_MODE,
6202 .set_args = IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
6203 .name = "set_mode"},
James Ketrenos43f66a62005-03-25 12:31:53 -06006204 {
Jeff Garzik0edd5b42005-09-07 00:48:31 -04006205 .cmd = IPW_PRIV_GET_MODE,
6206 .get_args = IW_PRIV_TYPE_CHAR | IW_PRIV_SIZE_FIXED | MAX_WX_STRING,
6207 .name = "get_mode"},
James Ketrenos43f66a62005-03-25 12:31:53 -06006208#ifdef CONFIG_IPW_PROMISC
6209 {
Jeff Garzik0edd5b42005-09-07 00:48:31 -04006210 IPW_PRIV_SET_PROMISC,
6211 IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 2, 0, "monitor"},
James Ketrenos43f66a62005-03-25 12:31:53 -06006212 {
Jeff Garzik0edd5b42005-09-07 00:48:31 -04006213 IPW_PRIV_RESET,
6214 IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 0, 0, "reset"},
6215#endif /* CONFIG_IPW_PROMISC */
James Ketrenos43f66a62005-03-25 12:31:53 -06006216};
6217
6218static iw_handler ipw_priv_handler[] = {
6219 ipw_wx_set_powermode,
6220 ipw_wx_get_powermode,
6221 ipw_wx_set_wireless_mode,
6222 ipw_wx_get_wireless_mode,
6223#ifdef CONFIG_IPW_PROMISC
6224 ipw_wx_set_promisc,
Jeff Garzikbf794512005-07-31 13:07:26 -04006225 ipw_wx_reset,
James Ketrenos43f66a62005-03-25 12:31:53 -06006226#endif
6227};
6228
Jeff Garzik0edd5b42005-09-07 00:48:31 -04006229static struct iw_handler_def ipw_wx_handler_def = {
6230 .standard = ipw_wx_handlers,
6231 .num_standard = ARRAY_SIZE(ipw_wx_handlers),
6232 .num_private = ARRAY_SIZE(ipw_priv_handler),
6233 .num_private_args = ARRAY_SIZE(ipw_priv_args),
6234 .private = ipw_priv_handler,
6235 .private_args = ipw_priv_args,
James Ketrenos43f66a62005-03-25 12:31:53 -06006236};
6237
James Ketrenos43f66a62005-03-25 12:31:53 -06006238/*
6239 * Get wireless statistics.
6240 * Called by /proc/net/wireless
6241 * Also called by SIOCGIWSTATS
6242 */
Jeff Garzik0edd5b42005-09-07 00:48:31 -04006243static struct iw_statistics *ipw_get_wireless_stats(struct net_device *dev)
James Ketrenos43f66a62005-03-25 12:31:53 -06006244{
6245 struct ipw_priv *priv = ieee80211_priv(dev);
6246 struct iw_statistics *wstats;
Jeff Garzikbf794512005-07-31 13:07:26 -04006247
James Ketrenos43f66a62005-03-25 12:31:53 -06006248 wstats = &priv->wstats;
6249
6250 /* if hw is disabled, then ipw2100_get_ordinal() can't be called.
Jeff Garzikbf794512005-07-31 13:07:26 -04006251 * ipw2100_wx_wireless_stats seems to be called before fw is
James Ketrenos43f66a62005-03-25 12:31:53 -06006252 * initialized. STATUS_ASSOCIATED will only be set if the hw is up
6253 * and associated; if not associcated, the values are all meaningless
6254 * anyway, so set them all to NULL and INVALID */
6255 if (!(priv->status & STATUS_ASSOCIATED)) {
6256 wstats->miss.beacon = 0;
6257 wstats->discard.retries = 0;
6258 wstats->qual.qual = 0;
6259 wstats->qual.level = 0;
6260 wstats->qual.noise = 0;
6261 wstats->qual.updated = 7;
6262 wstats->qual.updated |= IW_QUAL_NOISE_INVALID |
Jeff Garzik0edd5b42005-09-07 00:48:31 -04006263 IW_QUAL_QUAL_INVALID | IW_QUAL_LEVEL_INVALID;
James Ketrenos43f66a62005-03-25 12:31:53 -06006264 return wstats;
Jeff Garzikbf794512005-07-31 13:07:26 -04006265 }
James Ketrenos43f66a62005-03-25 12:31:53 -06006266
6267 wstats->qual.qual = priv->quality;
6268 wstats->qual.level = average_value(&priv->average_rssi);
6269 wstats->qual.noise = average_value(&priv->average_noise);
6270 wstats->qual.updated = IW_QUAL_QUAL_UPDATED | IW_QUAL_LEVEL_UPDATED |
Jeff Garzik0edd5b42005-09-07 00:48:31 -04006271 IW_QUAL_NOISE_UPDATED;
James Ketrenos43f66a62005-03-25 12:31:53 -06006272
6273 wstats->miss.beacon = average_value(&priv->average_missed_beacons);
6274 wstats->discard.retries = priv->last_tx_failures;
6275 wstats->discard.code = priv->ieee->ieee_stats.rx_discards_undecryptable;
Jeff Garzikbf794512005-07-31 13:07:26 -04006276
James Ketrenos43f66a62005-03-25 12:31:53 -06006277/* if (ipw_get_ordinal(priv, IPW_ORD_STAT_TX_RETRY, &tx_retry, &len))
6278 goto fail_get_ordinal;
6279 wstats->discard.retries += tx_retry; */
Jeff Garzikbf794512005-07-31 13:07:26 -04006280
James Ketrenos43f66a62005-03-25 12:31:53 -06006281 return wstats;
6282}
6283
James Ketrenos43f66a62005-03-25 12:31:53 -06006284/* net device stuff */
6285
6286static inline void init_sys_config(struct ipw_sys_config *sys_config)
6287{
Jeff Garzik0edd5b42005-09-07 00:48:31 -04006288 memset(sys_config, 0, sizeof(struct ipw_sys_config));
6289 sys_config->bt_coexistence = 1; /* We may need to look into prvStaBtConfig */
James Ketrenos43f66a62005-03-25 12:31:53 -06006290 sys_config->answer_broadcast_ssid_probe = 0;
6291 sys_config->accept_all_data_frames = 0;
6292 sys_config->accept_non_directed_frames = 1;
6293 sys_config->exclude_unicast_unencrypted = 0;
6294 sys_config->disable_unicast_decryption = 1;
6295 sys_config->exclude_multicast_unencrypted = 0;
6296 sys_config->disable_multicast_decryption = 1;
6297 sys_config->antenna_diversity = CFG_SYS_ANTENNA_BOTH;
Jeff Garzik0edd5b42005-09-07 00:48:31 -04006298 sys_config->pass_crc_to_host = 0; /* TODO: See if 1 gives us FCS */
James Ketrenos43f66a62005-03-25 12:31:53 -06006299 sys_config->dot11g_auto_detection = 0;
Jeff Garzikbf794512005-07-31 13:07:26 -04006300 sys_config->enable_cts_to_self = 0;
James Ketrenos43f66a62005-03-25 12:31:53 -06006301 sys_config->bt_coexist_collision_thr = 0;
6302 sys_config->pass_noise_stats_to_host = 1;
6303}
6304
6305static int ipw_net_open(struct net_device *dev)
6306{
6307 struct ipw_priv *priv = ieee80211_priv(dev);
6308 IPW_DEBUG_INFO("dev->open\n");
6309 /* we should be verifying the device is ready to be opened */
Jeff Garzikbf794512005-07-31 13:07:26 -04006310 if (!(priv->status & STATUS_RF_KILL_MASK) &&
6311 (priv->status & STATUS_ASSOCIATED))
James Ketrenos43f66a62005-03-25 12:31:53 -06006312 netif_start_queue(dev);
6313 return 0;
6314}
6315
6316static int ipw_net_stop(struct net_device *dev)
6317{
6318 IPW_DEBUG_INFO("dev->close\n");
6319 netif_stop_queue(dev);
6320 return 0;
6321}
6322
6323/*
6324todo:
6325
6326modify to send one tfd per fragment instead of using chunking. otherwise
6327we need to heavily modify the ieee80211_skb_to_txb.
6328*/
6329
6330static inline void ipw_tx_skb(struct ipw_priv *priv, struct ieee80211_txb *txb)
6331{
James Ketrenos0dacca12005-09-21 12:23:41 -05006332 struct ieee80211_hdr_3addr *hdr = (struct ieee80211_hdr_3addr *)
Jeff Garzik0edd5b42005-09-07 00:48:31 -04006333 txb->fragments[0]->data;
James Ketrenos43f66a62005-03-25 12:31:53 -06006334 int i = 0;
6335 struct tfd_frame *tfd;
6336 struct clx2_tx_queue *txq = &priv->txq[0];
6337 struct clx2_queue *q = &txq->q;
6338 u8 id, hdr_len, unicast;
6339 u16 remaining_bytes;
6340
6341 switch (priv->ieee->iw_mode) {
6342 case IW_MODE_ADHOC:
6343 hdr_len = IEEE80211_3ADDR_LEN;
6344 unicast = !is_broadcast_ether_addr(hdr->addr1) &&
Jeff Garzik0edd5b42005-09-07 00:48:31 -04006345 !is_multicast_ether_addr(hdr->addr1);
James Ketrenos43f66a62005-03-25 12:31:53 -06006346 id = ipw_find_station(priv, hdr->addr1);
6347 if (id == IPW_INVALID_STATION) {
6348 id = ipw_add_station(priv, hdr->addr1);
6349 if (id == IPW_INVALID_STATION) {
6350 IPW_WARNING("Attempt to send data to "
Jeff Garzikbf794512005-07-31 13:07:26 -04006351 "invalid cell: " MAC_FMT "\n",
James Ketrenos43f66a62005-03-25 12:31:53 -06006352 MAC_ARG(hdr->addr1));
6353 goto drop;
6354 }
6355 }
6356 break;
6357
6358 case IW_MODE_INFRA:
6359 default:
6360 unicast = !is_broadcast_ether_addr(hdr->addr3) &&
Jeff Garzik0edd5b42005-09-07 00:48:31 -04006361 !is_multicast_ether_addr(hdr->addr3);
James Ketrenos43f66a62005-03-25 12:31:53 -06006362 hdr_len = IEEE80211_3ADDR_LEN;
6363 id = 0;
6364 break;
6365 }
6366
6367 tfd = &txq->bd[q->first_empty];
6368 txq->txb[q->first_empty] = txb;
6369 memset(tfd, 0, sizeof(*tfd));
6370 tfd->u.data.station_number = id;
6371
6372 tfd->control_flags.message_type = TX_FRAME_TYPE;
6373 tfd->control_flags.control_bits = TFD_NEED_IRQ_MASK;
6374
6375 tfd->u.data.cmd_id = DINO_CMD_TX;
6376 tfd->u.data.len = txb->payload_size;
6377 remaining_bytes = txb->payload_size;
6378 if (unlikely(!unicast))
6379 tfd->u.data.tx_flags = DCT_FLAG_NO_WEP;
6380 else
6381 tfd->u.data.tx_flags = DCT_FLAG_NO_WEP | DCT_FLAG_ACK_REQD;
Jeff Garzikbf794512005-07-31 13:07:26 -04006382
James Ketrenos43f66a62005-03-25 12:31:53 -06006383 if (priv->assoc_request.ieee_mode == IPW_B_MODE)
6384 tfd->u.data.tx_flags_ext = DCT_FLAG_EXT_MODE_CCK;
6385 else
6386 tfd->u.data.tx_flags_ext = DCT_FLAG_EXT_MODE_OFDM;
6387
6388 if (priv->config & CFG_PREAMBLE)
6389 tfd->u.data.tx_flags |= DCT_FLAG_SHORT_PREMBL;
6390
6391 memcpy(&tfd->u.data.tfd.tfd_24.mchdr, hdr, hdr_len);
6392
6393 /* payload */
Jeff Garzik0edd5b42005-09-07 00:48:31 -04006394 tfd->u.data.num_chunks = min((u8) (NUM_TFD_CHUNKS - 2), txb->nr_frags);
James Ketrenos43f66a62005-03-25 12:31:53 -06006395 for (i = 0; i < tfd->u.data.num_chunks; i++) {
Jeff Garzikbf794512005-07-31 13:07:26 -04006396 IPW_DEBUG_TX("Dumping TX packet frag %i of %i (%d bytes):\n",
James Ketrenos43f66a62005-03-25 12:31:53 -06006397 i, tfd->u.data.num_chunks,
6398 txb->fragments[i]->len - hdr_len);
Jeff Garzikbf794512005-07-31 13:07:26 -04006399 printk_buf(IPW_DL_TX, txb->fragments[i]->data + hdr_len,
James Ketrenos43f66a62005-03-25 12:31:53 -06006400 txb->fragments[i]->len - hdr_len);
6401
Jeff Garzik0edd5b42005-09-07 00:48:31 -04006402 tfd->u.data.chunk_ptr[i] =
6403 pci_map_single(priv->pci_dev,
6404 txb->fragments[i]->data + hdr_len,
6405 txb->fragments[i]->len - hdr_len,
6406 PCI_DMA_TODEVICE);
James Ketrenos43f66a62005-03-25 12:31:53 -06006407 tfd->u.data.chunk_len[i] = txb->fragments[i]->len - hdr_len;
6408 }
6409
6410 if (i != txb->nr_frags) {
6411 struct sk_buff *skb;
6412 u16 remaining_bytes = 0;
6413 int j;
6414
6415 for (j = i; j < txb->nr_frags; j++)
6416 remaining_bytes += txb->fragments[j]->len - hdr_len;
6417
6418 printk(KERN_INFO "Trying to reallocate for %d bytes\n",
6419 remaining_bytes);
6420 skb = alloc_skb(remaining_bytes, GFP_ATOMIC);
6421 if (skb != NULL) {
6422 tfd->u.data.chunk_len[i] = remaining_bytes;
6423 for (j = i; j < txb->nr_frags; j++) {
6424 int size = txb->fragments[j]->len - hdr_len;
6425 printk(KERN_INFO "Adding frag %d %d...\n",
Jeff Garzik0edd5b42005-09-07 00:48:31 -04006426 j, size);
James Ketrenos43f66a62005-03-25 12:31:53 -06006427 memcpy(skb_put(skb, size),
Jeff Garzik0edd5b42005-09-07 00:48:31 -04006428 txb->fragments[j]->data + hdr_len, size);
James Ketrenos43f66a62005-03-25 12:31:53 -06006429 }
6430 dev_kfree_skb_any(txb->fragments[i]);
6431 txb->fragments[i] = skb;
Jeff Garzik0edd5b42005-09-07 00:48:31 -04006432 tfd->u.data.chunk_ptr[i] =
6433 pci_map_single(priv->pci_dev, skb->data,
6434 tfd->u.data.chunk_len[i],
6435 PCI_DMA_TODEVICE);
James Ketrenos43f66a62005-03-25 12:31:53 -06006436 tfd->u.data.num_chunks++;
Jeff Garzikbf794512005-07-31 13:07:26 -04006437 }
James Ketrenos43f66a62005-03-25 12:31:53 -06006438 }
6439
6440 /* kick DMA */
6441 q->first_empty = ipw_queue_inc_wrap(q->first_empty, q->n_bd);
6442 ipw_write32(priv, q->reg_w, q->first_empty);
6443
Jeff Garzikbf794512005-07-31 13:07:26 -04006444 if (ipw_queue_space(q) < q->high_mark)
James Ketrenos43f66a62005-03-25 12:31:53 -06006445 netif_stop_queue(priv->net_dev);
6446
6447 return;
6448
Jeff Garzik0edd5b42005-09-07 00:48:31 -04006449 drop:
James Ketrenos43f66a62005-03-25 12:31:53 -06006450 IPW_DEBUG_DROP("Silently dropping Tx packet.\n");
6451 ieee80211_txb_free(txb);
6452}
6453
6454static int ipw_net_hard_start_xmit(struct ieee80211_txb *txb,
James Ketrenosc8d42d12005-09-21 12:23:43 -05006455 struct net_device *dev, int pri)
James Ketrenos43f66a62005-03-25 12:31:53 -06006456{
6457 struct ipw_priv *priv = ieee80211_priv(dev);
6458 unsigned long flags;
6459
6460 IPW_DEBUG_TX("dev->xmit(%d bytes)\n", txb->payload_size);
6461
6462 spin_lock_irqsave(&priv->lock, flags);
6463
6464 if (!(priv->status & STATUS_ASSOCIATED)) {
6465 IPW_DEBUG_INFO("Tx attempt while not associated.\n");
6466 priv->ieee->stats.tx_carrier_errors++;
6467 netif_stop_queue(dev);
6468 goto fail_unlock;
6469 }
6470
6471 ipw_tx_skb(priv, txb);
6472
6473 spin_unlock_irqrestore(&priv->lock, flags);
6474 return 0;
6475
Jeff Garzik0edd5b42005-09-07 00:48:31 -04006476 fail_unlock:
James Ketrenos43f66a62005-03-25 12:31:53 -06006477 spin_unlock_irqrestore(&priv->lock, flags);
6478 return 1;
6479}
6480
6481static struct net_device_stats *ipw_net_get_stats(struct net_device *dev)
6482{
6483 struct ipw_priv *priv = ieee80211_priv(dev);
Jeff Garzikbf794512005-07-31 13:07:26 -04006484
James Ketrenos43f66a62005-03-25 12:31:53 -06006485 priv->ieee->stats.tx_packets = priv->tx_packets;
6486 priv->ieee->stats.rx_packets = priv->rx_packets;
6487 return &priv->ieee->stats;
6488}
6489
6490static void ipw_net_set_multicast_list(struct net_device *dev)
6491{
6492
6493}
6494
6495static int ipw_net_set_mac_address(struct net_device *dev, void *p)
6496{
6497 struct ipw_priv *priv = ieee80211_priv(dev);
6498 struct sockaddr *addr = p;
6499 if (!is_valid_ether_addr(addr->sa_data))
6500 return -EADDRNOTAVAIL;
6501 priv->config |= CFG_CUSTOM_MAC;
6502 memcpy(priv->mac_addr, addr->sa_data, ETH_ALEN);
6503 printk(KERN_INFO "%s: Setting MAC to " MAC_FMT "\n",
6504 priv->net_dev->name, MAC_ARG(priv->mac_addr));
6505 ipw_adapter_restart(priv);
6506 return 0;
6507}
6508
Jeff Garzikbf794512005-07-31 13:07:26 -04006509static void ipw_ethtool_get_drvinfo(struct net_device *dev,
James Ketrenos43f66a62005-03-25 12:31:53 -06006510 struct ethtool_drvinfo *info)
6511{
6512 struct ipw_priv *p = ieee80211_priv(dev);
6513 char vers[64];
6514 char date[32];
6515 u32 len;
6516
6517 strcpy(info->driver, DRV_NAME);
6518 strcpy(info->version, DRV_VERSION);
6519
6520 len = sizeof(vers);
6521 ipw_get_ordinal(p, IPW_ORD_STAT_FW_VERSION, vers, &len);
6522 len = sizeof(date);
6523 ipw_get_ordinal(p, IPW_ORD_STAT_FW_DATE, date, &len);
6524
Jeff Garzik0edd5b42005-09-07 00:48:31 -04006525 snprintf(info->fw_version, sizeof(info->fw_version), "%s (%s)",
James Ketrenos43f66a62005-03-25 12:31:53 -06006526 vers, date);
6527 strcpy(info->bus_info, pci_name(p->pci_dev));
6528 info->eedump_len = CX2_EEPROM_IMAGE_SIZE;
6529}
6530
6531static u32 ipw_ethtool_get_link(struct net_device *dev)
6532{
6533 struct ipw_priv *priv = ieee80211_priv(dev);
6534 return (priv->status & STATUS_ASSOCIATED) != 0;
6535}
6536
6537static int ipw_ethtool_get_eeprom_len(struct net_device *dev)
6538{
6539 return CX2_EEPROM_IMAGE_SIZE;
6540}
6541
6542static int ipw_ethtool_get_eeprom(struct net_device *dev,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04006543 struct ethtool_eeprom *eeprom, u8 * bytes)
James Ketrenos43f66a62005-03-25 12:31:53 -06006544{
6545 struct ipw_priv *p = ieee80211_priv(dev);
6546
6547 if (eeprom->offset + eeprom->len > CX2_EEPROM_IMAGE_SIZE)
6548 return -EINVAL;
Jeff Garzikbf794512005-07-31 13:07:26 -04006549
Jeff Garzik0edd5b42005-09-07 00:48:31 -04006550 memcpy(bytes, &((u8 *) p->eeprom)[eeprom->offset], eeprom->len);
James Ketrenos43f66a62005-03-25 12:31:53 -06006551 return 0;
6552}
6553
6554static int ipw_ethtool_set_eeprom(struct net_device *dev,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04006555 struct ethtool_eeprom *eeprom, u8 * bytes)
James Ketrenos43f66a62005-03-25 12:31:53 -06006556{
6557 struct ipw_priv *p = ieee80211_priv(dev);
6558 int i;
6559
6560 if (eeprom->offset + eeprom->len > CX2_EEPROM_IMAGE_SIZE)
6561 return -EINVAL;
6562
Jeff Garzik0edd5b42005-09-07 00:48:31 -04006563 memcpy(&((u8 *) p->eeprom)[eeprom->offset], bytes, eeprom->len);
Jeff Garzikbf794512005-07-31 13:07:26 -04006564 for (i = IPW_EEPROM_DATA;
Jeff Garzik0edd5b42005-09-07 00:48:31 -04006565 i < IPW_EEPROM_DATA + CX2_EEPROM_IMAGE_SIZE; i++)
James Ketrenos43f66a62005-03-25 12:31:53 -06006566 ipw_write8(p, i, p->eeprom[i]);
6567
6568 return 0;
6569}
6570
6571static struct ethtool_ops ipw_ethtool_ops = {
Jeff Garzik0edd5b42005-09-07 00:48:31 -04006572 .get_link = ipw_ethtool_get_link,
6573 .get_drvinfo = ipw_ethtool_get_drvinfo,
6574 .get_eeprom_len = ipw_ethtool_get_eeprom_len,
6575 .get_eeprom = ipw_ethtool_get_eeprom,
6576 .set_eeprom = ipw_ethtool_set_eeprom,
James Ketrenos43f66a62005-03-25 12:31:53 -06006577};
6578
6579static irqreturn_t ipw_isr(int irq, void *data, struct pt_regs *regs)
6580{
6581 struct ipw_priv *priv = data;
6582 u32 inta, inta_mask;
Jeff Garzikbf794512005-07-31 13:07:26 -04006583
James Ketrenos43f66a62005-03-25 12:31:53 -06006584 if (!priv)
6585 return IRQ_NONE;
6586
6587 spin_lock(&priv->lock);
6588
6589 if (!(priv->status & STATUS_INT_ENABLED)) {
6590 /* Shared IRQ */
6591 goto none;
6592 }
6593
6594 inta = ipw_read32(priv, CX2_INTA_RW);
6595 inta_mask = ipw_read32(priv, CX2_INTA_MASK_R);
Jeff Garzikbf794512005-07-31 13:07:26 -04006596
James Ketrenos43f66a62005-03-25 12:31:53 -06006597 if (inta == 0xFFFFFFFF) {
6598 /* Hardware disappeared */
6599 IPW_WARNING("IRQ INTA == 0xFFFFFFFF\n");
6600 goto none;
6601 }
6602
6603 if (!(inta & (CX2_INTA_MASK_ALL & inta_mask))) {
6604 /* Shared interrupt */
6605 goto none;
6606 }
6607
6608 /* tell the device to stop sending interrupts */
6609 ipw_disable_interrupts(priv);
Jeff Garzikbf794512005-07-31 13:07:26 -04006610
James Ketrenos43f66a62005-03-25 12:31:53 -06006611 /* ack current interrupts */
6612 inta &= (CX2_INTA_MASK_ALL & inta_mask);
6613 ipw_write32(priv, CX2_INTA_RW, inta);
Jeff Garzikbf794512005-07-31 13:07:26 -04006614
James Ketrenos43f66a62005-03-25 12:31:53 -06006615 /* Cache INTA value for our tasklet */
6616 priv->isr_inta = inta;
6617
6618 tasklet_schedule(&priv->irq_tasklet);
6619
Jeff Garzik0edd5b42005-09-07 00:48:31 -04006620 spin_unlock(&priv->lock);
James Ketrenos43f66a62005-03-25 12:31:53 -06006621
6622 return IRQ_HANDLED;
Jeff Garzik0edd5b42005-09-07 00:48:31 -04006623 none:
James Ketrenos43f66a62005-03-25 12:31:53 -06006624 spin_unlock(&priv->lock);
6625 return IRQ_NONE;
6626}
6627
6628static void ipw_rf_kill(void *adapter)
6629{
6630 struct ipw_priv *priv = adapter;
6631 unsigned long flags;
Jeff Garzikbf794512005-07-31 13:07:26 -04006632
James Ketrenos43f66a62005-03-25 12:31:53 -06006633 spin_lock_irqsave(&priv->lock, flags);
6634
6635 if (rf_kill_active(priv)) {
6636 IPW_DEBUG_RF_KILL("RF Kill active, rescheduling GPIO check\n");
6637 if (priv->workqueue)
6638 queue_delayed_work(priv->workqueue,
6639 &priv->rf_kill, 2 * HZ);
6640 goto exit_unlock;
6641 }
6642
6643 /* RF Kill is now disabled, so bring the device back up */
6644
6645 if (!(priv->status & STATUS_RF_KILL_MASK)) {
6646 IPW_DEBUG_RF_KILL("HW RF Kill no longer active, restarting "
6647 "device\n");
6648
6649 /* we can not do an adapter restart while inside an irq lock */
6650 queue_work(priv->workqueue, &priv->adapter_restart);
Jeff Garzikbf794512005-07-31 13:07:26 -04006651 } else
James Ketrenos43f66a62005-03-25 12:31:53 -06006652 IPW_DEBUG_RF_KILL("HW RF Kill deactivated. SW RF Kill still "
6653 "enabled\n");
6654
Jeff Garzik0edd5b42005-09-07 00:48:31 -04006655 exit_unlock:
James Ketrenos43f66a62005-03-25 12:31:53 -06006656 spin_unlock_irqrestore(&priv->lock, flags);
6657}
6658
6659static int ipw_setup_deferred_work(struct ipw_priv *priv)
6660{
6661 int ret = 0;
6662
James Ketrenos43f66a62005-03-25 12:31:53 -06006663 priv->workqueue = create_workqueue(DRV_NAME);
James Ketrenos43f66a62005-03-25 12:31:53 -06006664 init_waitqueue_head(&priv->wait_command_queue);
6665
6666 INIT_WORK(&priv->adhoc_check, ipw_adhoc_check, priv);
6667 INIT_WORK(&priv->associate, ipw_associate, priv);
6668 INIT_WORK(&priv->disassociate, ipw_disassociate, priv);
6669 INIT_WORK(&priv->rx_replenish, ipw_rx_queue_replenish, priv);
6670 INIT_WORK(&priv->adapter_restart, ipw_adapter_restart, priv);
6671 INIT_WORK(&priv->rf_kill, ipw_rf_kill, priv);
6672 INIT_WORK(&priv->up, (void (*)(void *))ipw_up, priv);
6673 INIT_WORK(&priv->down, (void (*)(void *))ipw_down, priv);
Jeff Garzikbf794512005-07-31 13:07:26 -04006674 INIT_WORK(&priv->request_scan,
James Ketrenos43f66a62005-03-25 12:31:53 -06006675 (void (*)(void *))ipw_request_scan, priv);
Jeff Garzikbf794512005-07-31 13:07:26 -04006676 INIT_WORK(&priv->gather_stats,
James Ketrenos43f66a62005-03-25 12:31:53 -06006677 (void (*)(void *))ipw_gather_stats, priv);
6678 INIT_WORK(&priv->abort_scan, (void (*)(void *))ipw_abort_scan, priv);
6679 INIT_WORK(&priv->roam, ipw_roam, priv);
6680 INIT_WORK(&priv->scan_check, ipw_scan_check, priv);
6681
6682 tasklet_init(&priv->irq_tasklet, (void (*)(unsigned long))
6683 ipw_irq_tasklet, (unsigned long)priv);
6684
6685 return ret;
6686}
6687
James Ketrenos43f66a62005-03-25 12:31:53 -06006688static void shim__set_security(struct net_device *dev,
6689 struct ieee80211_security *sec)
6690{
6691 struct ipw_priv *priv = ieee80211_priv(dev);
6692 int i;
6693
Jeff Garzikbf794512005-07-31 13:07:26 -04006694 for (i = 0; i < 4; i++) {
James Ketrenos43f66a62005-03-25 12:31:53 -06006695 if (sec->flags & (1 << i)) {
6696 priv->sec.key_sizes[i] = sec->key_sizes[i];
6697 if (sec->key_sizes[i] == 0)
6698 priv->sec.flags &= ~(1 << i);
6699 else
Jeff Garzikbf794512005-07-31 13:07:26 -04006700 memcpy(priv->sec.keys[i], sec->keys[i],
James Ketrenos43f66a62005-03-25 12:31:53 -06006701 sec->key_sizes[i]);
6702 priv->sec.flags |= (1 << i);
6703 priv->status |= STATUS_SECURITY_UPDATED;
Jeff Garzikbf794512005-07-31 13:07:26 -04006704 }
James Ketrenos43f66a62005-03-25 12:31:53 -06006705 }
6706
6707 if ((sec->flags & SEC_ACTIVE_KEY) &&
6708 priv->sec.active_key != sec->active_key) {
6709 if (sec->active_key <= 3) {
6710 priv->sec.active_key = sec->active_key;
6711 priv->sec.flags |= SEC_ACTIVE_KEY;
Jeff Garzikbf794512005-07-31 13:07:26 -04006712 } else
James Ketrenos43f66a62005-03-25 12:31:53 -06006713 priv->sec.flags &= ~SEC_ACTIVE_KEY;
6714 priv->status |= STATUS_SECURITY_UPDATED;
6715 }
6716
6717 if ((sec->flags & SEC_AUTH_MODE) &&
6718 (priv->sec.auth_mode != sec->auth_mode)) {
6719 priv->sec.auth_mode = sec->auth_mode;
6720 priv->sec.flags |= SEC_AUTH_MODE;
6721 if (sec->auth_mode == WLAN_AUTH_SHARED_KEY)
6722 priv->capability |= CAP_SHARED_KEY;
6723 else
6724 priv->capability &= ~CAP_SHARED_KEY;
6725 priv->status |= STATUS_SECURITY_UPDATED;
6726 }
Jeff Garzikbf794512005-07-31 13:07:26 -04006727
Jeff Garzik0edd5b42005-09-07 00:48:31 -04006728 if (sec->flags & SEC_ENABLED && priv->sec.enabled != sec->enabled) {
James Ketrenos43f66a62005-03-25 12:31:53 -06006729 priv->sec.flags |= SEC_ENABLED;
6730 priv->sec.enabled = sec->enabled;
6731 priv->status |= STATUS_SECURITY_UPDATED;
Jeff Garzikbf794512005-07-31 13:07:26 -04006732 if (sec->enabled)
James Ketrenos43f66a62005-03-25 12:31:53 -06006733 priv->capability |= CAP_PRIVACY_ON;
6734 else
6735 priv->capability &= ~CAP_PRIVACY_ON;
6736 }
Jeff Garzikbf794512005-07-31 13:07:26 -04006737
Jeff Garzik0edd5b42005-09-07 00:48:31 -04006738 if (sec->flags & SEC_LEVEL && priv->sec.level != sec->level) {
James Ketrenos43f66a62005-03-25 12:31:53 -06006739 priv->sec.level = sec->level;
6740 priv->sec.flags |= SEC_LEVEL;
6741 priv->status |= STATUS_SECURITY_UPDATED;
6742 }
6743
Jeff Garzikbf794512005-07-31 13:07:26 -04006744 /* To match current functionality of ipw2100 (which works well w/
6745 * various supplicants, we don't force a disassociate if the
James Ketrenos43f66a62005-03-25 12:31:53 -06006746 * privacy capability changes ... */
6747#if 0
6748 if ((priv->status & (STATUS_ASSOCIATED | STATUS_ASSOCIATING)) &&
Jeff Garzikbf794512005-07-31 13:07:26 -04006749 (((priv->assoc_request.capability &
James Ketrenos43f66a62005-03-25 12:31:53 -06006750 WLAN_CAPABILITY_PRIVACY) && !sec->enabled) ||
Jeff Garzikbf794512005-07-31 13:07:26 -04006751 (!(priv->assoc_request.capability &
Jeff Garzik0edd5b42005-09-07 00:48:31 -04006752 WLAN_CAPABILITY_PRIVACY) && sec->enabled))) {
James Ketrenos43f66a62005-03-25 12:31:53 -06006753 IPW_DEBUG_ASSOC("Disassociating due to capability "
6754 "change.\n");
6755 ipw_disassociate(priv);
6756 }
6757#endif
6758}
6759
Jeff Garzikbf794512005-07-31 13:07:26 -04006760static int init_supported_rates(struct ipw_priv *priv,
James Ketrenos43f66a62005-03-25 12:31:53 -06006761 struct ipw_supported_rates *rates)
6762{
6763 /* TODO: Mask out rates based on priv->rates_mask */
6764
6765 memset(rates, 0, sizeof(*rates));
Jeff Garzik0edd5b42005-09-07 00:48:31 -04006766 /* configure supported rates */
James Ketrenos43f66a62005-03-25 12:31:53 -06006767 switch (priv->ieee->freq_band) {
6768 case IEEE80211_52GHZ_BAND:
6769 rates->ieee_mode = IPW_A_MODE;
6770 rates->purpose = IPW_RATE_CAPABILITIES;
6771 ipw_add_ofdm_scan_rates(rates, IEEE80211_CCK_MODULATION,
6772 IEEE80211_OFDM_DEFAULT_RATES_MASK);
6773 break;
6774
Jeff Garzik0edd5b42005-09-07 00:48:31 -04006775 default: /* Mixed or 2.4Ghz */
James Ketrenos43f66a62005-03-25 12:31:53 -06006776 rates->ieee_mode = IPW_G_MODE;
6777 rates->purpose = IPW_RATE_CAPABILITIES;
6778 ipw_add_cck_scan_rates(rates, IEEE80211_CCK_MODULATION,
6779 IEEE80211_CCK_DEFAULT_RATES_MASK);
6780 if (priv->ieee->modulation & IEEE80211_OFDM_MODULATION) {
6781 ipw_add_ofdm_scan_rates(rates, IEEE80211_CCK_MODULATION,
6782 IEEE80211_OFDM_DEFAULT_RATES_MASK);
6783 }
6784 break;
6785 }
6786
6787 return 0;
6788}
6789
Jeff Garzikbf794512005-07-31 13:07:26 -04006790static int ipw_config(struct ipw_priv *priv)
James Ketrenos43f66a62005-03-25 12:31:53 -06006791{
6792 int i;
6793 struct ipw_tx_power tx_power;
6794
6795 memset(&priv->sys_config, 0, sizeof(priv->sys_config));
6796 memset(&tx_power, 0, sizeof(tx_power));
6797
6798 /* This is only called from ipw_up, which resets/reloads the firmware
6799 so, we don't need to first disable the card before we configure
6800 it */
6801
6802 /* configure device for 'G' band */
6803 tx_power.ieee_mode = IPW_G_MODE;
6804 tx_power.num_channels = 11;
6805 for (i = 0; i < 11; i++) {
6806 tx_power.channels_tx_power[i].channel_number = i + 1;
6807 tx_power.channels_tx_power[i].tx_power = priv->tx_power;
6808 }
6809 if (ipw_send_tx_power(priv, &tx_power))
6810 goto error;
6811
6812 /* configure device to also handle 'B' band */
6813 tx_power.ieee_mode = IPW_B_MODE;
6814 if (ipw_send_tx_power(priv, &tx_power))
6815 goto error;
6816
6817 /* initialize adapter address */
6818 if (ipw_send_adapter_address(priv, priv->net_dev->dev_addr))
6819 goto error;
6820
6821 /* set basic system config settings */
6822 init_sys_config(&priv->sys_config);
6823 if (ipw_send_system_config(priv, &priv->sys_config))
6824 goto error;
6825
Jeff Garzik0edd5b42005-09-07 00:48:31 -04006826 init_supported_rates(priv, &priv->rates);
6827 if (ipw_send_supported_rates(priv, &priv->rates))
James Ketrenos43f66a62005-03-25 12:31:53 -06006828 goto error;
6829
6830 /* Set request-to-send threshold */
6831 if (priv->rts_threshold) {
6832 if (ipw_send_rts_threshold(priv, priv->rts_threshold))
6833 goto error;
6834 }
6835
6836 if (ipw_set_random_seed(priv))
6837 goto error;
Jeff Garzikbf794512005-07-31 13:07:26 -04006838
James Ketrenos43f66a62005-03-25 12:31:53 -06006839 /* final state transition to the RUN state */
6840 if (ipw_send_host_complete(priv))
6841 goto error;
6842
6843 /* If configured to try and auto-associate, kick off a scan */
6844 if ((priv->config & CFG_ASSOCIATE) && ipw_request_scan(priv))
6845 goto error;
6846
6847 return 0;
Jeff Garzikbf794512005-07-31 13:07:26 -04006848
Jeff Garzik0edd5b42005-09-07 00:48:31 -04006849 error:
James Ketrenos43f66a62005-03-25 12:31:53 -06006850 return -EIO;
6851}
6852
6853#define MAX_HW_RESTARTS 5
6854static int ipw_up(struct ipw_priv *priv)
6855{
6856 int rc, i;
6857
6858 if (priv->status & STATUS_EXIT_PENDING)
6859 return -EIO;
6860
Jeff Garzik0edd5b42005-09-07 00:48:31 -04006861 for (i = 0; i < MAX_HW_RESTARTS; i++) {
Jeff Garzikbf794512005-07-31 13:07:26 -04006862 /* Load the microcode, firmware, and eeprom.
James Ketrenos43f66a62005-03-25 12:31:53 -06006863 * Also start the clocks. */
6864 rc = ipw_load(priv);
6865 if (rc) {
Jeff Garzik0edd5b42005-09-07 00:48:31 -04006866 IPW_ERROR("Unable to load firmware: 0x%08X\n", rc);
James Ketrenos43f66a62005-03-25 12:31:53 -06006867 return rc;
6868 }
6869
6870 ipw_init_ordinals(priv);
6871 if (!(priv->config & CFG_CUSTOM_MAC))
6872 eeprom_parse_mac(priv, priv->mac_addr);
6873 memcpy(priv->net_dev->dev_addr, priv->mac_addr, ETH_ALEN);
6874
6875 if (priv->status & STATUS_RF_KILL_MASK)
6876 return 0;
6877
6878 rc = ipw_config(priv);
6879 if (!rc) {
6880 IPW_DEBUG_INFO("Configured device on count %i\n", i);
6881 priv->notif_missed_beacons = 0;
6882 netif_start_queue(priv->net_dev);
6883 return 0;
6884 } else {
6885 IPW_DEBUG_INFO("Device configuration failed: 0x%08X\n",
6886 rc);
6887 }
Jeff Garzikbf794512005-07-31 13:07:26 -04006888
James Ketrenos43f66a62005-03-25 12:31:53 -06006889 IPW_DEBUG_INFO("Failed to config device on retry %d of %d\n",
6890 i, MAX_HW_RESTARTS);
6891
6892 /* We had an error bringing up the hardware, so take it
6893 * all the way back down so we can try again */
6894 ipw_down(priv);
6895 }
6896
Jeff Garzikbf794512005-07-31 13:07:26 -04006897 /* tried to restart and config the device for as long as our
James Ketrenos43f66a62005-03-25 12:31:53 -06006898 * patience could withstand */
Jeff Garzik0edd5b42005-09-07 00:48:31 -04006899 IPW_ERROR("Unable to initialize device after %d attempts.\n", i);
James Ketrenos43f66a62005-03-25 12:31:53 -06006900 return -EIO;
6901}
6902
6903static void ipw_down(struct ipw_priv *priv)
6904{
6905 /* Attempt to disable the card */
6906#if 0
6907 ipw_send_card_disable(priv, 0);
6908#endif
6909
6910 /* tell the device to stop sending interrupts */
6911 ipw_disable_interrupts(priv);
6912
6913 /* Clear all bits but the RF Kill */
6914 priv->status &= STATUS_RF_KILL_MASK;
6915
6916 netif_carrier_off(priv->net_dev);
6917 netif_stop_queue(priv->net_dev);
6918
6919 ipw_stop_nic(priv);
6920}
6921
6922/* Called by register_netdev() */
6923static int ipw_net_init(struct net_device *dev)
6924{
6925 struct ipw_priv *priv = ieee80211_priv(dev);
6926
6927 if (priv->status & STATUS_RF_KILL_SW) {
6928 IPW_WARNING("Radio disabled by module parameter.\n");
6929 return 0;
6930 } else if (rf_kill_active(priv)) {
6931 IPW_WARNING("Radio Frequency Kill Switch is On:\n"
6932 "Kill switch must be turned off for "
6933 "wireless networking to work.\n");
6934 queue_delayed_work(priv->workqueue, &priv->rf_kill, 2 * HZ);
6935 return 0;
6936 }
6937
6938 if (ipw_up(priv))
6939 return -EIO;
6940
6941 return 0;
6942}
6943
6944/* PCI driver stuff */
6945static struct pci_device_id card_ids[] = {
6946 {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2701, 0, 0, 0},
6947 {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2702, 0, 0, 0},
6948 {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2711, 0, 0, 0},
6949 {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2712, 0, 0, 0},
6950 {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2721, 0, 0, 0},
6951 {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2722, 0, 0, 0},
6952 {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2731, 0, 0, 0},
6953 {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2732, 0, 0, 0},
6954 {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2741, 0, 0, 0},
6955 {PCI_VENDOR_ID_INTEL, 0x1043, 0x103c, 0x2741, 0, 0, 0},
6956 {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2742, 0, 0, 0},
6957 {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2751, 0, 0, 0},
6958 {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2752, 0, 0, 0},
6959 {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2753, 0, 0, 0},
6960 {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2754, 0, 0, 0},
6961 {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2761, 0, 0, 0},
6962 {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2762, 0, 0, 0},
6963 {PCI_VENDOR_ID_INTEL, 0x104f, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
Jeff Garzik0edd5b42005-09-07 00:48:31 -04006964 {PCI_VENDOR_ID_INTEL, 0x4220, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, /* BG */
6965 {PCI_VENDOR_ID_INTEL, 0x4221, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, /* 2225BG */
6966 {PCI_VENDOR_ID_INTEL, 0x4223, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, /* ABG */
6967 {PCI_VENDOR_ID_INTEL, 0x4224, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, /* ABG */
Jeff Garzikbf794512005-07-31 13:07:26 -04006968
James Ketrenos43f66a62005-03-25 12:31:53 -06006969 /* required last entry */
6970 {0,}
6971};
6972
6973MODULE_DEVICE_TABLE(pci, card_ids);
6974
6975static struct attribute *ipw_sysfs_entries[] = {
6976 &dev_attr_rf_kill.attr,
6977 &dev_attr_direct_dword.attr,
6978 &dev_attr_indirect_byte.attr,
6979 &dev_attr_indirect_dword.attr,
6980 &dev_attr_mem_gpio_reg.attr,
6981 &dev_attr_command_event_reg.attr,
6982 &dev_attr_nic_type.attr,
6983 &dev_attr_status.attr,
6984 &dev_attr_cfg.attr,
6985 &dev_attr_dump_errors.attr,
6986 &dev_attr_dump_events.attr,
6987 &dev_attr_eeprom_delay.attr,
6988 &dev_attr_ucode_version.attr,
6989 &dev_attr_rtc.attr,
6990 NULL
6991};
6992
6993static struct attribute_group ipw_attribute_group = {
6994 .name = NULL, /* put in device directory */
Jeff Garzik0edd5b42005-09-07 00:48:31 -04006995 .attrs = ipw_sysfs_entries,
James Ketrenos43f66a62005-03-25 12:31:53 -06006996};
6997
Jeff Garzik0edd5b42005-09-07 00:48:31 -04006998static int ipw_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
James Ketrenos43f66a62005-03-25 12:31:53 -06006999{
7000 int err = 0;
7001 struct net_device *net_dev;
7002 void __iomem *base;
7003 u32 length, val;
7004 struct ipw_priv *priv;
7005 int band, modulation;
7006
7007 net_dev = alloc_ieee80211(sizeof(struct ipw_priv));
7008 if (net_dev == NULL) {
7009 err = -ENOMEM;
7010 goto out;
7011 }
7012
7013 priv = ieee80211_priv(net_dev);
7014 priv->ieee = netdev_priv(net_dev);
7015 priv->net_dev = net_dev;
7016 priv->pci_dev = pdev;
7017#ifdef CONFIG_IPW_DEBUG
7018 ipw_debug_level = debug;
7019#endif
7020 spin_lock_init(&priv->lock);
7021
7022 if (pci_enable_device(pdev)) {
7023 err = -ENODEV;
7024 goto out_free_ieee80211;
7025 }
7026
7027 pci_set_master(pdev);
7028
Tobias Klauser0e08b442005-06-20 14:28:41 -07007029 err = pci_set_dma_mask(pdev, DMA_32BIT_MASK);
Jeff Garzikbf794512005-07-31 13:07:26 -04007030 if (!err)
Tobias Klauser0e08b442005-06-20 14:28:41 -07007031 err = pci_set_consistent_dma_mask(pdev, DMA_32BIT_MASK);
James Ketrenos43f66a62005-03-25 12:31:53 -06007032 if (err) {
7033 printk(KERN_WARNING DRV_NAME ": No suitable DMA available.\n");
7034 goto out_pci_disable_device;
7035 }
7036
7037 pci_set_drvdata(pdev, priv);
7038
7039 err = pci_request_regions(pdev, DRV_NAME);
Jeff Garzikbf794512005-07-31 13:07:26 -04007040 if (err)
James Ketrenos43f66a62005-03-25 12:31:53 -06007041 goto out_pci_disable_device;
7042
Jeff Garzikbf794512005-07-31 13:07:26 -04007043 /* We disable the RETRY_TIMEOUT register (0x41) to keep
James Ketrenos43f66a62005-03-25 12:31:53 -06007044 * PCI Tx retries from interfering with C3 CPU state */
Jeff Garzikbf794512005-07-31 13:07:26 -04007045 pci_read_config_dword(pdev, 0x40, &val);
7046 if ((val & 0x0000ff00) != 0)
James Ketrenos43f66a62005-03-25 12:31:53 -06007047 pci_write_config_dword(pdev, 0x40, val & 0xffff00ff);
Jeff Garzikbf794512005-07-31 13:07:26 -04007048
James Ketrenos43f66a62005-03-25 12:31:53 -06007049 length = pci_resource_len(pdev, 0);
7050 priv->hw_len = length;
Jeff Garzikbf794512005-07-31 13:07:26 -04007051
James Ketrenos43f66a62005-03-25 12:31:53 -06007052 base = ioremap_nocache(pci_resource_start(pdev, 0), length);
7053 if (!base) {
7054 err = -ENODEV;
7055 goto out_pci_release_regions;
7056 }
7057
7058 priv->hw_base = base;
7059 IPW_DEBUG_INFO("pci_resource_len = 0x%08x\n", length);
7060 IPW_DEBUG_INFO("pci_resource_base = %p\n", base);
7061
7062 err = ipw_setup_deferred_work(priv);
7063 if (err) {
7064 IPW_ERROR("Unable to setup deferred work\n");
7065 goto out_iounmap;
7066 }
7067
7068 /* Initialize module parameter values here */
7069 if (ifname)
7070 strncpy(net_dev->name, ifname, IFNAMSIZ);
7071
Jeff Garzikbf794512005-07-31 13:07:26 -04007072 if (associate)
James Ketrenos43f66a62005-03-25 12:31:53 -06007073 priv->config |= CFG_ASSOCIATE;
7074 else
7075 IPW_DEBUG_INFO("Auto associate disabled.\n");
Jeff Garzikbf794512005-07-31 13:07:26 -04007076
7077 if (auto_create)
James Ketrenos43f66a62005-03-25 12:31:53 -06007078 priv->config |= CFG_ADHOC_CREATE;
7079 else
7080 IPW_DEBUG_INFO("Auto adhoc creation disabled.\n");
Jeff Garzikbf794512005-07-31 13:07:26 -04007081
James Ketrenos43f66a62005-03-25 12:31:53 -06007082 if (disable) {
7083 priv->status |= STATUS_RF_KILL_SW;
7084 IPW_DEBUG_INFO("Radio disabled.\n");
7085 }
7086
7087 if (channel != 0) {
7088 priv->config |= CFG_STATIC_CHANNEL;
7089 priv->channel = channel;
7090 IPW_DEBUG_INFO("Bind to static channel %d\n", channel);
Jeff Garzik0edd5b42005-09-07 00:48:31 -04007091 IPW_DEBUG_INFO("Bind to static channel %d\n", channel);
James Ketrenos43f66a62005-03-25 12:31:53 -06007092 /* TODO: Validate that provided channel is in range */
7093 }
7094
7095 switch (mode) {
7096 case 1:
7097 priv->ieee->iw_mode = IW_MODE_ADHOC;
7098 break;
Jeff Garzikbf794512005-07-31 13:07:26 -04007099#ifdef CONFIG_IPW_PROMISC
James Ketrenos43f66a62005-03-25 12:31:53 -06007100 case 2:
7101 priv->ieee->iw_mode = IW_MODE_MONITOR;
7102 break;
7103#endif
7104 default:
7105 case 0:
7106 priv->ieee->iw_mode = IW_MODE_INFRA;
7107 break;
7108 }
7109
7110 if ((priv->pci_dev->device == 0x4223) ||
7111 (priv->pci_dev->device == 0x4224)) {
Jeff Garzikbf794512005-07-31 13:07:26 -04007112 printk(KERN_INFO DRV_NAME
James Ketrenos43f66a62005-03-25 12:31:53 -06007113 ": Detected Intel PRO/Wireless 2915ABG Network "
7114 "Connection\n");
James Ketrenosa33a1982005-09-14 14:28:59 -05007115 priv->ieee->abg_true = 1;
James Ketrenos43f66a62005-03-25 12:31:53 -06007116 band = IEEE80211_52GHZ_BAND | IEEE80211_24GHZ_BAND;
7117 modulation = IEEE80211_OFDM_MODULATION |
Jeff Garzik0edd5b42005-09-07 00:48:31 -04007118 IEEE80211_CCK_MODULATION;
James Ketrenos43f66a62005-03-25 12:31:53 -06007119 priv->adapter = IPW_2915ABG;
Jeff Garzik0edd5b42005-09-07 00:48:31 -04007120 priv->ieee->mode = IEEE_A | IEEE_G | IEEE_B;
James Ketrenos43f66a62005-03-25 12:31:53 -06007121 } else {
Jeff Garzikbf794512005-07-31 13:07:26 -04007122 if (priv->pci_dev->device == 0x4221)
7123 printk(KERN_INFO DRV_NAME
James Ketrenos43f66a62005-03-25 12:31:53 -06007124 ": Detected Intel PRO/Wireless 2225BG Network "
7125 "Connection\n");
7126 else
Jeff Garzikbf794512005-07-31 13:07:26 -04007127 printk(KERN_INFO DRV_NAME
James Ketrenos43f66a62005-03-25 12:31:53 -06007128 ": Detected Intel PRO/Wireless 2200BG Network "
7129 "Connection\n");
Jeff Garzikbf794512005-07-31 13:07:26 -04007130
James Ketrenosa33a1982005-09-14 14:28:59 -05007131 priv->ieee->abg_true = 0;
James Ketrenos43f66a62005-03-25 12:31:53 -06007132 band = IEEE80211_24GHZ_BAND;
7133 modulation = IEEE80211_OFDM_MODULATION |
Jeff Garzik0edd5b42005-09-07 00:48:31 -04007134 IEEE80211_CCK_MODULATION;
James Ketrenos43f66a62005-03-25 12:31:53 -06007135 priv->adapter = IPW_2200BG;
Jeff Garzik0edd5b42005-09-07 00:48:31 -04007136 priv->ieee->mode = IEEE_G | IEEE_B;
James Ketrenos43f66a62005-03-25 12:31:53 -06007137 }
7138
7139 priv->ieee->freq_band = band;
7140 priv->ieee->modulation = modulation;
7141
7142 priv->rates_mask = IEEE80211_DEFAULT_RATES_MASK;
7143
7144 priv->missed_beacon_threshold = IPW_MB_DISASSOCIATE_THRESHOLD_DEFAULT;
7145 priv->roaming_threshold = IPW_MB_ROAMING_THRESHOLD_DEFAULT;
7146
7147 priv->rts_threshold = DEFAULT_RTS_THRESHOLD;
7148
7149 /* If power management is turned on, default to AC mode */
Jeff Garzik0edd5b42005-09-07 00:48:31 -04007150 priv->power_mode = IPW_POWER_AC;
James Ketrenos43f66a62005-03-25 12:31:53 -06007151 priv->tx_power = IPW_DEFAULT_TX_POWER;
7152
Jeff Garzik0edd5b42005-09-07 00:48:31 -04007153 err = request_irq(pdev->irq, ipw_isr, SA_SHIRQ, DRV_NAME, priv);
James Ketrenos43f66a62005-03-25 12:31:53 -06007154 if (err) {
7155 IPW_ERROR("Error allocating IRQ %d\n", pdev->irq);
7156 goto out_destroy_workqueue;
7157 }
7158
7159 SET_MODULE_OWNER(net_dev);
7160 SET_NETDEV_DEV(net_dev, &pdev->dev);
7161
7162 priv->ieee->hard_start_xmit = ipw_net_hard_start_xmit;
7163 priv->ieee->set_security = shim__set_security;
7164
7165 net_dev->open = ipw_net_open;
7166 net_dev->stop = ipw_net_stop;
7167 net_dev->init = ipw_net_init;
7168 net_dev->get_stats = ipw_net_get_stats;
7169 net_dev->set_multicast_list = ipw_net_set_multicast_list;
7170 net_dev->set_mac_address = ipw_net_set_mac_address;
7171 net_dev->get_wireless_stats = ipw_get_wireless_stats;
7172 net_dev->wireless_handlers = &ipw_wx_handler_def;
7173 net_dev->ethtool_ops = &ipw_ethtool_ops;
7174 net_dev->irq = pdev->irq;
Jeff Garzik0edd5b42005-09-07 00:48:31 -04007175 net_dev->base_addr = (unsigned long)priv->hw_base;
James Ketrenos43f66a62005-03-25 12:31:53 -06007176 net_dev->mem_start = pci_resource_start(pdev, 0);
7177 net_dev->mem_end = net_dev->mem_start + pci_resource_len(pdev, 0) - 1;
7178
7179 err = sysfs_create_group(&pdev->dev.kobj, &ipw_attribute_group);
7180 if (err) {
7181 IPW_ERROR("failed to create sysfs device attributes\n");
7182 goto out_release_irq;
7183 }
7184
7185 err = register_netdev(net_dev);
7186 if (err) {
7187 IPW_ERROR("failed to register network device\n");
7188 goto out_remove_group;
7189 }
7190
7191 return 0;
7192
Jeff Garzik0edd5b42005-09-07 00:48:31 -04007193 out_remove_group:
James Ketrenos43f66a62005-03-25 12:31:53 -06007194 sysfs_remove_group(&pdev->dev.kobj, &ipw_attribute_group);
Jeff Garzik0edd5b42005-09-07 00:48:31 -04007195 out_release_irq:
James Ketrenos43f66a62005-03-25 12:31:53 -06007196 free_irq(pdev->irq, priv);
Jeff Garzik0edd5b42005-09-07 00:48:31 -04007197 out_destroy_workqueue:
James Ketrenos43f66a62005-03-25 12:31:53 -06007198 destroy_workqueue(priv->workqueue);
7199 priv->workqueue = NULL;
Jeff Garzik0edd5b42005-09-07 00:48:31 -04007200 out_iounmap:
James Ketrenos43f66a62005-03-25 12:31:53 -06007201 iounmap(priv->hw_base);
Jeff Garzik0edd5b42005-09-07 00:48:31 -04007202 out_pci_release_regions:
James Ketrenos43f66a62005-03-25 12:31:53 -06007203 pci_release_regions(pdev);
Jeff Garzik0edd5b42005-09-07 00:48:31 -04007204 out_pci_disable_device:
James Ketrenos43f66a62005-03-25 12:31:53 -06007205 pci_disable_device(pdev);
7206 pci_set_drvdata(pdev, NULL);
Jeff Garzik0edd5b42005-09-07 00:48:31 -04007207 out_free_ieee80211:
James Ketrenos43f66a62005-03-25 12:31:53 -06007208 free_ieee80211(priv->net_dev);
Jeff Garzik0edd5b42005-09-07 00:48:31 -04007209 out:
James Ketrenos43f66a62005-03-25 12:31:53 -06007210 return err;
7211}
7212
7213static void ipw_pci_remove(struct pci_dev *pdev)
7214{
7215 struct ipw_priv *priv = pci_get_drvdata(pdev);
7216 if (!priv)
7217 return;
7218
7219 priv->status |= STATUS_EXIT_PENDING;
7220
7221 sysfs_remove_group(&pdev->dev.kobj, &ipw_attribute_group);
7222
7223 ipw_down(priv);
7224
7225 unregister_netdev(priv->net_dev);
7226
7227 if (priv->rxq) {
7228 ipw_rx_queue_free(priv, priv->rxq);
7229 priv->rxq = NULL;
7230 }
7231 ipw_tx_queue_free(priv);
7232
7233 /* ipw_down will ensure that there is no more pending work
7234 * in the workqueue's, so we can safely remove them now. */
Jeff Garzikbf794512005-07-31 13:07:26 -04007235 if (priv->workqueue) {
James Ketrenos43f66a62005-03-25 12:31:53 -06007236 cancel_delayed_work(&priv->adhoc_check);
7237 cancel_delayed_work(&priv->gather_stats);
7238 cancel_delayed_work(&priv->request_scan);
7239 cancel_delayed_work(&priv->rf_kill);
7240 cancel_delayed_work(&priv->scan_check);
7241 destroy_workqueue(priv->workqueue);
7242 priv->workqueue = NULL;
7243 }
7244
7245 free_irq(pdev->irq, priv);
7246 iounmap(priv->hw_base);
7247 pci_release_regions(pdev);
7248 pci_disable_device(pdev);
7249 pci_set_drvdata(pdev, NULL);
7250 free_ieee80211(priv->net_dev);
7251
7252#ifdef CONFIG_PM
7253 if (fw_loaded) {
7254 release_firmware(bootfw);
7255 release_firmware(ucode);
7256 release_firmware(firmware);
7257 fw_loaded = 0;
7258 }
7259#endif
7260}
7261
James Ketrenos43f66a62005-03-25 12:31:53 -06007262#ifdef CONFIG_PM
Pavel Machek583a4e82005-09-03 15:56:58 -07007263static int ipw_pci_suspend(struct pci_dev *pdev, pm_message_t state)
James Ketrenos43f66a62005-03-25 12:31:53 -06007264{
7265 struct ipw_priv *priv = pci_get_drvdata(pdev);
7266 struct net_device *dev = priv->net_dev;
7267
7268 printk(KERN_INFO "%s: Going into suspend...\n", dev->name);
7269
Jeff Garzik0edd5b42005-09-07 00:48:31 -04007270 /* Take down the device; powers it off, etc. */
James Ketrenos43f66a62005-03-25 12:31:53 -06007271 ipw_down(priv);
7272
7273 /* Remove the PRESENT state of the device */
7274 netif_device_detach(dev);
7275
James Ketrenos43f66a62005-03-25 12:31:53 -06007276 pci_save_state(pdev);
James Ketrenos43f66a62005-03-25 12:31:53 -06007277 pci_disable_device(pdev);
Pavel Machek583a4e82005-09-03 15:56:58 -07007278 pci_set_power_state(pdev, pci_choose_state(pdev, state));
Jeff Garzikbf794512005-07-31 13:07:26 -04007279
James Ketrenos43f66a62005-03-25 12:31:53 -06007280 return 0;
7281}
7282
7283static int ipw_pci_resume(struct pci_dev *pdev)
7284{
7285 struct ipw_priv *priv = pci_get_drvdata(pdev);
7286 struct net_device *dev = priv->net_dev;
7287 u32 val;
Jeff Garzikbf794512005-07-31 13:07:26 -04007288
James Ketrenos43f66a62005-03-25 12:31:53 -06007289 printk(KERN_INFO "%s: Coming out of suspend...\n", dev->name);
7290
7291 pci_set_power_state(pdev, 0);
7292 pci_enable_device(pdev);
7293#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,10)
7294 pci_restore_state(pdev, priv->pm_state);
7295#else
7296 pci_restore_state(pdev);
7297#endif
7298 /*
7299 * Suspend/Resume resets the PCI configuration space, so we have to
7300 * re-disable the RETRY_TIMEOUT register (0x41) to keep PCI Tx retries
7301 * from interfering with C3 CPU state. pci_restore_state won't help
7302 * here since it only restores the first 64 bytes pci config header.
7303 */
Jeff Garzikbf794512005-07-31 13:07:26 -04007304 pci_read_config_dword(pdev, 0x40, &val);
7305 if ((val & 0x0000ff00) != 0)
James Ketrenos43f66a62005-03-25 12:31:53 -06007306 pci_write_config_dword(pdev, 0x40, val & 0xffff00ff);
7307
7308 /* Set the device back into the PRESENT state; this will also wake
7309 * the queue of needed */
7310 netif_device_attach(dev);
7311
7312 /* Bring the device back up */
7313 queue_work(priv->workqueue, &priv->up);
Jeff Garzikbf794512005-07-31 13:07:26 -04007314
James Ketrenos43f66a62005-03-25 12:31:53 -06007315 return 0;
7316}
7317#endif
7318
7319/* driver initialization stuff */
7320static struct pci_driver ipw_driver = {
7321 .name = DRV_NAME,
7322 .id_table = card_ids,
7323 .probe = ipw_pci_probe,
7324 .remove = __devexit_p(ipw_pci_remove),
7325#ifdef CONFIG_PM
7326 .suspend = ipw_pci_suspend,
7327 .resume = ipw_pci_resume,
7328#endif
7329};
7330
7331static int __init ipw_init(void)
7332{
7333 int ret;
7334
7335 printk(KERN_INFO DRV_NAME ": " DRV_DESCRIPTION ", " DRV_VERSION "\n");
7336 printk(KERN_INFO DRV_NAME ": " DRV_COPYRIGHT "\n");
7337
7338 ret = pci_module_init(&ipw_driver);
7339 if (ret) {
7340 IPW_ERROR("Unable to initialize PCI module\n");
7341 return ret;
7342 }
7343
Jeff Garzik0edd5b42005-09-07 00:48:31 -04007344 ret = driver_create_file(&ipw_driver.driver, &driver_attr_debug_level);
James Ketrenos43f66a62005-03-25 12:31:53 -06007345 if (ret) {
7346 IPW_ERROR("Unable to create driver sysfs file\n");
7347 pci_unregister_driver(&ipw_driver);
7348 return ret;
7349 }
7350
7351 return ret;
7352}
7353
7354static void __exit ipw_exit(void)
7355{
7356 driver_remove_file(&ipw_driver.driver, &driver_attr_debug_level);
7357 pci_unregister_driver(&ipw_driver);
7358}
7359
7360module_param(disable, int, 0444);
7361MODULE_PARM_DESC(disable, "manually disable the radio (default 0 [radio on])");
7362
7363module_param(associate, int, 0444);
7364MODULE_PARM_DESC(associate, "auto associate when scanning (default on)");
7365
7366module_param(auto_create, int, 0444);
7367MODULE_PARM_DESC(auto_create, "auto create adhoc network (default on)");
7368
7369module_param(debug, int, 0444);
7370MODULE_PARM_DESC(debug, "debug output mask");
7371
7372module_param(channel, int, 0444);
Jeff Garzikbf794512005-07-31 13:07:26 -04007373MODULE_PARM_DESC(channel, "channel to limit associate to (default 0 [ANY])");
James Ketrenos43f66a62005-03-25 12:31:53 -06007374
7375module_param(ifname, charp, 0444);
7376MODULE_PARM_DESC(ifname, "network device name (default eth%d)");
7377
Jeff Garzikbf794512005-07-31 13:07:26 -04007378#ifdef CONFIG_IPW_PROMISC
James Ketrenos43f66a62005-03-25 12:31:53 -06007379module_param(mode, int, 0444);
7380MODULE_PARM_DESC(mode, "network mode (0=BSS,1=IBSS,2=Monitor)");
7381#else
7382module_param(mode, int, 0444);
7383MODULE_PARM_DESC(mode, "network mode (0=BSS,1=IBSS)");
7384#endif
7385
7386module_exit(ipw_exit);
7387module_init(ipw_init);