blob: 0a583afbcddf1f7d1246928d47d0ef02847b90fd [file] [log] [blame]
James Ketrenos43f66a62005-03-25 12:31:53 -06001/******************************************************************************
Jeff Garzikbf794512005-07-31 13:07:26 -04002
James Ketrenos43f66a62005-03-25 12:31:53 -06003 Copyright(c) 2003 - 2004 Intel Corporation. All rights reserved.
4
5 802.11 status code portion of this file from ethereal-0.10.6:
6 Copyright 2000, Axis Communications AB
7 Ethereal - Network traffic analyzer
8 By Gerald Combs <gerald@ethereal.com>
9 Copyright 1998 Gerald Combs
10
Jeff Garzikbf794512005-07-31 13:07:26 -040011 This program is free software; you can redistribute it and/or modify it
12 under the terms of version 2 of the GNU General Public License as
James Ketrenos43f66a62005-03-25 12:31:53 -060013 published by the Free Software Foundation.
Jeff Garzikbf794512005-07-31 13:07:26 -040014
15 This program is distributed in the hope that it will be useful, but WITHOUT
16 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
17 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
James Ketrenos43f66a62005-03-25 12:31:53 -060018 more details.
Jeff Garzikbf794512005-07-31 13:07:26 -040019
James Ketrenos43f66a62005-03-25 12:31:53 -060020 You should have received a copy of the GNU General Public License along with
Jeff Garzikbf794512005-07-31 13:07:26 -040021 this program; if not, write to the Free Software Foundation, Inc., 59
James Ketrenos43f66a62005-03-25 12:31:53 -060022 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
Jeff Garzikbf794512005-07-31 13:07:26 -040023
James Ketrenos43f66a62005-03-25 12:31:53 -060024 The full GNU General Public License is included in this distribution in the
25 file called LICENSE.
Jeff Garzikbf794512005-07-31 13:07:26 -040026
James Ketrenos43f66a62005-03-25 12:31:53 -060027 Contact Information:
28 James P. Ketrenos <ipw2100-admin@linux.intel.com>
29 Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
30
31******************************************************************************/
32
33#include "ipw2200.h"
34
James Ketrenosb095c382005-08-24 22:04:42 -050035#define IPW2200_VERSION "1.0.4"
James Ketrenos43f66a62005-03-25 12:31:53 -060036#define DRV_DESCRIPTION "Intel(R) PRO/Wireless 2200/2915 Network Driver"
37#define DRV_COPYRIGHT "Copyright(c) 2003-2004 Intel Corporation"
38#define DRV_VERSION IPW2200_VERSION
39
James Ketrenosb095c382005-08-24 22:04:42 -050040#define ETH_P_80211_STATS (ETH_P_80211_RAW + 1)
41
James Ketrenos43f66a62005-03-25 12:31:53 -060042MODULE_DESCRIPTION(DRV_DESCRIPTION);
43MODULE_VERSION(DRV_VERSION);
44MODULE_AUTHOR(DRV_COPYRIGHT);
45MODULE_LICENSE("GPL");
46
47static int debug = 0;
48static int channel = 0;
James Ketrenos43f66a62005-03-25 12:31:53 -060049static int mode = 0;
50
51static u32 ipw_debug_level;
52static int associate = 1;
53static int auto_create = 1;
James Ketrenosa613bff2005-08-24 21:43:11 -050054static int led = 0;
James Ketrenos43f66a62005-03-25 12:31:53 -060055static int disable = 0;
James Ketrenosb095c382005-08-24 22:04:42 -050056static int hwcrypto = 1;
James Ketrenos43f66a62005-03-25 12:31:53 -060057static const char ipw_modes[] = {
58 'a', 'b', 'g', '?'
59};
60
James Ketrenosb095c382005-08-24 22:04:42 -050061#ifdef CONFIG_IPW_QOS
62static int qos_enable = 0;
63static int qos_burst_enable = 0;
64static int qos_no_ack_mask = 0;
65static int burst_duration_CCK = 0;
66static int burst_duration_OFDM = 0;
67
68static struct ieee80211_qos_parameters def_qos_parameters_OFDM = {
69 {QOS_TX0_CW_MIN_OFDM, QOS_TX1_CW_MIN_OFDM, QOS_TX2_CW_MIN_OFDM,
70 QOS_TX3_CW_MIN_OFDM},
71 {QOS_TX0_CW_MAX_OFDM, QOS_TX1_CW_MAX_OFDM, QOS_TX2_CW_MAX_OFDM,
72 QOS_TX3_CW_MAX_OFDM},
73 {QOS_TX0_AIFS, QOS_TX1_AIFS, QOS_TX2_AIFS, QOS_TX3_AIFS},
74 {QOS_TX0_ACM, QOS_TX1_ACM, QOS_TX2_ACM, QOS_TX3_ACM},
75 {QOS_TX0_TXOP_LIMIT_OFDM, QOS_TX1_TXOP_LIMIT_OFDM,
76 QOS_TX2_TXOP_LIMIT_OFDM, QOS_TX3_TXOP_LIMIT_OFDM}
77};
78
79static struct ieee80211_qos_parameters def_qos_parameters_CCK = {
80 {QOS_TX0_CW_MIN_CCK, QOS_TX1_CW_MIN_CCK, QOS_TX2_CW_MIN_CCK,
81 QOS_TX3_CW_MIN_CCK},
82 {QOS_TX0_CW_MAX_CCK, QOS_TX1_CW_MAX_CCK, QOS_TX2_CW_MAX_CCK,
83 QOS_TX3_CW_MAX_CCK},
84 {QOS_TX0_AIFS, QOS_TX1_AIFS, QOS_TX2_AIFS, QOS_TX3_AIFS},
85 {QOS_TX0_ACM, QOS_TX1_ACM, QOS_TX2_ACM, QOS_TX3_ACM},
86 {QOS_TX0_TXOP_LIMIT_CCK, QOS_TX1_TXOP_LIMIT_CCK, QOS_TX2_TXOP_LIMIT_CCK,
87 QOS_TX3_TXOP_LIMIT_CCK}
88};
89
90static struct ieee80211_qos_parameters def_parameters_OFDM = {
91 {DEF_TX0_CW_MIN_OFDM, DEF_TX1_CW_MIN_OFDM, DEF_TX2_CW_MIN_OFDM,
92 DEF_TX3_CW_MIN_OFDM},
93 {DEF_TX0_CW_MAX_OFDM, DEF_TX1_CW_MAX_OFDM, DEF_TX2_CW_MAX_OFDM,
94 DEF_TX3_CW_MAX_OFDM},
95 {DEF_TX0_AIFS, DEF_TX1_AIFS, DEF_TX2_AIFS, DEF_TX3_AIFS},
96 {DEF_TX0_ACM, DEF_TX1_ACM, DEF_TX2_ACM, DEF_TX3_ACM},
97 {DEF_TX0_TXOP_LIMIT_OFDM, DEF_TX1_TXOP_LIMIT_OFDM,
98 DEF_TX2_TXOP_LIMIT_OFDM, DEF_TX3_TXOP_LIMIT_OFDM}
99};
100
101static struct ieee80211_qos_parameters def_parameters_CCK = {
102 {DEF_TX0_CW_MIN_CCK, DEF_TX1_CW_MIN_CCK, DEF_TX2_CW_MIN_CCK,
103 DEF_TX3_CW_MIN_CCK},
104 {DEF_TX0_CW_MAX_CCK, DEF_TX1_CW_MAX_CCK, DEF_TX2_CW_MAX_CCK,
105 DEF_TX3_CW_MAX_CCK},
106 {DEF_TX0_AIFS, DEF_TX1_AIFS, DEF_TX2_AIFS, DEF_TX3_AIFS},
107 {DEF_TX0_ACM, DEF_TX1_ACM, DEF_TX2_ACM, DEF_TX3_ACM},
108 {DEF_TX0_TXOP_LIMIT_CCK, DEF_TX1_TXOP_LIMIT_CCK, DEF_TX2_TXOP_LIMIT_CCK,
109 DEF_TX3_TXOP_LIMIT_CCK}
110};
111
112static u8 qos_oui[QOS_OUI_LEN] = { 0x00, 0x50, 0xF2 };
113
114static int from_priority_to_tx_queue[] = {
115 IPW_TX_QUEUE_1, IPW_TX_QUEUE_2, IPW_TX_QUEUE_2, IPW_TX_QUEUE_1,
116 IPW_TX_QUEUE_3, IPW_TX_QUEUE_3, IPW_TX_QUEUE_4, IPW_TX_QUEUE_4
117};
118
119static u32 ipw_qos_get_burst_duration(struct ipw_priv *priv);
120
121static int ipw_send_qos_params_command(struct ipw_priv *priv, struct ieee80211_qos_parameters
122 *qos_param);
123static int ipw_send_qos_info_command(struct ipw_priv *priv, struct ieee80211_qos_information_element
124 *qos_param);
125#endif /* CONFIG_IPW_QOS */
126
127static void ipw_remove_current_network(struct ipw_priv *priv);
James Ketrenos43f66a62005-03-25 12:31:53 -0600128static void ipw_rx(struct ipw_priv *priv);
Jeff Garzikbf794512005-07-31 13:07:26 -0400129static int ipw_queue_tx_reclaim(struct ipw_priv *priv,
James Ketrenos43f66a62005-03-25 12:31:53 -0600130 struct clx2_tx_queue *txq, int qindex);
131static int ipw_queue_reset(struct ipw_priv *priv);
132
133static int ipw_queue_tx_hcmd(struct ipw_priv *priv, int hcmd, void *buf,
134 int len, int sync);
135
136static void ipw_tx_queue_free(struct ipw_priv *);
137
138static struct ipw_rx_queue *ipw_rx_queue_alloc(struct ipw_priv *);
139static void ipw_rx_queue_free(struct ipw_priv *, struct ipw_rx_queue *);
140static void ipw_rx_queue_replenish(void *);
James Ketrenos43f66a62005-03-25 12:31:53 -0600141static int ipw_up(struct ipw_priv *);
James Ketrenosc848d0a2005-08-24 21:56:24 -0500142static void ipw_bg_up(void *);
James Ketrenos43f66a62005-03-25 12:31:53 -0600143static void ipw_down(struct ipw_priv *);
James Ketrenosc848d0a2005-08-24 21:56:24 -0500144static void ipw_bg_down(void *);
James Ketrenos43f66a62005-03-25 12:31:53 -0600145static int ipw_config(struct ipw_priv *);
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400146static int init_supported_rates(struct ipw_priv *priv,
147 struct ipw_supported_rates *prates);
James Ketrenosb095c382005-08-24 22:04:42 -0500148static void ipw_set_hwcrypto_keys(struct ipw_priv *);
149static void ipw_send_wep_keys(struct ipw_priv *, int);
James Ketrenos43f66a62005-03-25 12:31:53 -0600150
Jeff Garzikbf794512005-07-31 13:07:26 -0400151static char *snprint_line(char *buf, size_t count,
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400152 const u8 * data, u32 len, u32 ofs)
James Ketrenos43f66a62005-03-25 12:31:53 -0600153{
154 int out, i, j, l;
155 char c;
Jeff Garzikbf794512005-07-31 13:07:26 -0400156
James Ketrenos43f66a62005-03-25 12:31:53 -0600157 out = snprintf(buf, count, "%08X", ofs);
158
159 for (l = 0, i = 0; i < 2; i++) {
160 out += snprintf(buf + out, count - out, " ");
Jeff Garzikbf794512005-07-31 13:07:26 -0400161 for (j = 0; j < 8 && l < len; j++, l++)
162 out += snprintf(buf + out, count - out, "%02X ",
James Ketrenos43f66a62005-03-25 12:31:53 -0600163 data[(i * 8 + j)]);
164 for (; j < 8; j++)
165 out += snprintf(buf + out, count - out, " ");
166 }
Jeff Garzikbf794512005-07-31 13:07:26 -0400167
James Ketrenos43f66a62005-03-25 12:31:53 -0600168 out += snprintf(buf + out, count - out, " ");
169 for (l = 0, i = 0; i < 2; i++) {
170 out += snprintf(buf + out, count - out, " ");
171 for (j = 0; j < 8 && l < len; j++, l++) {
172 c = data[(i * 8 + j)];
173 if (!isascii(c) || !isprint(c))
174 c = '.';
Jeff Garzikbf794512005-07-31 13:07:26 -0400175
James Ketrenos43f66a62005-03-25 12:31:53 -0600176 out += snprintf(buf + out, count - out, "%c", c);
177 }
178
179 for (; j < 8; j++)
180 out += snprintf(buf + out, count - out, " ");
181 }
Jeff Garzikbf794512005-07-31 13:07:26 -0400182
James Ketrenos43f66a62005-03-25 12:31:53 -0600183 return buf;
184}
185
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400186static void printk_buf(int level, const u8 * data, u32 len)
James Ketrenos43f66a62005-03-25 12:31:53 -0600187{
188 char line[81];
189 u32 ofs = 0;
190 if (!(ipw_debug_level & level))
191 return;
192
193 while (len) {
194 printk(KERN_DEBUG "%s\n",
Jeff Garzikbf794512005-07-31 13:07:26 -0400195 snprint_line(line, sizeof(line), &data[ofs],
James Ketrenos43f66a62005-03-25 12:31:53 -0600196 min(len, 16U), ofs));
197 ofs += 16;
198 len -= min(len, 16U);
199 }
200}
201
202static u32 _ipw_read_reg32(struct ipw_priv *priv, u32 reg);
203#define ipw_read_reg32(a, b) _ipw_read_reg32(a, b)
204
205static u8 _ipw_read_reg8(struct ipw_priv *ipw, u32 reg);
206#define ipw_read_reg8(a, b) _ipw_read_reg8(a, b)
207
208static void _ipw_write_reg8(struct ipw_priv *priv, u32 reg, u8 value);
209static inline void ipw_write_reg8(struct ipw_priv *a, u32 b, u8 c)
210{
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400211 IPW_DEBUG_IO("%s %d: write_indirect8(0x%08X, 0x%08X)\n", __FILE__,
212 __LINE__, (u32) (b), (u32) (c));
James Ketrenos43f66a62005-03-25 12:31:53 -0600213 _ipw_write_reg8(a, b, c);
214}
215
216static void _ipw_write_reg16(struct ipw_priv *priv, u32 reg, u16 value);
217static inline void ipw_write_reg16(struct ipw_priv *a, u32 b, u16 c)
218{
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400219 IPW_DEBUG_IO("%s %d: write_indirect16(0x%08X, 0x%08X)\n", __FILE__,
220 __LINE__, (u32) (b), (u32) (c));
James Ketrenos43f66a62005-03-25 12:31:53 -0600221 _ipw_write_reg16(a, b, c);
222}
223
224static void _ipw_write_reg32(struct ipw_priv *priv, u32 reg, u32 value);
225static inline void ipw_write_reg32(struct ipw_priv *a, u32 b, u32 c)
226{
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400227 IPW_DEBUG_IO("%s %d: write_indirect32(0x%08X, 0x%08X)\n", __FILE__,
228 __LINE__, (u32) (b), (u32) (c));
James Ketrenos43f66a62005-03-25 12:31:53 -0600229 _ipw_write_reg32(a, b, c);
230}
231
232#define _ipw_write8(ipw, ofs, val) writeb((val), (ipw)->hw_base + (ofs))
233#define ipw_write8(ipw, ofs, val) \
234 IPW_DEBUG_IO("%s %d: write_direct8(0x%08X, 0x%08X)\n", __FILE__, __LINE__, (u32)(ofs), (u32)(val)); \
235 _ipw_write8(ipw, ofs, val)
236
237#define _ipw_write16(ipw, ofs, val) writew((val), (ipw)->hw_base + (ofs))
238#define ipw_write16(ipw, ofs, val) \
239 IPW_DEBUG_IO("%s %d: write_direct16(0x%08X, 0x%08X)\n", __FILE__, __LINE__, (u32)(ofs), (u32)(val)); \
240 _ipw_write16(ipw, ofs, val)
241
242#define _ipw_write32(ipw, ofs, val) writel((val), (ipw)->hw_base + (ofs))
243#define ipw_write32(ipw, ofs, val) \
244 IPW_DEBUG_IO("%s %d: write_direct32(0x%08X, 0x%08X)\n", __FILE__, __LINE__, (u32)(ofs), (u32)(val)); \
245 _ipw_write32(ipw, ofs, val)
246
247#define _ipw_read8(ipw, ofs) readb((ipw)->hw_base + (ofs))
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400248static inline u8 __ipw_read8(char *f, u32 l, struct ipw_priv *ipw, u32 ofs)
249{
250 IPW_DEBUG_IO("%s %d: read_direct8(0x%08X)\n", f, l, (u32) (ofs));
James Ketrenos43f66a62005-03-25 12:31:53 -0600251 return _ipw_read8(ipw, ofs);
252}
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400253
James Ketrenos43f66a62005-03-25 12:31:53 -0600254#define ipw_read8(ipw, ofs) __ipw_read8(__FILE__, __LINE__, ipw, ofs)
255
256#define _ipw_read16(ipw, ofs) readw((ipw)->hw_base + (ofs))
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400257static inline u16 __ipw_read16(char *f, u32 l, struct ipw_priv *ipw, u32 ofs)
258{
259 IPW_DEBUG_IO("%s %d: read_direct16(0x%08X)\n", f, l, (u32) (ofs));
James Ketrenos43f66a62005-03-25 12:31:53 -0600260 return _ipw_read16(ipw, ofs);
261}
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400262
James Ketrenos43f66a62005-03-25 12:31:53 -0600263#define ipw_read16(ipw, ofs) __ipw_read16(__FILE__, __LINE__, ipw, ofs)
264
265#define _ipw_read32(ipw, ofs) readl((ipw)->hw_base + (ofs))
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400266static inline u32 __ipw_read32(char *f, u32 l, struct ipw_priv *ipw, u32 ofs)
267{
268 IPW_DEBUG_IO("%s %d: read_direct32(0x%08X)\n", f, l, (u32) (ofs));
James Ketrenos43f66a62005-03-25 12:31:53 -0600269 return _ipw_read32(ipw, ofs);
270}
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400271
James Ketrenos43f66a62005-03-25 12:31:53 -0600272#define ipw_read32(ipw, ofs) __ipw_read32(__FILE__, __LINE__, ipw, ofs)
273
274static void _ipw_read_indirect(struct ipw_priv *, u32, u8 *, int);
275#define ipw_read_indirect(a, b, c, d) \
276 IPW_DEBUG_IO("%s %d: read_inddirect(0x%08X) %d bytes\n", __FILE__, __LINE__, (u32)(b), d); \
277 _ipw_read_indirect(a, b, c, d)
278
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400279static void _ipw_write_indirect(struct ipw_priv *priv, u32 addr, u8 * data,
280 int num);
James Ketrenos43f66a62005-03-25 12:31:53 -0600281#define ipw_write_indirect(a, b, c, d) \
282 IPW_DEBUG_IO("%s %d: write_indirect(0x%08X) %d bytes\n", __FILE__, __LINE__, (u32)(b), d); \
283 _ipw_write_indirect(a, b, c, d)
284
285/* indirect write s */
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400286static void _ipw_write_reg32(struct ipw_priv *priv, u32 reg, u32 value)
James Ketrenos43f66a62005-03-25 12:31:53 -0600287{
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400288 IPW_DEBUG_IO(" %p : reg = 0x%8X : value = 0x%8X\n", priv, reg, value);
James Ketrenosb095c382005-08-24 22:04:42 -0500289 _ipw_write32(priv, IPW_INDIRECT_ADDR, reg);
290 _ipw_write32(priv, IPW_INDIRECT_DATA, value);
James Ketrenos43f66a62005-03-25 12:31:53 -0600291}
292
James Ketrenos43f66a62005-03-25 12:31:53 -0600293static void _ipw_write_reg8(struct ipw_priv *priv, u32 reg, u8 value)
294{
295 IPW_DEBUG_IO(" reg = 0x%8X : value = 0x%8X\n", reg, value);
James Ketrenosb095c382005-08-24 22:04:42 -0500296 _ipw_write32(priv, IPW_INDIRECT_ADDR, reg & IPW_INDIRECT_ADDR_MASK);
297 _ipw_write8(priv, IPW_INDIRECT_DATA, value);
Jeff Garzikbf794512005-07-31 13:07:26 -0400298 IPW_DEBUG_IO(" reg = 0x%8lX : value = 0x%8X\n",
James Ketrenosb095c382005-08-24 22:04:42 -0500299 (unsigned long)(priv->hw_base + IPW_INDIRECT_DATA), value);
James Ketrenos43f66a62005-03-25 12:31:53 -0600300}
301
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400302static void _ipw_write_reg16(struct ipw_priv *priv, u32 reg, u16 value)
James Ketrenos43f66a62005-03-25 12:31:53 -0600303{
304 IPW_DEBUG_IO(" reg = 0x%8X : value = 0x%8X\n", reg, value);
James Ketrenosb095c382005-08-24 22:04:42 -0500305 _ipw_write32(priv, IPW_INDIRECT_ADDR, reg & IPW_INDIRECT_ADDR_MASK);
306 _ipw_write16(priv, IPW_INDIRECT_DATA, value);
James Ketrenos43f66a62005-03-25 12:31:53 -0600307}
308
309/* indirect read s */
310
311static u8 _ipw_read_reg8(struct ipw_priv *priv, u32 reg)
312{
313 u32 word;
James Ketrenosb095c382005-08-24 22:04:42 -0500314 _ipw_write32(priv, IPW_INDIRECT_ADDR, reg & IPW_INDIRECT_ADDR_MASK);
James Ketrenos43f66a62005-03-25 12:31:53 -0600315 IPW_DEBUG_IO(" reg = 0x%8X : \n", reg);
James Ketrenosb095c382005-08-24 22:04:42 -0500316 word = _ipw_read32(priv, IPW_INDIRECT_DATA);
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400317 return (word >> ((reg & 0x3) * 8)) & 0xff;
James Ketrenos43f66a62005-03-25 12:31:53 -0600318}
319
320static u32 _ipw_read_reg32(struct ipw_priv *priv, u32 reg)
321{
322 u32 value;
323
324 IPW_DEBUG_IO("%p : reg = 0x%08x\n", priv, reg);
325
James Ketrenosb095c382005-08-24 22:04:42 -0500326 _ipw_write32(priv, IPW_INDIRECT_ADDR, reg);
327 value = _ipw_read32(priv, IPW_INDIRECT_DATA);
James Ketrenos43f66a62005-03-25 12:31:53 -0600328 IPW_DEBUG_IO(" reg = 0x%4X : value = 0x%4x \n", reg, value);
329 return value;
330}
331
332/* iterative/auto-increment 32 bit reads and writes */
333static void _ipw_read_indirect(struct ipw_priv *priv, u32 addr, u8 * buf,
334 int num)
335{
James Ketrenosb095c382005-08-24 22:04:42 -0500336 u32 aligned_addr = addr & IPW_INDIRECT_ADDR_MASK;
James Ketrenos43f66a62005-03-25 12:31:53 -0600337 u32 dif_len = addr - aligned_addr;
James Ketrenos43f66a62005-03-25 12:31:53 -0600338 u32 i;
Jeff Garzikbf794512005-07-31 13:07:26 -0400339
James Ketrenos43f66a62005-03-25 12:31:53 -0600340 IPW_DEBUG_IO("addr = %i, buf = %p, num = %i\n", addr, buf, num);
341
James Ketrenosea2b26e2005-08-24 21:25:16 -0500342 if (num <= 0) {
343 return;
344 }
345
James Ketrenos43f66a62005-03-25 12:31:53 -0600346 /* Read the first nibble byte by byte */
347 if (unlikely(dif_len)) {
James Ketrenosb095c382005-08-24 22:04:42 -0500348 _ipw_write32(priv, IPW_INDIRECT_ADDR, aligned_addr);
James Ketrenosea2b26e2005-08-24 21:25:16 -0500349 /* Start reading at aligned_addr + dif_len */
350 for (i = dif_len; ((i < 4) && (num > 0)); i++, num--)
James Ketrenosb095c382005-08-24 22:04:42 -0500351 *buf++ = _ipw_read8(priv, IPW_INDIRECT_DATA + i);
James Ketrenos43f66a62005-03-25 12:31:53 -0600352 aligned_addr += 4;
353 }
354
James Ketrenosb095c382005-08-24 22:04:42 -0500355 _ipw_write32(priv, IPW_AUTOINC_ADDR, aligned_addr);
James Ketrenosea2b26e2005-08-24 21:25:16 -0500356 for (; num >= 4; buf += 4, aligned_addr += 4, num -= 4)
James Ketrenosb095c382005-08-24 22:04:42 -0500357 *(u32 *) buf = _ipw_read32(priv, IPW_AUTOINC_DATA);
Jeff Garzikbf794512005-07-31 13:07:26 -0400358
James Ketrenos43f66a62005-03-25 12:31:53 -0600359 /* Copy the last nibble */
James Ketrenosea2b26e2005-08-24 21:25:16 -0500360 if (unlikely(num)) {
James Ketrenosb095c382005-08-24 22:04:42 -0500361 _ipw_write32(priv, IPW_INDIRECT_ADDR, aligned_addr);
James Ketrenosea2b26e2005-08-24 21:25:16 -0500362 for (i = 0; num > 0; i++, num--)
James Ketrenosb095c382005-08-24 22:04:42 -0500363 *buf++ = ipw_read8(priv, IPW_INDIRECT_DATA + i);
James Ketrenosea2b26e2005-08-24 21:25:16 -0500364 }
James Ketrenos43f66a62005-03-25 12:31:53 -0600365}
366
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400367static void _ipw_write_indirect(struct ipw_priv *priv, u32 addr, u8 * buf,
James Ketrenos43f66a62005-03-25 12:31:53 -0600368 int num)
369{
James Ketrenosb095c382005-08-24 22:04:42 -0500370 u32 aligned_addr = addr & IPW_INDIRECT_ADDR_MASK;
James Ketrenos43f66a62005-03-25 12:31:53 -0600371 u32 dif_len = addr - aligned_addr;
James Ketrenos43f66a62005-03-25 12:31:53 -0600372 u32 i;
Jeff Garzikbf794512005-07-31 13:07:26 -0400373
James Ketrenos43f66a62005-03-25 12:31:53 -0600374 IPW_DEBUG_IO("addr = %i, buf = %p, num = %i\n", addr, buf, num);
Jeff Garzikbf794512005-07-31 13:07:26 -0400375
James Ketrenosea2b26e2005-08-24 21:25:16 -0500376 if (num <= 0) {
377 return;
378 }
379
James Ketrenos43f66a62005-03-25 12:31:53 -0600380 /* Write the first nibble byte by byte */
381 if (unlikely(dif_len)) {
James Ketrenosb095c382005-08-24 22:04:42 -0500382 _ipw_write32(priv, IPW_INDIRECT_ADDR, aligned_addr);
James Ketrenosea2b26e2005-08-24 21:25:16 -0500383 /* Start reading at aligned_addr + dif_len */
384 for (i = dif_len; ((i < 4) && (num > 0)); i++, num--, buf++)
James Ketrenosb095c382005-08-24 22:04:42 -0500385 _ipw_write8(priv, IPW_INDIRECT_DATA + i, *buf);
James Ketrenos43f66a62005-03-25 12:31:53 -0600386 aligned_addr += 4;
387 }
Jeff Garzikbf794512005-07-31 13:07:26 -0400388
James Ketrenosb095c382005-08-24 22:04:42 -0500389 _ipw_write32(priv, IPW_AUTOINC_ADDR, aligned_addr);
James Ketrenosea2b26e2005-08-24 21:25:16 -0500390 for (; num >= 4; buf += 4, aligned_addr += 4, num -= 4)
James Ketrenosb095c382005-08-24 22:04:42 -0500391 _ipw_write32(priv, IPW_AUTOINC_DATA, *(u32 *) buf);
Jeff Garzikbf794512005-07-31 13:07:26 -0400392
James Ketrenos43f66a62005-03-25 12:31:53 -0600393 /* Copy the last nibble */
James Ketrenosea2b26e2005-08-24 21:25:16 -0500394 if (unlikely(num)) {
James Ketrenosb095c382005-08-24 22:04:42 -0500395 _ipw_write32(priv, IPW_INDIRECT_ADDR, aligned_addr);
James Ketrenosea2b26e2005-08-24 21:25:16 -0500396 for (i = 0; num > 0; i++, num--, buf++)
James Ketrenosb095c382005-08-24 22:04:42 -0500397 _ipw_write8(priv, IPW_INDIRECT_DATA + i, *buf);
James Ketrenosea2b26e2005-08-24 21:25:16 -0500398 }
James Ketrenos43f66a62005-03-25 12:31:53 -0600399}
400
Jeff Garzikbf794512005-07-31 13:07:26 -0400401static void ipw_write_direct(struct ipw_priv *priv, u32 addr, void *buf,
James Ketrenos43f66a62005-03-25 12:31:53 -0600402 int num)
403{
404 memcpy_toio((priv->hw_base + addr), buf, num);
405}
406
407static inline void ipw_set_bit(struct ipw_priv *priv, u32 reg, u32 mask)
408{
409 ipw_write32(priv, reg, ipw_read32(priv, reg) | mask);
410}
411
412static inline void ipw_clear_bit(struct ipw_priv *priv, u32 reg, u32 mask)
413{
414 ipw_write32(priv, reg, ipw_read32(priv, reg) & ~mask);
415}
416
417static inline void ipw_enable_interrupts(struct ipw_priv *priv)
418{
419 if (priv->status & STATUS_INT_ENABLED)
420 return;
421 priv->status |= STATUS_INT_ENABLED;
James Ketrenosb095c382005-08-24 22:04:42 -0500422 ipw_write32(priv, IPW_INTA_MASK_R, IPW_INTA_MASK_ALL);
James Ketrenos43f66a62005-03-25 12:31:53 -0600423}
424
425static inline void ipw_disable_interrupts(struct ipw_priv *priv)
426{
427 if (!(priv->status & STATUS_INT_ENABLED))
428 return;
429 priv->status &= ~STATUS_INT_ENABLED;
James Ketrenosb095c382005-08-24 22:04:42 -0500430 ipw_write32(priv, IPW_INTA_MASK_R, ~IPW_INTA_MASK_ALL);
James Ketrenos43f66a62005-03-25 12:31:53 -0600431}
432
433static char *ipw_error_desc(u32 val)
434{
435 switch (val) {
Jeff Garzikbf794512005-07-31 13:07:26 -0400436 case IPW_FW_ERROR_OK:
James Ketrenos43f66a62005-03-25 12:31:53 -0600437 return "ERROR_OK";
Jeff Garzikbf794512005-07-31 13:07:26 -0400438 case IPW_FW_ERROR_FAIL:
James Ketrenos43f66a62005-03-25 12:31:53 -0600439 return "ERROR_FAIL";
Jeff Garzikbf794512005-07-31 13:07:26 -0400440 case IPW_FW_ERROR_MEMORY_UNDERFLOW:
James Ketrenos43f66a62005-03-25 12:31:53 -0600441 return "MEMORY_UNDERFLOW";
Jeff Garzikbf794512005-07-31 13:07:26 -0400442 case IPW_FW_ERROR_MEMORY_OVERFLOW:
James Ketrenos43f66a62005-03-25 12:31:53 -0600443 return "MEMORY_OVERFLOW";
Jeff Garzikbf794512005-07-31 13:07:26 -0400444 case IPW_FW_ERROR_BAD_PARAM:
James Ketrenosb095c382005-08-24 22:04:42 -0500445 return "BAD_PARAM";
Jeff Garzikbf794512005-07-31 13:07:26 -0400446 case IPW_FW_ERROR_BAD_CHECKSUM:
James Ketrenosb095c382005-08-24 22:04:42 -0500447 return "BAD_CHECKSUM";
Jeff Garzikbf794512005-07-31 13:07:26 -0400448 case IPW_FW_ERROR_NMI_INTERRUPT:
James Ketrenosb095c382005-08-24 22:04:42 -0500449 return "NMI_INTERRUPT";
Jeff Garzikbf794512005-07-31 13:07:26 -0400450 case IPW_FW_ERROR_BAD_DATABASE:
James Ketrenosb095c382005-08-24 22:04:42 -0500451 return "BAD_DATABASE";
Jeff Garzikbf794512005-07-31 13:07:26 -0400452 case IPW_FW_ERROR_ALLOC_FAIL:
James Ketrenosb095c382005-08-24 22:04:42 -0500453 return "ALLOC_FAIL";
Jeff Garzikbf794512005-07-31 13:07:26 -0400454 case IPW_FW_ERROR_DMA_UNDERRUN:
James Ketrenosb095c382005-08-24 22:04:42 -0500455 return "DMA_UNDERRUN";
Jeff Garzikbf794512005-07-31 13:07:26 -0400456 case IPW_FW_ERROR_DMA_STATUS:
James Ketrenosb095c382005-08-24 22:04:42 -0500457 return "DMA_STATUS";
458 case IPW_FW_ERROR_DINO_ERROR:
459 return "DINO_ERROR";
460 case IPW_FW_ERROR_EEPROM_ERROR:
461 return "EEPROM_ERROR";
Jeff Garzikbf794512005-07-31 13:07:26 -0400462 case IPW_FW_ERROR_SYSASSERT:
James Ketrenosb095c382005-08-24 22:04:42 -0500463 return "SYSASSERT";
Jeff Garzikbf794512005-07-31 13:07:26 -0400464 case IPW_FW_ERROR_FATAL_ERROR:
James Ketrenosb095c382005-08-24 22:04:42 -0500465 return "FATAL_ERROR";
Jeff Garzikbf794512005-07-31 13:07:26 -0400466 default:
James Ketrenosb095c382005-08-24 22:04:42 -0500467 return "UNKNOWN_ERROR";
James Ketrenos43f66a62005-03-25 12:31:53 -0600468 }
469}
470
471static void ipw_dump_nic_error_log(struct ipw_priv *priv)
472{
473 u32 desc, time, blink1, blink2, ilink1, ilink2, idata, i, count, base;
474
475 base = ipw_read32(priv, IPWSTATUS_ERROR_LOG);
476 count = ipw_read_reg32(priv, base);
Jeff Garzikbf794512005-07-31 13:07:26 -0400477
James Ketrenos43f66a62005-03-25 12:31:53 -0600478 if (ERROR_START_OFFSET <= count * ERROR_ELEM_SIZE) {
479 IPW_ERROR("Start IPW Error Log Dump:\n");
480 IPW_ERROR("Status: 0x%08X, Config: %08X\n",
481 priv->status, priv->config);
482 }
483
Jeff Garzikbf794512005-07-31 13:07:26 -0400484 for (i = ERROR_START_OFFSET;
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400485 i <= count * ERROR_ELEM_SIZE; i += ERROR_ELEM_SIZE) {
486 desc = ipw_read_reg32(priv, base + i);
487 time = ipw_read_reg32(priv, base + i + 1 * sizeof(u32));
488 blink1 = ipw_read_reg32(priv, base + i + 2 * sizeof(u32));
489 blink2 = ipw_read_reg32(priv, base + i + 3 * sizeof(u32));
490 ilink1 = ipw_read_reg32(priv, base + i + 4 * sizeof(u32));
491 ilink2 = ipw_read_reg32(priv, base + i + 5 * sizeof(u32));
492 idata = ipw_read_reg32(priv, base + i + 6 * sizeof(u32));
James Ketrenos43f66a62005-03-25 12:31:53 -0600493
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400494 IPW_ERROR("%s %i 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x\n",
495 ipw_error_desc(desc), time, blink1, blink2,
496 ilink1, ilink2, idata);
James Ketrenos43f66a62005-03-25 12:31:53 -0600497 }
498}
499
500static void ipw_dump_nic_event_log(struct ipw_priv *priv)
501{
502 u32 ev, time, data, i, count, base;
503
504 base = ipw_read32(priv, IPW_EVENT_LOG);
505 count = ipw_read_reg32(priv, base);
Jeff Garzikbf794512005-07-31 13:07:26 -0400506
James Ketrenos43f66a62005-03-25 12:31:53 -0600507 if (EVENT_START_OFFSET <= count * EVENT_ELEM_SIZE)
508 IPW_ERROR("Start IPW Event Log Dump:\n");
509
Jeff Garzikbf794512005-07-31 13:07:26 -0400510 for (i = EVENT_START_OFFSET;
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400511 i <= count * EVENT_ELEM_SIZE; i += EVENT_ELEM_SIZE) {
James Ketrenos43f66a62005-03-25 12:31:53 -0600512 ev = ipw_read_reg32(priv, base + i);
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400513 time = ipw_read_reg32(priv, base + i + 1 * sizeof(u32));
514 data = ipw_read_reg32(priv, base + i + 2 * sizeof(u32));
James Ketrenos43f66a62005-03-25 12:31:53 -0600515
516#ifdef CONFIG_IPW_DEBUG
517 IPW_ERROR("%i\t0x%08x\t%i\n", time, data, ev);
518#endif
519 }
520}
521
James Ketrenosc848d0a2005-08-24 21:56:24 -0500522static inline int ipw_is_init(struct ipw_priv *priv)
523{
524 return (priv->status & STATUS_INIT) ? 1 : 0;
525}
526
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400527static int ipw_get_ordinal(struct ipw_priv *priv, u32 ord, void *val, u32 * len)
James Ketrenos43f66a62005-03-25 12:31:53 -0600528{
529 u32 addr, field_info, field_len, field_count, total_len;
530
531 IPW_DEBUG_ORD("ordinal = %i\n", ord);
532
533 if (!priv || !val || !len) {
534 IPW_DEBUG_ORD("Invalid argument\n");
535 return -EINVAL;
536 }
Jeff Garzikbf794512005-07-31 13:07:26 -0400537
James Ketrenos43f66a62005-03-25 12:31:53 -0600538 /* verify device ordinal tables have been initialized */
539 if (!priv->table0_addr || !priv->table1_addr || !priv->table2_addr) {
540 IPW_DEBUG_ORD("Access ordinals before initialization\n");
541 return -EINVAL;
542 }
543
544 switch (IPW_ORD_TABLE_ID_MASK & ord) {
545 case IPW_ORD_TABLE_0_MASK:
546 /*
547 * TABLE 0: Direct access to a table of 32 bit values
548 *
Jeff Garzikbf794512005-07-31 13:07:26 -0400549 * This is a very simple table with the data directly
James Ketrenos43f66a62005-03-25 12:31:53 -0600550 * read from the table
551 */
552
553 /* remove the table id from the ordinal */
554 ord &= IPW_ORD_TABLE_VALUE_MASK;
555
556 /* boundary check */
557 if (ord > priv->table0_len) {
558 IPW_DEBUG_ORD("ordinal value (%i) longer then "
559 "max (%i)\n", ord, priv->table0_len);
560 return -EINVAL;
561 }
562
563 /* verify we have enough room to store the value */
564 if (*len < sizeof(u32)) {
565 IPW_DEBUG_ORD("ordinal buffer length too small, "
Jiri Bencaaa4d302005-06-07 14:58:41 +0200566 "need %zd\n", sizeof(u32));
James Ketrenos43f66a62005-03-25 12:31:53 -0600567 return -EINVAL;
568 }
569
570 IPW_DEBUG_ORD("Reading TABLE0[%i] from offset 0x%08x\n",
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400571 ord, priv->table0_addr + (ord << 2));
James Ketrenos43f66a62005-03-25 12:31:53 -0600572
573 *len = sizeof(u32);
574 ord <<= 2;
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400575 *((u32 *) val) = ipw_read32(priv, priv->table0_addr + ord);
James Ketrenos43f66a62005-03-25 12:31:53 -0600576 break;
577
578 case IPW_ORD_TABLE_1_MASK:
579 /*
580 * TABLE 1: Indirect access to a table of 32 bit values
Jeff Garzikbf794512005-07-31 13:07:26 -0400581 *
582 * This is a fairly large table of u32 values each
James Ketrenos43f66a62005-03-25 12:31:53 -0600583 * representing starting addr for the data (which is
584 * also a u32)
585 */
586
587 /* remove the table id from the ordinal */
588 ord &= IPW_ORD_TABLE_VALUE_MASK;
Jeff Garzikbf794512005-07-31 13:07:26 -0400589
James Ketrenos43f66a62005-03-25 12:31:53 -0600590 /* boundary check */
591 if (ord > priv->table1_len) {
592 IPW_DEBUG_ORD("ordinal value too long\n");
593 return -EINVAL;
594 }
595
596 /* verify we have enough room to store the value */
597 if (*len < sizeof(u32)) {
598 IPW_DEBUG_ORD("ordinal buffer length too small, "
Jiri Bencaaa4d302005-06-07 14:58:41 +0200599 "need %zd\n", sizeof(u32));
James Ketrenos43f66a62005-03-25 12:31:53 -0600600 return -EINVAL;
601 }
602
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400603 *((u32 *) val) =
604 ipw_read_reg32(priv, (priv->table1_addr + (ord << 2)));
James Ketrenos43f66a62005-03-25 12:31:53 -0600605 *len = sizeof(u32);
606 break;
607
608 case IPW_ORD_TABLE_2_MASK:
609 /*
610 * TABLE 2: Indirect access to a table of variable sized values
611 *
612 * This table consist of six values, each containing
613 * - dword containing the starting offset of the data
614 * - dword containing the lengh in the first 16bits
615 * and the count in the second 16bits
616 */
617
618 /* remove the table id from the ordinal */
619 ord &= IPW_ORD_TABLE_VALUE_MASK;
620
621 /* boundary check */
622 if (ord > priv->table2_len) {
623 IPW_DEBUG_ORD("ordinal value too long\n");
624 return -EINVAL;
625 }
626
627 /* get the address of statistic */
628 addr = ipw_read_reg32(priv, priv->table2_addr + (ord << 3));
Jeff Garzikbf794512005-07-31 13:07:26 -0400629
630 /* get the second DW of statistics ;
James Ketrenos43f66a62005-03-25 12:31:53 -0600631 * two 16-bit words - first is length, second is count */
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400632 field_info =
633 ipw_read_reg32(priv,
634 priv->table2_addr + (ord << 3) +
635 sizeof(u32));
Jeff Garzikbf794512005-07-31 13:07:26 -0400636
James Ketrenos43f66a62005-03-25 12:31:53 -0600637 /* get each entry length */
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400638 field_len = *((u16 *) & field_info);
Jeff Garzikbf794512005-07-31 13:07:26 -0400639
James Ketrenos43f66a62005-03-25 12:31:53 -0600640 /* get number of entries */
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400641 field_count = *(((u16 *) & field_info) + 1);
Jeff Garzikbf794512005-07-31 13:07:26 -0400642
James Ketrenos43f66a62005-03-25 12:31:53 -0600643 /* abort if not enought memory */
644 total_len = field_len * field_count;
645 if (total_len > *len) {
646 *len = total_len;
647 return -EINVAL;
648 }
Jeff Garzikbf794512005-07-31 13:07:26 -0400649
James Ketrenos43f66a62005-03-25 12:31:53 -0600650 *len = total_len;
651 if (!total_len)
652 return 0;
653
654 IPW_DEBUG_ORD("addr = 0x%08x, total_len = %i, "
Jeff Garzikbf794512005-07-31 13:07:26 -0400655 "field_info = 0x%08x\n",
James Ketrenos43f66a62005-03-25 12:31:53 -0600656 addr, total_len, field_info);
657 ipw_read_indirect(priv, addr, val, total_len);
658 break;
659
660 default:
661 IPW_DEBUG_ORD("Invalid ordinal!\n");
662 return -EINVAL;
663
664 }
665
James Ketrenos43f66a62005-03-25 12:31:53 -0600666 return 0;
667}
668
669static void ipw_init_ordinals(struct ipw_priv *priv)
670{
671 priv->table0_addr = IPW_ORDINALS_TABLE_LOWER;
Jeff Garzikbf794512005-07-31 13:07:26 -0400672 priv->table0_len = ipw_read32(priv, priv->table0_addr);
James Ketrenos43f66a62005-03-25 12:31:53 -0600673
674 IPW_DEBUG_ORD("table 0 offset at 0x%08x, len = %i\n",
675 priv->table0_addr, priv->table0_len);
676
677 priv->table1_addr = ipw_read32(priv, IPW_ORDINALS_TABLE_1);
678 priv->table1_len = ipw_read_reg32(priv, priv->table1_addr);
679
680 IPW_DEBUG_ORD("table 1 offset at 0x%08x, len = %i\n",
681 priv->table1_addr, priv->table1_len);
682
683 priv->table2_addr = ipw_read32(priv, IPW_ORDINALS_TABLE_2);
684 priv->table2_len = ipw_read_reg32(priv, priv->table2_addr);
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400685 priv->table2_len &= 0x0000ffff; /* use first two bytes */
James Ketrenos43f66a62005-03-25 12:31:53 -0600686
687 IPW_DEBUG_ORD("table 2 offset at 0x%08x, len = %i\n",
688 priv->table2_addr, priv->table2_len);
689
690}
691
James Ketrenosa613bff2005-08-24 21:43:11 -0500692u32 ipw_register_toggle(u32 reg)
693{
James Ketrenosb095c382005-08-24 22:04:42 -0500694 reg &= ~IPW_START_STANDBY;
695 if (reg & IPW_GATE_ODMA)
696 reg &= ~IPW_GATE_ODMA;
697 if (reg & IPW_GATE_IDMA)
698 reg &= ~IPW_GATE_IDMA;
699 if (reg & IPW_GATE_ADMA)
700 reg &= ~IPW_GATE_ADMA;
James Ketrenosa613bff2005-08-24 21:43:11 -0500701 return reg;
702}
703
704/*
705 * LED behavior:
706 * - On radio ON, turn on any LEDs that require to be on during start
707 * - On initialization, start unassociated blink
708 * - On association, disable unassociated blink
709 * - On disassociation, start unassociated blink
710 * - On radio OFF, turn off any LEDs started during radio on
711 *
712 */
713#define LD_TIME_LINK_ON 300
714#define LD_TIME_LINK_OFF 2700
715#define LD_TIME_ACT_ON 250
716
717void ipw_led_link_on(struct ipw_priv *priv)
718{
719 unsigned long flags;
720 u32 led;
721
722 /* If configured to not use LEDs, or nic_type is 1,
723 * then we don't toggle a LINK led */
724 if (priv->config & CFG_NO_LED || priv->nic_type == EEPROM_NIC_TYPE_1)
725 return;
726
727 spin_lock_irqsave(&priv->lock, flags);
728
729 if (!(priv->status & STATUS_RF_KILL_MASK) &&
730 !(priv->status & STATUS_LED_LINK_ON)) {
731 IPW_DEBUG_LED("Link LED On\n");
James Ketrenosb095c382005-08-24 22:04:42 -0500732 led = ipw_read_reg32(priv, IPW_EVENT_REG);
James Ketrenosa613bff2005-08-24 21:43:11 -0500733 led |= priv->led_association_on;
734
735 led = ipw_register_toggle(led);
736
737 IPW_DEBUG_LED("Reg: 0x%08X\n", led);
James Ketrenosb095c382005-08-24 22:04:42 -0500738 ipw_write_reg32(priv, IPW_EVENT_REG, led);
James Ketrenosa613bff2005-08-24 21:43:11 -0500739
740 priv->status |= STATUS_LED_LINK_ON;
741
742 /* If we aren't associated, schedule turning the LED off */
743 if (!(priv->status & STATUS_ASSOCIATED))
744 queue_delayed_work(priv->workqueue,
745 &priv->led_link_off,
746 LD_TIME_LINK_ON);
747 }
748
749 spin_unlock_irqrestore(&priv->lock, flags);
750}
751
James Ketrenosc848d0a2005-08-24 21:56:24 -0500752static void ipw_bg_led_link_on(void *data)
753{
754 struct ipw_priv *priv = data;
755 down(&priv->sem);
756 ipw_led_link_on(data);
757 up(&priv->sem);
758}
759
James Ketrenosa613bff2005-08-24 21:43:11 -0500760void ipw_led_link_off(struct ipw_priv *priv)
761{
762 unsigned long flags;
763 u32 led;
764
765 /* If configured not to use LEDs, or nic type is 1,
766 * then we don't goggle the LINK led. */
767 if (priv->config & CFG_NO_LED || priv->nic_type == EEPROM_NIC_TYPE_1)
768 return;
769
770 spin_lock_irqsave(&priv->lock, flags);
771
772 if (priv->status & STATUS_LED_LINK_ON) {
James Ketrenosb095c382005-08-24 22:04:42 -0500773 led = ipw_read_reg32(priv, IPW_EVENT_REG);
James Ketrenosa613bff2005-08-24 21:43:11 -0500774 led &= priv->led_association_off;
775 led = ipw_register_toggle(led);
776
777 IPW_DEBUG_LED("Reg: 0x%08X\n", led);
James Ketrenosb095c382005-08-24 22:04:42 -0500778 ipw_write_reg32(priv, IPW_EVENT_REG, led);
James Ketrenosa613bff2005-08-24 21:43:11 -0500779
780 IPW_DEBUG_LED("Link LED Off\n");
781
782 priv->status &= ~STATUS_LED_LINK_ON;
783
784 /* If we aren't associated and the radio is on, schedule
785 * turning the LED on (blink while unassociated) */
786 if (!(priv->status & STATUS_RF_KILL_MASK) &&
787 !(priv->status & STATUS_ASSOCIATED))
788 queue_delayed_work(priv->workqueue, &priv->led_link_on,
789 LD_TIME_LINK_OFF);
790
791 }
792
793 spin_unlock_irqrestore(&priv->lock, flags);
794}
795
James Ketrenosc848d0a2005-08-24 21:56:24 -0500796static void ipw_bg_led_link_off(void *data)
797{
798 struct ipw_priv *priv = data;
799 down(&priv->sem);
800 ipw_led_link_off(data);
801 up(&priv->sem);
802}
803
James Ketrenosb095c382005-08-24 22:04:42 -0500804static inline void __ipw_led_activity_on(struct ipw_priv *priv)
James Ketrenosa613bff2005-08-24 21:43:11 -0500805{
James Ketrenosa613bff2005-08-24 21:43:11 -0500806 u32 led;
807
808 if (priv->config & CFG_NO_LED)
809 return;
810
James Ketrenosb095c382005-08-24 22:04:42 -0500811 if (priv->status & STATUS_RF_KILL_MASK)
James Ketrenosa613bff2005-08-24 21:43:11 -0500812 return;
James Ketrenosa613bff2005-08-24 21:43:11 -0500813
814 if (!(priv->status & STATUS_LED_ACT_ON)) {
James Ketrenosb095c382005-08-24 22:04:42 -0500815 led = ipw_read_reg32(priv, IPW_EVENT_REG);
James Ketrenosa613bff2005-08-24 21:43:11 -0500816 led |= priv->led_activity_on;
817
818 led = ipw_register_toggle(led);
819
820 IPW_DEBUG_LED("Reg: 0x%08X\n", led);
James Ketrenosb095c382005-08-24 22:04:42 -0500821 ipw_write_reg32(priv, IPW_EVENT_REG, led);
James Ketrenosa613bff2005-08-24 21:43:11 -0500822
823 IPW_DEBUG_LED("Activity LED On\n");
824
825 priv->status |= STATUS_LED_ACT_ON;
826
James Ketrenosc848d0a2005-08-24 21:56:24 -0500827 cancel_delayed_work(&priv->led_act_off);
James Ketrenosa613bff2005-08-24 21:43:11 -0500828 queue_delayed_work(priv->workqueue, &priv->led_act_off,
829 LD_TIME_ACT_ON);
830 } else {
831 /* Reschedule LED off for full time period */
832 cancel_delayed_work(&priv->led_act_off);
833 queue_delayed_work(priv->workqueue, &priv->led_act_off,
834 LD_TIME_ACT_ON);
835 }
James Ketrenosb095c382005-08-24 22:04:42 -0500836}
James Ketrenosa613bff2005-08-24 21:43:11 -0500837
James Ketrenosb095c382005-08-24 22:04:42 -0500838void ipw_led_activity_on(struct ipw_priv *priv)
839{
840 unsigned long flags;
841 spin_lock_irqsave(&priv->lock, flags);
842 __ipw_led_activity_on(priv);
James Ketrenosa613bff2005-08-24 21:43:11 -0500843 spin_unlock_irqrestore(&priv->lock, flags);
844}
845
846void ipw_led_activity_off(struct ipw_priv *priv)
847{
848 unsigned long flags;
849 u32 led;
850
851 if (priv->config & CFG_NO_LED)
852 return;
853
854 spin_lock_irqsave(&priv->lock, flags);
855
856 if (priv->status & STATUS_LED_ACT_ON) {
James Ketrenosb095c382005-08-24 22:04:42 -0500857 led = ipw_read_reg32(priv, IPW_EVENT_REG);
James Ketrenosa613bff2005-08-24 21:43:11 -0500858 led &= priv->led_activity_off;
859
860 led = ipw_register_toggle(led);
861
862 IPW_DEBUG_LED("Reg: 0x%08X\n", led);
James Ketrenosb095c382005-08-24 22:04:42 -0500863 ipw_write_reg32(priv, IPW_EVENT_REG, led);
James Ketrenosa613bff2005-08-24 21:43:11 -0500864
865 IPW_DEBUG_LED("Activity LED Off\n");
866
867 priv->status &= ~STATUS_LED_ACT_ON;
868 }
869
870 spin_unlock_irqrestore(&priv->lock, flags);
871}
872
James Ketrenosc848d0a2005-08-24 21:56:24 -0500873static void ipw_bg_led_activity_off(void *data)
874{
875 struct ipw_priv *priv = data;
876 down(&priv->sem);
877 ipw_led_activity_off(data);
878 up(&priv->sem);
879}
880
James Ketrenosa613bff2005-08-24 21:43:11 -0500881void ipw_led_band_on(struct ipw_priv *priv)
882{
883 unsigned long flags;
884 u32 led;
885
886 /* Only nic type 1 supports mode LEDs */
James Ketrenosc848d0a2005-08-24 21:56:24 -0500887 if (priv->config & CFG_NO_LED ||
888 priv->nic_type != EEPROM_NIC_TYPE_1 || !priv->assoc_network)
James Ketrenosa613bff2005-08-24 21:43:11 -0500889 return;
890
891 spin_lock_irqsave(&priv->lock, flags);
892
James Ketrenosb095c382005-08-24 22:04:42 -0500893 led = ipw_read_reg32(priv, IPW_EVENT_REG);
James Ketrenosa613bff2005-08-24 21:43:11 -0500894 if (priv->assoc_network->mode == IEEE_A) {
895 led |= priv->led_ofdm_on;
896 led &= priv->led_association_off;
897 IPW_DEBUG_LED("Mode LED On: 802.11a\n");
898 } else if (priv->assoc_network->mode == IEEE_G) {
899 led |= priv->led_ofdm_on;
900 led |= priv->led_association_on;
901 IPW_DEBUG_LED("Mode LED On: 802.11g\n");
902 } else {
903 led &= priv->led_ofdm_off;
904 led |= priv->led_association_on;
905 IPW_DEBUG_LED("Mode LED On: 802.11b\n");
906 }
907
908 led = ipw_register_toggle(led);
909
910 IPW_DEBUG_LED("Reg: 0x%08X\n", led);
James Ketrenosb095c382005-08-24 22:04:42 -0500911 ipw_write_reg32(priv, IPW_EVENT_REG, led);
James Ketrenosa613bff2005-08-24 21:43:11 -0500912
913 spin_unlock_irqrestore(&priv->lock, flags);
914}
915
916void ipw_led_band_off(struct ipw_priv *priv)
917{
918 unsigned long flags;
919 u32 led;
920
921 /* Only nic type 1 supports mode LEDs */
922 if (priv->config & CFG_NO_LED || priv->nic_type != EEPROM_NIC_TYPE_1)
923 return;
924
925 spin_lock_irqsave(&priv->lock, flags);
926
James Ketrenosb095c382005-08-24 22:04:42 -0500927 led = ipw_read_reg32(priv, IPW_EVENT_REG);
James Ketrenosa613bff2005-08-24 21:43:11 -0500928 led &= priv->led_ofdm_off;
929 led &= priv->led_association_off;
930
931 led = ipw_register_toggle(led);
932
933 IPW_DEBUG_LED("Reg: 0x%08X\n", led);
James Ketrenosb095c382005-08-24 22:04:42 -0500934 ipw_write_reg32(priv, IPW_EVENT_REG, led);
James Ketrenosa613bff2005-08-24 21:43:11 -0500935
936 spin_unlock_irqrestore(&priv->lock, flags);
937}
938
939void ipw_led_radio_on(struct ipw_priv *priv)
940{
941 ipw_led_link_on(priv);
942}
943
944void ipw_led_radio_off(struct ipw_priv *priv)
945{
946 ipw_led_activity_off(priv);
947 ipw_led_link_off(priv);
948}
949
950void ipw_led_link_up(struct ipw_priv *priv)
951{
952 /* Set the Link Led on for all nic types */
953 ipw_led_link_on(priv);
954}
955
956void ipw_led_link_down(struct ipw_priv *priv)
957{
958 ipw_led_activity_off(priv);
959 ipw_led_link_off(priv);
960
961 if (priv->status & STATUS_RF_KILL_MASK)
962 ipw_led_radio_off(priv);
963}
964
965void ipw_led_init(struct ipw_priv *priv)
966{
967 priv->nic_type = priv->eeprom[EEPROM_NIC_TYPE];
968
969 /* Set the default PINs for the link and activity leds */
James Ketrenosb095c382005-08-24 22:04:42 -0500970 priv->led_activity_on = IPW_ACTIVITY_LED;
971 priv->led_activity_off = ~(IPW_ACTIVITY_LED);
James Ketrenosa613bff2005-08-24 21:43:11 -0500972
James Ketrenosb095c382005-08-24 22:04:42 -0500973 priv->led_association_on = IPW_ASSOCIATED_LED;
974 priv->led_association_off = ~(IPW_ASSOCIATED_LED);
James Ketrenosa613bff2005-08-24 21:43:11 -0500975
976 /* Set the default PINs for the OFDM leds */
James Ketrenosb095c382005-08-24 22:04:42 -0500977 priv->led_ofdm_on = IPW_OFDM_LED;
978 priv->led_ofdm_off = ~(IPW_OFDM_LED);
James Ketrenosa613bff2005-08-24 21:43:11 -0500979
980 switch (priv->nic_type) {
981 case EEPROM_NIC_TYPE_1:
982 /* In this NIC type, the LEDs are reversed.... */
James Ketrenosb095c382005-08-24 22:04:42 -0500983 priv->led_activity_on = IPW_ASSOCIATED_LED;
984 priv->led_activity_off = ~(IPW_ASSOCIATED_LED);
985 priv->led_association_on = IPW_ACTIVITY_LED;
986 priv->led_association_off = ~(IPW_ACTIVITY_LED);
James Ketrenosa613bff2005-08-24 21:43:11 -0500987
988 if (!(priv->config & CFG_NO_LED))
989 ipw_led_band_on(priv);
990
991 /* And we don't blink link LEDs for this nic, so
992 * just return here */
993 return;
994
995 case EEPROM_NIC_TYPE_3:
996 case EEPROM_NIC_TYPE_2:
997 case EEPROM_NIC_TYPE_4:
998 case EEPROM_NIC_TYPE_0:
999 break;
1000
1001 default:
1002 IPW_DEBUG_INFO("Unknown NIC type from EEPROM: %d\n",
1003 priv->nic_type);
1004 priv->nic_type = EEPROM_NIC_TYPE_0;
1005 break;
1006 }
1007
1008 if (!(priv->config & CFG_NO_LED)) {
1009 if (priv->status & STATUS_ASSOCIATED)
1010 ipw_led_link_on(priv);
1011 else
1012 ipw_led_link_off(priv);
1013 }
1014}
1015
1016void ipw_led_shutdown(struct ipw_priv *priv)
1017{
1018 cancel_delayed_work(&priv->led_link_on);
1019 cancel_delayed_work(&priv->led_link_off);
1020 cancel_delayed_work(&priv->led_act_off);
1021 ipw_led_activity_off(priv);
1022 ipw_led_link_off(priv);
1023 ipw_led_band_off(priv);
1024}
1025
James Ketrenos43f66a62005-03-25 12:31:53 -06001026/*
1027 * The following adds a new attribute to the sysfs representation
1028 * of this device driver (i.e. a new file in /sys/bus/pci/drivers/ipw/)
1029 * used for controling the debug level.
Jeff Garzikbf794512005-07-31 13:07:26 -04001030 *
James Ketrenos43f66a62005-03-25 12:31:53 -06001031 * See the level definitions in ipw for details.
1032 */
1033static ssize_t show_debug_level(struct device_driver *d, char *buf)
1034{
1035 return sprintf(buf, "0x%08X\n", ipw_debug_level);
1036}
James Ketrenosa613bff2005-08-24 21:43:11 -05001037
1038static ssize_t store_debug_level(struct device_driver *d, const char *buf,
1039 size_t count)
James Ketrenos43f66a62005-03-25 12:31:53 -06001040{
1041 char *p = (char *)buf;
1042 u32 val;
1043
1044 if (p[1] == 'x' || p[1] == 'X' || p[0] == 'x' || p[0] == 'X') {
1045 p++;
1046 if (p[0] == 'x' || p[0] == 'X')
1047 p++;
1048 val = simple_strtoul(p, &p, 16);
1049 } else
1050 val = simple_strtoul(p, &p, 10);
Jeff Garzikbf794512005-07-31 13:07:26 -04001051 if (p == buf)
1052 printk(KERN_INFO DRV_NAME
James Ketrenos43f66a62005-03-25 12:31:53 -06001053 ": %s is not in hex or decimal form.\n", buf);
1054 else
1055 ipw_debug_level = val;
1056
1057 return strnlen(buf, count);
1058}
1059
Jeff Garzikbf794512005-07-31 13:07:26 -04001060static DRIVER_ATTR(debug_level, S_IWUSR | S_IRUGO,
James Ketrenos43f66a62005-03-25 12:31:53 -06001061 show_debug_level, store_debug_level);
1062
James Ketrenosa613bff2005-08-24 21:43:11 -05001063static ssize_t show_scan_age(struct device *d, struct device_attribute *attr,
1064 char *buf)
1065{
1066 struct ipw_priv *priv = dev_get_drvdata(d);
1067 return sprintf(buf, "%d\n", priv->ieee->scan_age);
1068}
1069
1070static ssize_t store_scan_age(struct device *d, struct device_attribute *attr,
1071 const char *buf, size_t count)
1072{
1073 struct ipw_priv *priv = dev_get_drvdata(d);
James Ketrenosc848d0a2005-08-24 21:56:24 -05001074#ifdef CONFIG_IPW_DEBUG
James Ketrenosa613bff2005-08-24 21:43:11 -05001075 struct net_device *dev = priv->net_dev;
James Ketrenosc848d0a2005-08-24 21:56:24 -05001076#endif
James Ketrenosa613bff2005-08-24 21:43:11 -05001077 char buffer[] = "00000000";
1078 unsigned long len =
1079 (sizeof(buffer) - 1) > count ? count : sizeof(buffer) - 1;
1080 unsigned long val;
1081 char *p = buffer;
1082
1083 IPW_DEBUG_INFO("enter\n");
1084
1085 strncpy(buffer, buf, len);
1086 buffer[len] = 0;
1087
1088 if (p[1] == 'x' || p[1] == 'X' || p[0] == 'x' || p[0] == 'X') {
1089 p++;
1090 if (p[0] == 'x' || p[0] == 'X')
1091 p++;
1092 val = simple_strtoul(p, &p, 16);
1093 } else
1094 val = simple_strtoul(p, &p, 10);
1095 if (p == buffer) {
1096 IPW_DEBUG_INFO("%s: user supplied invalid value.\n", dev->name);
1097 } else {
1098 priv->ieee->scan_age = val;
1099 IPW_DEBUG_INFO("set scan_age = %u\n", priv->ieee->scan_age);
1100 }
1101
1102 IPW_DEBUG_INFO("exit\n");
1103 return len;
1104}
1105
1106static DEVICE_ATTR(scan_age, S_IWUSR | S_IRUGO, show_scan_age, store_scan_age);
1107
1108static ssize_t show_led(struct device *d, struct device_attribute *attr,
1109 char *buf)
1110{
1111 struct ipw_priv *priv = dev_get_drvdata(d);
1112 return sprintf(buf, "%d\n", (priv->config & CFG_NO_LED) ? 0 : 1);
1113}
1114
1115static ssize_t store_led(struct device *d, struct device_attribute *attr,
1116 const char *buf, size_t count)
1117{
1118 struct ipw_priv *priv = dev_get_drvdata(d);
1119
1120 IPW_DEBUG_INFO("enter\n");
1121
1122 if (count == 0)
1123 return 0;
1124
1125 if (*buf == 0) {
1126 IPW_DEBUG_LED("Disabling LED control.\n");
1127 priv->config |= CFG_NO_LED;
1128 ipw_led_shutdown(priv);
1129 } else {
1130 IPW_DEBUG_LED("Enabling LED control.\n");
1131 priv->config &= ~CFG_NO_LED;
1132 ipw_led_init(priv);
1133 }
1134
1135 IPW_DEBUG_INFO("exit\n");
1136 return count;
1137}
1138
1139static DEVICE_ATTR(led, S_IWUSR | S_IRUGO, show_led, store_led);
1140
Andrew Mortonad3fee52005-06-20 14:30:36 -07001141static ssize_t show_status(struct device *d,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001142 struct device_attribute *attr, char *buf)
James Ketrenos43f66a62005-03-25 12:31:53 -06001143{
Andrew Mortonad3fee52005-06-20 14:30:36 -07001144 struct ipw_priv *p = d->driver_data;
James Ketrenos43f66a62005-03-25 12:31:53 -06001145 return sprintf(buf, "0x%08x\n", (int)p->status);
1146}
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001147
James Ketrenos43f66a62005-03-25 12:31:53 -06001148static DEVICE_ATTR(status, S_IRUGO, show_status, NULL);
1149
Andrew Mortonad3fee52005-06-20 14:30:36 -07001150static ssize_t show_cfg(struct device *d, struct device_attribute *attr,
1151 char *buf)
James Ketrenos43f66a62005-03-25 12:31:53 -06001152{
Andrew Mortonad3fee52005-06-20 14:30:36 -07001153 struct ipw_priv *p = d->driver_data;
James Ketrenos43f66a62005-03-25 12:31:53 -06001154 return sprintf(buf, "0x%08x\n", (int)p->config);
1155}
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001156
James Ketrenos43f66a62005-03-25 12:31:53 -06001157static DEVICE_ATTR(cfg, S_IRUGO, show_cfg, NULL);
1158
Andrew Mortonad3fee52005-06-20 14:30:36 -07001159static ssize_t show_nic_type(struct device *d,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001160 struct device_attribute *attr, char *buf)
James Ketrenos43f66a62005-03-25 12:31:53 -06001161{
James Ketrenosa613bff2005-08-24 21:43:11 -05001162 struct ipw_priv *priv = d->driver_data;
1163 return sprintf(buf, "TYPE: %d\n", priv->nic_type);
James Ketrenos43f66a62005-03-25 12:31:53 -06001164}
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001165
James Ketrenos43f66a62005-03-25 12:31:53 -06001166static DEVICE_ATTR(nic_type, S_IRUGO, show_nic_type, NULL);
1167
Andrew Mortonad3fee52005-06-20 14:30:36 -07001168static ssize_t dump_error_log(struct device *d,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001169 struct device_attribute *attr, const char *buf,
1170 size_t count)
James Ketrenos43f66a62005-03-25 12:31:53 -06001171{
1172 char *p = (char *)buf;
1173
Jeff Garzikbf794512005-07-31 13:07:26 -04001174 if (p[0] == '1')
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001175 ipw_dump_nic_error_log((struct ipw_priv *)d->driver_data);
James Ketrenos43f66a62005-03-25 12:31:53 -06001176
1177 return strnlen(buf, count);
1178}
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001179
James Ketrenos43f66a62005-03-25 12:31:53 -06001180static DEVICE_ATTR(dump_errors, S_IWUSR, NULL, dump_error_log);
1181
Andrew Mortonad3fee52005-06-20 14:30:36 -07001182static ssize_t dump_event_log(struct device *d,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001183 struct device_attribute *attr, const char *buf,
1184 size_t count)
James Ketrenos43f66a62005-03-25 12:31:53 -06001185{
1186 char *p = (char *)buf;
1187
Jeff Garzikbf794512005-07-31 13:07:26 -04001188 if (p[0] == '1')
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001189 ipw_dump_nic_event_log((struct ipw_priv *)d->driver_data);
James Ketrenos43f66a62005-03-25 12:31:53 -06001190
1191 return strnlen(buf, count);
1192}
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001193
James Ketrenos43f66a62005-03-25 12:31:53 -06001194static DEVICE_ATTR(dump_events, S_IWUSR, NULL, dump_event_log);
1195
Andrew Mortonad3fee52005-06-20 14:30:36 -07001196static ssize_t show_ucode_version(struct device *d,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001197 struct device_attribute *attr, char *buf)
James Ketrenos43f66a62005-03-25 12:31:53 -06001198{
1199 u32 len = sizeof(u32), tmp = 0;
Andrew Mortonad3fee52005-06-20 14:30:36 -07001200 struct ipw_priv *p = d->driver_data;
James Ketrenos43f66a62005-03-25 12:31:53 -06001201
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001202 if (ipw_get_ordinal(p, IPW_ORD_STAT_UCODE_VERSION, &tmp, &len))
James Ketrenos43f66a62005-03-25 12:31:53 -06001203 return 0;
1204
1205 return sprintf(buf, "0x%08x\n", tmp);
1206}
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001207
1208static DEVICE_ATTR(ucode_version, S_IWUSR | S_IRUGO, show_ucode_version, NULL);
James Ketrenos43f66a62005-03-25 12:31:53 -06001209
Andrew Mortonad3fee52005-06-20 14:30:36 -07001210static ssize_t show_rtc(struct device *d, struct device_attribute *attr,
1211 char *buf)
James Ketrenos43f66a62005-03-25 12:31:53 -06001212{
1213 u32 len = sizeof(u32), tmp = 0;
Andrew Mortonad3fee52005-06-20 14:30:36 -07001214 struct ipw_priv *p = d->driver_data;
James Ketrenos43f66a62005-03-25 12:31:53 -06001215
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001216 if (ipw_get_ordinal(p, IPW_ORD_STAT_RTC, &tmp, &len))
James Ketrenos43f66a62005-03-25 12:31:53 -06001217 return 0;
1218
1219 return sprintf(buf, "0x%08x\n", tmp);
1220}
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001221
1222static DEVICE_ATTR(rtc, S_IWUSR | S_IRUGO, show_rtc, NULL);
James Ketrenos43f66a62005-03-25 12:31:53 -06001223
1224/*
1225 * Add a device attribute to view/control the delay between eeprom
1226 * operations.
1227 */
Andrew Mortonad3fee52005-06-20 14:30:36 -07001228static ssize_t show_eeprom_delay(struct device *d,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001229 struct device_attribute *attr, char *buf)
James Ketrenos43f66a62005-03-25 12:31:53 -06001230{
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001231 int n = ((struct ipw_priv *)d->driver_data)->eeprom_delay;
James Ketrenos43f66a62005-03-25 12:31:53 -06001232 return sprintf(buf, "%i\n", n);
1233}
Andrew Mortonad3fee52005-06-20 14:30:36 -07001234static ssize_t store_eeprom_delay(struct device *d,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001235 struct device_attribute *attr,
1236 const char *buf, size_t count)
James Ketrenos43f66a62005-03-25 12:31:53 -06001237{
Andrew Mortonad3fee52005-06-20 14:30:36 -07001238 struct ipw_priv *p = d->driver_data;
James Ketrenos43f66a62005-03-25 12:31:53 -06001239 sscanf(buf, "%i", &p->eeprom_delay);
1240 return strnlen(buf, count);
1241}
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001242
1243static DEVICE_ATTR(eeprom_delay, S_IWUSR | S_IRUGO,
1244 show_eeprom_delay, store_eeprom_delay);
James Ketrenos43f66a62005-03-25 12:31:53 -06001245
Andrew Mortonad3fee52005-06-20 14:30:36 -07001246static ssize_t show_command_event_reg(struct device *d,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001247 struct device_attribute *attr, char *buf)
James Ketrenos43f66a62005-03-25 12:31:53 -06001248{
1249 u32 reg = 0;
Andrew Mortonad3fee52005-06-20 14:30:36 -07001250 struct ipw_priv *p = d->driver_data;
James Ketrenos43f66a62005-03-25 12:31:53 -06001251
James Ketrenosb095c382005-08-24 22:04:42 -05001252 reg = ipw_read_reg32(p, IPW_INTERNAL_CMD_EVENT);
James Ketrenos43f66a62005-03-25 12:31:53 -06001253 return sprintf(buf, "0x%08x\n", reg);
1254}
Andrew Mortonad3fee52005-06-20 14:30:36 -07001255static ssize_t store_command_event_reg(struct device *d,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001256 struct device_attribute *attr,
1257 const char *buf, size_t count)
James Ketrenos43f66a62005-03-25 12:31:53 -06001258{
1259 u32 reg;
Andrew Mortonad3fee52005-06-20 14:30:36 -07001260 struct ipw_priv *p = d->driver_data;
James Ketrenos43f66a62005-03-25 12:31:53 -06001261
1262 sscanf(buf, "%x", &reg);
James Ketrenosb095c382005-08-24 22:04:42 -05001263 ipw_write_reg32(p, IPW_INTERNAL_CMD_EVENT, reg);
James Ketrenos43f66a62005-03-25 12:31:53 -06001264 return strnlen(buf, count);
1265}
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001266
1267static DEVICE_ATTR(command_event_reg, S_IWUSR | S_IRUGO,
1268 show_command_event_reg, store_command_event_reg);
James Ketrenos43f66a62005-03-25 12:31:53 -06001269
Andrew Mortonad3fee52005-06-20 14:30:36 -07001270static ssize_t show_mem_gpio_reg(struct device *d,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001271 struct device_attribute *attr, char *buf)
James Ketrenos43f66a62005-03-25 12:31:53 -06001272{
1273 u32 reg = 0;
Andrew Mortonad3fee52005-06-20 14:30:36 -07001274 struct ipw_priv *p = d->driver_data;
James Ketrenos43f66a62005-03-25 12:31:53 -06001275
1276 reg = ipw_read_reg32(p, 0x301100);
1277 return sprintf(buf, "0x%08x\n", reg);
1278}
Andrew Mortonad3fee52005-06-20 14:30:36 -07001279static ssize_t store_mem_gpio_reg(struct device *d,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001280 struct device_attribute *attr,
1281 const char *buf, size_t count)
James Ketrenos43f66a62005-03-25 12:31:53 -06001282{
1283 u32 reg;
Andrew Mortonad3fee52005-06-20 14:30:36 -07001284 struct ipw_priv *p = d->driver_data;
James Ketrenos43f66a62005-03-25 12:31:53 -06001285
1286 sscanf(buf, "%x", &reg);
1287 ipw_write_reg32(p, 0x301100, reg);
1288 return strnlen(buf, count);
1289}
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001290
1291static DEVICE_ATTR(mem_gpio_reg, S_IWUSR | S_IRUGO,
1292 show_mem_gpio_reg, store_mem_gpio_reg);
James Ketrenos43f66a62005-03-25 12:31:53 -06001293
Andrew Mortonad3fee52005-06-20 14:30:36 -07001294static ssize_t show_indirect_dword(struct device *d,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001295 struct device_attribute *attr, char *buf)
James Ketrenos43f66a62005-03-25 12:31:53 -06001296{
1297 u32 reg = 0;
Andrew Mortonad3fee52005-06-20 14:30:36 -07001298 struct ipw_priv *priv = d->driver_data;
Jeff Garzikbf794512005-07-31 13:07:26 -04001299 if (priv->status & STATUS_INDIRECT_DWORD)
James Ketrenos43f66a62005-03-25 12:31:53 -06001300 reg = ipw_read_reg32(priv, priv->indirect_dword);
Jeff Garzikbf794512005-07-31 13:07:26 -04001301 else
James Ketrenos43f66a62005-03-25 12:31:53 -06001302 reg = 0;
Jeff Garzikbf794512005-07-31 13:07:26 -04001303
James Ketrenos43f66a62005-03-25 12:31:53 -06001304 return sprintf(buf, "0x%08x\n", reg);
1305}
Andrew Mortonad3fee52005-06-20 14:30:36 -07001306static ssize_t store_indirect_dword(struct device *d,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001307 struct device_attribute *attr,
1308 const char *buf, size_t count)
James Ketrenos43f66a62005-03-25 12:31:53 -06001309{
Andrew Mortonad3fee52005-06-20 14:30:36 -07001310 struct ipw_priv *priv = d->driver_data;
James Ketrenos43f66a62005-03-25 12:31:53 -06001311
1312 sscanf(buf, "%x", &priv->indirect_dword);
1313 priv->status |= STATUS_INDIRECT_DWORD;
1314 return strnlen(buf, count);
1315}
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001316
1317static DEVICE_ATTR(indirect_dword, S_IWUSR | S_IRUGO,
1318 show_indirect_dword, store_indirect_dword);
James Ketrenos43f66a62005-03-25 12:31:53 -06001319
Andrew Mortonad3fee52005-06-20 14:30:36 -07001320static ssize_t show_indirect_byte(struct device *d,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001321 struct device_attribute *attr, char *buf)
James Ketrenos43f66a62005-03-25 12:31:53 -06001322{
1323 u8 reg = 0;
Andrew Mortonad3fee52005-06-20 14:30:36 -07001324 struct ipw_priv *priv = d->driver_data;
Jeff Garzikbf794512005-07-31 13:07:26 -04001325 if (priv->status & STATUS_INDIRECT_BYTE)
James Ketrenos43f66a62005-03-25 12:31:53 -06001326 reg = ipw_read_reg8(priv, priv->indirect_byte);
Jeff Garzikbf794512005-07-31 13:07:26 -04001327 else
James Ketrenos43f66a62005-03-25 12:31:53 -06001328 reg = 0;
1329
1330 return sprintf(buf, "0x%02x\n", reg);
1331}
Andrew Mortonad3fee52005-06-20 14:30:36 -07001332static ssize_t store_indirect_byte(struct device *d,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001333 struct device_attribute *attr,
1334 const char *buf, size_t count)
James Ketrenos43f66a62005-03-25 12:31:53 -06001335{
Andrew Mortonad3fee52005-06-20 14:30:36 -07001336 struct ipw_priv *priv = d->driver_data;
James Ketrenos43f66a62005-03-25 12:31:53 -06001337
1338 sscanf(buf, "%x", &priv->indirect_byte);
1339 priv->status |= STATUS_INDIRECT_BYTE;
1340 return strnlen(buf, count);
1341}
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001342
1343static DEVICE_ATTR(indirect_byte, S_IWUSR | S_IRUGO,
James Ketrenos43f66a62005-03-25 12:31:53 -06001344 show_indirect_byte, store_indirect_byte);
1345
Andrew Mortonad3fee52005-06-20 14:30:36 -07001346static ssize_t show_direct_dword(struct device *d,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001347 struct device_attribute *attr, char *buf)
James Ketrenos43f66a62005-03-25 12:31:53 -06001348{
1349 u32 reg = 0;
Andrew Mortonad3fee52005-06-20 14:30:36 -07001350 struct ipw_priv *priv = d->driver_data;
James Ketrenos43f66a62005-03-25 12:31:53 -06001351
Jeff Garzikbf794512005-07-31 13:07:26 -04001352 if (priv->status & STATUS_DIRECT_DWORD)
James Ketrenos43f66a62005-03-25 12:31:53 -06001353 reg = ipw_read32(priv, priv->direct_dword);
Jeff Garzikbf794512005-07-31 13:07:26 -04001354 else
James Ketrenos43f66a62005-03-25 12:31:53 -06001355 reg = 0;
1356
1357 return sprintf(buf, "0x%08x\n", reg);
1358}
Andrew Mortonad3fee52005-06-20 14:30:36 -07001359static ssize_t store_direct_dword(struct device *d,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001360 struct device_attribute *attr,
1361 const char *buf, size_t count)
James Ketrenos43f66a62005-03-25 12:31:53 -06001362{
Andrew Mortonad3fee52005-06-20 14:30:36 -07001363 struct ipw_priv *priv = d->driver_data;
James Ketrenos43f66a62005-03-25 12:31:53 -06001364
1365 sscanf(buf, "%x", &priv->direct_dword);
1366 priv->status |= STATUS_DIRECT_DWORD;
1367 return strnlen(buf, count);
1368}
James Ketrenos43f66a62005-03-25 12:31:53 -06001369
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001370static DEVICE_ATTR(direct_dword, S_IWUSR | S_IRUGO,
1371 show_direct_dword, store_direct_dword);
James Ketrenos43f66a62005-03-25 12:31:53 -06001372
1373static inline int rf_kill_active(struct ipw_priv *priv)
1374{
1375 if (0 == (ipw_read32(priv, 0x30) & 0x10000))
1376 priv->status |= STATUS_RF_KILL_HW;
1377 else
1378 priv->status &= ~STATUS_RF_KILL_HW;
1379
1380 return (priv->status & STATUS_RF_KILL_HW) ? 1 : 0;
1381}
1382
Andrew Mortonad3fee52005-06-20 14:30:36 -07001383static ssize_t show_rf_kill(struct device *d, struct device_attribute *attr,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001384 char *buf)
James Ketrenos43f66a62005-03-25 12:31:53 -06001385{
1386 /* 0 - RF kill not enabled
Jeff Garzikbf794512005-07-31 13:07:26 -04001387 1 - SW based RF kill active (sysfs)
James Ketrenos43f66a62005-03-25 12:31:53 -06001388 2 - HW based RF kill active
1389 3 - Both HW and SW baed RF kill active */
Andrew Mortonad3fee52005-06-20 14:30:36 -07001390 struct ipw_priv *priv = d->driver_data;
James Ketrenos43f66a62005-03-25 12:31:53 -06001391 int val = ((priv->status & STATUS_RF_KILL_SW) ? 0x1 : 0x0) |
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001392 (rf_kill_active(priv) ? 0x2 : 0x0);
James Ketrenos43f66a62005-03-25 12:31:53 -06001393 return sprintf(buf, "%i\n", val);
1394}
1395
1396static int ipw_radio_kill_sw(struct ipw_priv *priv, int disable_radio)
1397{
Jeff Garzikbf794512005-07-31 13:07:26 -04001398 if ((disable_radio ? 1 : 0) ==
James Ketrenosea2b26e2005-08-24 21:25:16 -05001399 ((priv->status & STATUS_RF_KILL_SW) ? 1 : 0))
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001400 return 0;
James Ketrenos43f66a62005-03-25 12:31:53 -06001401
1402 IPW_DEBUG_RF_KILL("Manual SW RF Kill set to: RADIO %s\n",
1403 disable_radio ? "OFF" : "ON");
1404
1405 if (disable_radio) {
1406 priv->status |= STATUS_RF_KILL_SW;
1407
James Ketrenosa613bff2005-08-24 21:43:11 -05001408 if (priv->workqueue)
James Ketrenos43f66a62005-03-25 12:31:53 -06001409 cancel_delayed_work(&priv->request_scan);
James Ketrenos43f66a62005-03-25 12:31:53 -06001410 queue_work(priv->workqueue, &priv->down);
1411 } else {
1412 priv->status &= ~STATUS_RF_KILL_SW;
1413 if (rf_kill_active(priv)) {
1414 IPW_DEBUG_RF_KILL("Can not turn radio back on - "
1415 "disabled by HW switch\n");
1416 /* Make sure the RF_KILL check timer is running */
1417 cancel_delayed_work(&priv->rf_kill);
Jeff Garzikbf794512005-07-31 13:07:26 -04001418 queue_delayed_work(priv->workqueue, &priv->rf_kill,
James Ketrenos43f66a62005-03-25 12:31:53 -06001419 2 * HZ);
Jeff Garzikbf794512005-07-31 13:07:26 -04001420 } else
James Ketrenos43f66a62005-03-25 12:31:53 -06001421 queue_work(priv->workqueue, &priv->up);
1422 }
1423
1424 return 1;
1425}
1426
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001427static ssize_t store_rf_kill(struct device *d, struct device_attribute *attr,
1428 const char *buf, size_t count)
James Ketrenos43f66a62005-03-25 12:31:53 -06001429{
Andrew Mortonad3fee52005-06-20 14:30:36 -07001430 struct ipw_priv *priv = d->driver_data;
Jeff Garzikbf794512005-07-31 13:07:26 -04001431
James Ketrenos43f66a62005-03-25 12:31:53 -06001432 ipw_radio_kill_sw(priv, buf[0] == '1');
1433
1434 return count;
1435}
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001436
1437static DEVICE_ATTR(rf_kill, S_IWUSR | S_IRUGO, show_rf_kill, store_rf_kill);
James Ketrenos43f66a62005-03-25 12:31:53 -06001438
James Ketrenosb095c382005-08-24 22:04:42 -05001439static ssize_t show_speed_scan(struct device *d, struct device_attribute *attr,
1440 char *buf)
1441{
1442 struct ipw_priv *priv = (struct ipw_priv *)d->driver_data;
1443 int pos = 0, len = 0;
1444 if (priv->config & CFG_SPEED_SCAN) {
1445 while (priv->speed_scan[pos] != 0)
1446 len += sprintf(&buf[len], "%d ",
1447 priv->speed_scan[pos++]);
1448 return len + sprintf(&buf[len], "\n");
1449 }
1450
1451 return sprintf(buf, "0\n");
1452}
1453
1454static ssize_t store_speed_scan(struct device *d, struct device_attribute *attr,
1455 const char *buf, size_t count)
1456{
1457 struct ipw_priv *priv = (struct ipw_priv *)d->driver_data;
1458 int channel, pos = 0;
1459 const char *p = buf;
1460
1461 /* list of space separated channels to scan, optionally ending with 0 */
1462 while ((channel = simple_strtol(p, NULL, 0))) {
1463 if (pos == MAX_SPEED_SCAN - 1) {
1464 priv->speed_scan[pos] = 0;
1465 break;
1466 }
1467
1468 if (ieee80211_is_valid_channel(priv->ieee, channel))
1469 priv->speed_scan[pos++] = channel;
1470 else
1471 IPW_WARNING("Skipping invalid channel request: %d\n",
1472 channel);
1473 p = strchr(p, ' ');
1474 if (!p)
1475 break;
1476 while (*p == ' ' || *p == '\t')
1477 p++;
1478 }
1479
1480 if (pos == 0)
1481 priv->config &= ~CFG_SPEED_SCAN;
1482 else {
1483 priv->speed_scan_pos = 0;
1484 priv->config |= CFG_SPEED_SCAN;
1485 }
1486
1487 return count;
1488}
1489
1490static DEVICE_ATTR(speed_scan, S_IWUSR | S_IRUGO, show_speed_scan,
1491 store_speed_scan);
1492
1493static ssize_t show_net_stats(struct device *d, struct device_attribute *attr,
1494 char *buf)
1495{
1496 struct ipw_priv *priv = (struct ipw_priv *)d->driver_data;
1497 return sprintf(buf, "%c\n", (priv->config & CFG_NET_STATS) ? '1' : '0');
1498}
1499
1500static ssize_t store_net_stats(struct device *d, struct device_attribute *attr,
1501 const char *buf, size_t count)
1502{
1503 struct ipw_priv *priv = (struct ipw_priv *)d->driver_data;
1504 if (buf[0] == '1')
1505 priv->config |= CFG_NET_STATS;
1506 else
1507 priv->config &= ~CFG_NET_STATS;
1508
1509 return count;
1510}
1511
1512static DEVICE_ATTR(net_stats, S_IWUSR | S_IRUGO, show_net_stats,
1513 store_net_stats);
1514
James Ketrenosea2b26e2005-08-24 21:25:16 -05001515static void notify_wx_assoc_event(struct ipw_priv *priv)
1516{
1517 union iwreq_data wrqu;
1518 wrqu.ap_addr.sa_family = ARPHRD_ETHER;
1519 if (priv->status & STATUS_ASSOCIATED)
1520 memcpy(wrqu.ap_addr.sa_data, priv->bssid, ETH_ALEN);
1521 else
1522 memset(wrqu.ap_addr.sa_data, 0, ETH_ALEN);
1523 wireless_send_event(priv->net_dev, SIOCGIWAP, &wrqu, NULL);
1524}
1525
James Ketrenos43f66a62005-03-25 12:31:53 -06001526static void ipw_irq_tasklet(struct ipw_priv *priv)
1527{
1528 u32 inta, inta_mask, handled = 0;
1529 unsigned long flags;
1530 int rc = 0;
1531
1532 spin_lock_irqsave(&priv->lock, flags);
1533
James Ketrenosb095c382005-08-24 22:04:42 -05001534 inta = ipw_read32(priv, IPW_INTA_RW);
1535 inta_mask = ipw_read32(priv, IPW_INTA_MASK_R);
1536 inta &= (IPW_INTA_MASK_ALL & inta_mask);
James Ketrenos43f66a62005-03-25 12:31:53 -06001537
1538 /* Add any cached INTA values that need to be handled */
1539 inta |= priv->isr_inta;
1540
1541 /* handle all the justifications for the interrupt */
James Ketrenosb095c382005-08-24 22:04:42 -05001542 if (inta & IPW_INTA_BIT_RX_TRANSFER) {
James Ketrenos43f66a62005-03-25 12:31:53 -06001543 ipw_rx(priv);
James Ketrenosb095c382005-08-24 22:04:42 -05001544 handled |= IPW_INTA_BIT_RX_TRANSFER;
James Ketrenos43f66a62005-03-25 12:31:53 -06001545 }
1546
James Ketrenosb095c382005-08-24 22:04:42 -05001547 if (inta & IPW_INTA_BIT_TX_CMD_QUEUE) {
James Ketrenos43f66a62005-03-25 12:31:53 -06001548 IPW_DEBUG_HC("Command completed.\n");
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001549 rc = ipw_queue_tx_reclaim(priv, &priv->txq_cmd, -1);
James Ketrenos43f66a62005-03-25 12:31:53 -06001550 priv->status &= ~STATUS_HCMD_ACTIVE;
1551 wake_up_interruptible(&priv->wait_command_queue);
James Ketrenosb095c382005-08-24 22:04:42 -05001552 handled |= IPW_INTA_BIT_TX_CMD_QUEUE;
James Ketrenos43f66a62005-03-25 12:31:53 -06001553 }
1554
James Ketrenosb095c382005-08-24 22:04:42 -05001555 if (inta & IPW_INTA_BIT_TX_QUEUE_1) {
James Ketrenos43f66a62005-03-25 12:31:53 -06001556 IPW_DEBUG_TX("TX_QUEUE_1\n");
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001557 rc = ipw_queue_tx_reclaim(priv, &priv->txq[0], 0);
James Ketrenosb095c382005-08-24 22:04:42 -05001558 handled |= IPW_INTA_BIT_TX_QUEUE_1;
James Ketrenos43f66a62005-03-25 12:31:53 -06001559 }
1560
James Ketrenosb095c382005-08-24 22:04:42 -05001561 if (inta & IPW_INTA_BIT_TX_QUEUE_2) {
James Ketrenos43f66a62005-03-25 12:31:53 -06001562 IPW_DEBUG_TX("TX_QUEUE_2\n");
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001563 rc = ipw_queue_tx_reclaim(priv, &priv->txq[1], 1);
James Ketrenosb095c382005-08-24 22:04:42 -05001564 handled |= IPW_INTA_BIT_TX_QUEUE_2;
James Ketrenos43f66a62005-03-25 12:31:53 -06001565 }
1566
James Ketrenosb095c382005-08-24 22:04:42 -05001567 if (inta & IPW_INTA_BIT_TX_QUEUE_3) {
James Ketrenos43f66a62005-03-25 12:31:53 -06001568 IPW_DEBUG_TX("TX_QUEUE_3\n");
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001569 rc = ipw_queue_tx_reclaim(priv, &priv->txq[2], 2);
James Ketrenosb095c382005-08-24 22:04:42 -05001570 handled |= IPW_INTA_BIT_TX_QUEUE_3;
James Ketrenos43f66a62005-03-25 12:31:53 -06001571 }
1572
James Ketrenosb095c382005-08-24 22:04:42 -05001573 if (inta & IPW_INTA_BIT_TX_QUEUE_4) {
James Ketrenos43f66a62005-03-25 12:31:53 -06001574 IPW_DEBUG_TX("TX_QUEUE_4\n");
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001575 rc = ipw_queue_tx_reclaim(priv, &priv->txq[3], 3);
James Ketrenosb095c382005-08-24 22:04:42 -05001576 handled |= IPW_INTA_BIT_TX_QUEUE_4;
James Ketrenos43f66a62005-03-25 12:31:53 -06001577 }
1578
James Ketrenosb095c382005-08-24 22:04:42 -05001579 if (inta & IPW_INTA_BIT_STATUS_CHANGE) {
James Ketrenos43f66a62005-03-25 12:31:53 -06001580 IPW_WARNING("STATUS_CHANGE\n");
James Ketrenosb095c382005-08-24 22:04:42 -05001581 handled |= IPW_INTA_BIT_STATUS_CHANGE;
James Ketrenos43f66a62005-03-25 12:31:53 -06001582 }
1583
James Ketrenosb095c382005-08-24 22:04:42 -05001584 if (inta & IPW_INTA_BIT_BEACON_PERIOD_EXPIRED) {
James Ketrenos43f66a62005-03-25 12:31:53 -06001585 IPW_WARNING("TX_PERIOD_EXPIRED\n");
James Ketrenosb095c382005-08-24 22:04:42 -05001586 handled |= IPW_INTA_BIT_BEACON_PERIOD_EXPIRED;
James Ketrenos43f66a62005-03-25 12:31:53 -06001587 }
1588
James Ketrenosb095c382005-08-24 22:04:42 -05001589 if (inta & IPW_INTA_BIT_SLAVE_MODE_HOST_CMD_DONE) {
James Ketrenos43f66a62005-03-25 12:31:53 -06001590 IPW_WARNING("HOST_CMD_DONE\n");
James Ketrenosb095c382005-08-24 22:04:42 -05001591 handled |= IPW_INTA_BIT_SLAVE_MODE_HOST_CMD_DONE;
James Ketrenos43f66a62005-03-25 12:31:53 -06001592 }
1593
James Ketrenosb095c382005-08-24 22:04:42 -05001594 if (inta & IPW_INTA_BIT_FW_INITIALIZATION_DONE) {
James Ketrenos43f66a62005-03-25 12:31:53 -06001595 IPW_WARNING("FW_INITIALIZATION_DONE\n");
James Ketrenosb095c382005-08-24 22:04:42 -05001596 handled |= IPW_INTA_BIT_FW_INITIALIZATION_DONE;
James Ketrenos43f66a62005-03-25 12:31:53 -06001597 }
1598
James Ketrenosb095c382005-08-24 22:04:42 -05001599 if (inta & IPW_INTA_BIT_FW_CARD_DISABLE_PHY_OFF_DONE) {
James Ketrenos43f66a62005-03-25 12:31:53 -06001600 IPW_WARNING("PHY_OFF_DONE\n");
James Ketrenosb095c382005-08-24 22:04:42 -05001601 handled |= IPW_INTA_BIT_FW_CARD_DISABLE_PHY_OFF_DONE;
James Ketrenos43f66a62005-03-25 12:31:53 -06001602 }
1603
James Ketrenosb095c382005-08-24 22:04:42 -05001604 if (inta & IPW_INTA_BIT_RF_KILL_DONE) {
James Ketrenos43f66a62005-03-25 12:31:53 -06001605 IPW_DEBUG_RF_KILL("RF_KILL_DONE\n");
1606 priv->status |= STATUS_RF_KILL_HW;
1607 wake_up_interruptible(&priv->wait_command_queue);
James Ketrenosea2b26e2005-08-24 21:25:16 -05001608 priv->status &= ~(STATUS_ASSOCIATED | STATUS_ASSOCIATING);
James Ketrenos43f66a62005-03-25 12:31:53 -06001609 cancel_delayed_work(&priv->request_scan);
James Ketrenosa613bff2005-08-24 21:43:11 -05001610 schedule_work(&priv->link_down);
James Ketrenos43f66a62005-03-25 12:31:53 -06001611 queue_delayed_work(priv->workqueue, &priv->rf_kill, 2 * HZ);
James Ketrenosb095c382005-08-24 22:04:42 -05001612 handled |= IPW_INTA_BIT_RF_KILL_DONE;
James Ketrenos43f66a62005-03-25 12:31:53 -06001613 }
Jeff Garzikbf794512005-07-31 13:07:26 -04001614
James Ketrenosb095c382005-08-24 22:04:42 -05001615 if (inta & IPW_INTA_BIT_FATAL_ERROR) {
James Ketrenos43f66a62005-03-25 12:31:53 -06001616 IPW_ERROR("Firmware error detected. Restarting.\n");
1617#ifdef CONFIG_IPW_DEBUG
1618 if (ipw_debug_level & IPW_DL_FW_ERRORS) {
1619 ipw_dump_nic_error_log(priv);
1620 ipw_dump_nic_event_log(priv);
1621 }
1622#endif
James Ketrenosb095c382005-08-24 22:04:42 -05001623 /* XXX: If hardware encryption is for WPA/WPA2,
1624 * we have to notify the supplicant. */
1625 if (priv->ieee->sec.encrypt) {
1626 priv->status &= ~STATUS_ASSOCIATED;
1627 notify_wx_assoc_event(priv);
1628 }
1629
1630 /* Keep the restart process from trying to send host
1631 * commands by clearing the INIT status bit */
1632 priv->status &= ~STATUS_INIT;
James Ketrenos43f66a62005-03-25 12:31:53 -06001633 queue_work(priv->workqueue, &priv->adapter_restart);
James Ketrenosb095c382005-08-24 22:04:42 -05001634 handled |= IPW_INTA_BIT_FATAL_ERROR;
James Ketrenos43f66a62005-03-25 12:31:53 -06001635 }
1636
James Ketrenosb095c382005-08-24 22:04:42 -05001637 if (inta & IPW_INTA_BIT_PARITY_ERROR) {
James Ketrenos43f66a62005-03-25 12:31:53 -06001638 IPW_ERROR("Parity error\n");
James Ketrenosb095c382005-08-24 22:04:42 -05001639 handled |= IPW_INTA_BIT_PARITY_ERROR;
James Ketrenos43f66a62005-03-25 12:31:53 -06001640 }
1641
1642 if (handled != inta) {
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001643 IPW_ERROR("Unhandled INTA bits 0x%08x\n", inta & ~handled);
James Ketrenos43f66a62005-03-25 12:31:53 -06001644 }
1645
1646 /* enable all interrupts */
1647 ipw_enable_interrupts(priv);
1648
1649 spin_unlock_irqrestore(&priv->lock, flags);
1650}
Jeff Garzikbf794512005-07-31 13:07:26 -04001651
James Ketrenos43f66a62005-03-25 12:31:53 -06001652#ifdef CONFIG_IPW_DEBUG
1653#define IPW_CMD(x) case IPW_CMD_ ## x : return #x
1654static char *get_cmd_string(u8 cmd)
1655{
1656 switch (cmd) {
1657 IPW_CMD(HOST_COMPLETE);
Jeff Garzikbf794512005-07-31 13:07:26 -04001658 IPW_CMD(POWER_DOWN);
1659 IPW_CMD(SYSTEM_CONFIG);
1660 IPW_CMD(MULTICAST_ADDRESS);
1661 IPW_CMD(SSID);
1662 IPW_CMD(ADAPTER_ADDRESS);
1663 IPW_CMD(PORT_TYPE);
1664 IPW_CMD(RTS_THRESHOLD);
1665 IPW_CMD(FRAG_THRESHOLD);
1666 IPW_CMD(POWER_MODE);
1667 IPW_CMD(WEP_KEY);
1668 IPW_CMD(TGI_TX_KEY);
1669 IPW_CMD(SCAN_REQUEST);
1670 IPW_CMD(SCAN_REQUEST_EXT);
1671 IPW_CMD(ASSOCIATE);
1672 IPW_CMD(SUPPORTED_RATES);
1673 IPW_CMD(SCAN_ABORT);
1674 IPW_CMD(TX_FLUSH);
1675 IPW_CMD(QOS_PARAMETERS);
1676 IPW_CMD(DINO_CONFIG);
1677 IPW_CMD(RSN_CAPABILITIES);
1678 IPW_CMD(RX_KEY);
1679 IPW_CMD(CARD_DISABLE);
1680 IPW_CMD(SEED_NUMBER);
1681 IPW_CMD(TX_POWER);
1682 IPW_CMD(COUNTRY_INFO);
1683 IPW_CMD(AIRONET_INFO);
1684 IPW_CMD(AP_TX_POWER);
1685 IPW_CMD(CCKM_INFO);
1686 IPW_CMD(CCX_VER_INFO);
1687 IPW_CMD(SET_CALIBRATION);
1688 IPW_CMD(SENSITIVITY_CALIB);
1689 IPW_CMD(RETRY_LIMIT);
1690 IPW_CMD(IPW_PRE_POWER_DOWN);
1691 IPW_CMD(VAP_BEACON_TEMPLATE);
1692 IPW_CMD(VAP_DTIM_PERIOD);
1693 IPW_CMD(EXT_SUPPORTED_RATES);
1694 IPW_CMD(VAP_LOCAL_TX_PWR_CONSTRAINT);
1695 IPW_CMD(VAP_QUIET_INTERVALS);
1696 IPW_CMD(VAP_CHANNEL_SWITCH);
1697 IPW_CMD(VAP_MANDATORY_CHANNELS);
1698 IPW_CMD(VAP_CELL_PWR_LIMIT);
1699 IPW_CMD(VAP_CF_PARAM_SET);
1700 IPW_CMD(VAP_SET_BEACONING_STATE);
1701 IPW_CMD(MEASUREMENT);
1702 IPW_CMD(POWER_CAPABILITY);
1703 IPW_CMD(SUPPORTED_CHANNELS);
1704 IPW_CMD(TPC_REPORT);
1705 IPW_CMD(WME_INFO);
1706 IPW_CMD(PRODUCTION_COMMAND);
1707 default:
James Ketrenos43f66a62005-03-25 12:31:53 -06001708 return "UNKNOWN";
1709 }
1710}
James Ketrenosea2b26e2005-08-24 21:25:16 -05001711#endif
James Ketrenos43f66a62005-03-25 12:31:53 -06001712
1713#define HOST_COMPLETE_TIMEOUT HZ
1714static int ipw_send_cmd(struct ipw_priv *priv, struct host_cmd *cmd)
1715{
1716 int rc = 0;
James Ketrenosa613bff2005-08-24 21:43:11 -05001717 unsigned long flags;
James Ketrenos43f66a62005-03-25 12:31:53 -06001718
James Ketrenosa613bff2005-08-24 21:43:11 -05001719 spin_lock_irqsave(&priv->lock, flags);
James Ketrenos43f66a62005-03-25 12:31:53 -06001720 if (priv->status & STATUS_HCMD_ACTIVE) {
1721 IPW_ERROR("Already sending a command\n");
James Ketrenosa613bff2005-08-24 21:43:11 -05001722 spin_unlock_irqrestore(&priv->lock, flags);
James Ketrenos43f66a62005-03-25 12:31:53 -06001723 return -1;
1724 }
1725
1726 priv->status |= STATUS_HCMD_ACTIVE;
Jeff Garzikbf794512005-07-31 13:07:26 -04001727
James Ketrenosb095c382005-08-24 22:04:42 -05001728 IPW_DEBUG_HC("%s command (#%d) %d bytes: 0x%08X\n",
1729 get_cmd_string(cmd->cmd), cmd->cmd, cmd->len,
1730 priv->status);
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001731 printk_buf(IPW_DL_HOST_COMMAND, (u8 *) cmd->param, cmd->len);
James Ketrenos43f66a62005-03-25 12:31:53 -06001732
1733 rc = ipw_queue_tx_hcmd(priv, cmd->cmd, &cmd->param, cmd->len, 0);
James Ketrenosa613bff2005-08-24 21:43:11 -05001734 if (rc) {
1735 priv->status &= ~STATUS_HCMD_ACTIVE;
1736 spin_unlock_irqrestore(&priv->lock, flags);
James Ketrenos43f66a62005-03-25 12:31:53 -06001737 return rc;
James Ketrenosa613bff2005-08-24 21:43:11 -05001738 }
1739 spin_unlock_irqrestore(&priv->lock, flags);
James Ketrenos43f66a62005-03-25 12:31:53 -06001740
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001741 rc = wait_event_interruptible_timeout(priv->wait_command_queue,
1742 !(priv->
1743 status & STATUS_HCMD_ACTIVE),
1744 HOST_COMPLETE_TIMEOUT);
James Ketrenos43f66a62005-03-25 12:31:53 -06001745 if (rc == 0) {
James Ketrenosa613bff2005-08-24 21:43:11 -05001746 spin_lock_irqsave(&priv->lock, flags);
1747 if (priv->status & STATUS_HCMD_ACTIVE) {
1748 IPW_DEBUG_INFO("Command completion failed out after "
1749 "%dms.\n",
1750 1000 * (HOST_COMPLETE_TIMEOUT / HZ));
1751 priv->status &= ~STATUS_HCMD_ACTIVE;
1752 spin_unlock_irqrestore(&priv->lock, flags);
1753 return -EIO;
1754 }
1755 spin_unlock_irqrestore(&priv->lock, flags);
James Ketrenos43f66a62005-03-25 12:31:53 -06001756 }
James Ketrenosa613bff2005-08-24 21:43:11 -05001757
James Ketrenosb095c382005-08-24 22:04:42 -05001758 if (priv->status & STATUS_RF_KILL_HW) {
James Ketrenos43f66a62005-03-25 12:31:53 -06001759 IPW_DEBUG_INFO("Command aborted due to RF Kill Switch\n");
1760 return -EIO;
1761 }
1762
1763 return 0;
1764}
1765
1766static int ipw_send_host_complete(struct ipw_priv *priv)
1767{
1768 struct host_cmd cmd = {
1769 .cmd = IPW_CMD_HOST_COMPLETE,
1770 .len = 0
1771 };
1772
1773 if (!priv) {
1774 IPW_ERROR("Invalid args\n");
1775 return -1;
1776 }
1777
1778 if (ipw_send_cmd(priv, &cmd)) {
1779 IPW_ERROR("failed to send HOST_COMPLETE command\n");
1780 return -1;
1781 }
Jeff Garzikbf794512005-07-31 13:07:26 -04001782
James Ketrenos43f66a62005-03-25 12:31:53 -06001783 return 0;
1784}
1785
Jeff Garzikbf794512005-07-31 13:07:26 -04001786static int ipw_send_system_config(struct ipw_priv *priv,
James Ketrenos43f66a62005-03-25 12:31:53 -06001787 struct ipw_sys_config *config)
1788{
1789 struct host_cmd cmd = {
1790 .cmd = IPW_CMD_SYSTEM_CONFIG,
1791 .len = sizeof(*config)
1792 };
1793
1794 if (!priv || !config) {
1795 IPW_ERROR("Invalid args\n");
1796 return -1;
1797 }
1798
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001799 memcpy(&cmd.param, config, sizeof(*config));
James Ketrenos43f66a62005-03-25 12:31:53 -06001800 if (ipw_send_cmd(priv, &cmd)) {
1801 IPW_ERROR("failed to send SYSTEM_CONFIG command\n");
1802 return -1;
1803 }
1804
1805 return 0;
1806}
1807
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001808static int ipw_send_ssid(struct ipw_priv *priv, u8 * ssid, int len)
James Ketrenos43f66a62005-03-25 12:31:53 -06001809{
1810 struct host_cmd cmd = {
1811 .cmd = IPW_CMD_SSID,
1812 .len = min(len, IW_ESSID_MAX_SIZE)
1813 };
1814
1815 if (!priv || !ssid) {
1816 IPW_ERROR("Invalid args\n");
1817 return -1;
1818 }
1819
1820 memcpy(&cmd.param, ssid, cmd.len);
1821 if (ipw_send_cmd(priv, &cmd)) {
1822 IPW_ERROR("failed to send SSID command\n");
1823 return -1;
1824 }
Jeff Garzikbf794512005-07-31 13:07:26 -04001825
James Ketrenos43f66a62005-03-25 12:31:53 -06001826 return 0;
1827}
1828
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001829static int ipw_send_adapter_address(struct ipw_priv *priv, u8 * mac)
James Ketrenos43f66a62005-03-25 12:31:53 -06001830{
1831 struct host_cmd cmd = {
1832 .cmd = IPW_CMD_ADAPTER_ADDRESS,
1833 .len = ETH_ALEN
1834 };
1835
1836 if (!priv || !mac) {
1837 IPW_ERROR("Invalid args\n");
1838 return -1;
1839 }
1840
1841 IPW_DEBUG_INFO("%s: Setting MAC to " MAC_FMT "\n",
1842 priv->net_dev->name, MAC_ARG(mac));
1843
1844 memcpy(&cmd.param, mac, ETH_ALEN);
1845
1846 if (ipw_send_cmd(priv, &cmd)) {
1847 IPW_ERROR("failed to send ADAPTER_ADDRESS command\n");
1848 return -1;
1849 }
Jeff Garzikbf794512005-07-31 13:07:26 -04001850
James Ketrenos43f66a62005-03-25 12:31:53 -06001851 return 0;
1852}
1853
James Ketrenosa613bff2005-08-24 21:43:11 -05001854/*
1855 * NOTE: This must be executed from our workqueue as it results in udelay
1856 * being called which may corrupt the keyboard if executed on default
1857 * workqueue
1858 */
James Ketrenos43f66a62005-03-25 12:31:53 -06001859static void ipw_adapter_restart(void *adapter)
1860{
1861 struct ipw_priv *priv = adapter;
1862
1863 if (priv->status & STATUS_RF_KILL_MASK)
1864 return;
1865
1866 ipw_down(priv);
James Ketrenosb095c382005-08-24 22:04:42 -05001867
1868 if (priv->assoc_network &&
1869 (priv->assoc_network->capability & WLAN_CAPABILITY_IBSS))
1870 ipw_remove_current_network(priv);
1871
James Ketrenos43f66a62005-03-25 12:31:53 -06001872 if (ipw_up(priv)) {
1873 IPW_ERROR("Failed to up device\n");
1874 return;
1875 }
James Ketrenosb095c382005-08-24 22:04:42 -05001876
1877 if ((priv->capability & CAP_PRIVACY_ON) &&
1878 (priv->ieee->sec.level == SEC_LEVEL_1) &&
1879 !(priv->ieee->host_encrypt || priv->ieee->host_decrypt))
1880 ipw_set_hwcrypto_keys(priv);
James Ketrenos43f66a62005-03-25 12:31:53 -06001881}
1882
James Ketrenosc848d0a2005-08-24 21:56:24 -05001883static void ipw_bg_adapter_restart(void *data)
1884{
1885 struct ipw_priv *priv = data;
1886 down(&priv->sem);
1887 ipw_adapter_restart(data);
1888 up(&priv->sem);
1889}
1890
James Ketrenos43f66a62005-03-25 12:31:53 -06001891#define IPW_SCAN_CHECK_WATCHDOG (5 * HZ)
1892
1893static void ipw_scan_check(void *data)
1894{
1895 struct ipw_priv *priv = data;
1896 if (priv->status & (STATUS_SCANNING | STATUS_SCAN_ABORTING)) {
1897 IPW_DEBUG_SCAN("Scan completion watchdog resetting "
Jeff Garzikbf794512005-07-31 13:07:26 -04001898 "adapter (%dms).\n",
James Ketrenos43f66a62005-03-25 12:31:53 -06001899 IPW_SCAN_CHECK_WATCHDOG / 100);
James Ketrenosa613bff2005-08-24 21:43:11 -05001900 queue_work(priv->workqueue, &priv->adapter_restart);
James Ketrenos43f66a62005-03-25 12:31:53 -06001901 }
1902}
1903
James Ketrenosc848d0a2005-08-24 21:56:24 -05001904static void ipw_bg_scan_check(void *data)
1905{
1906 struct ipw_priv *priv = data;
1907 down(&priv->sem);
1908 ipw_scan_check(data);
1909 up(&priv->sem);
1910}
1911
James Ketrenos43f66a62005-03-25 12:31:53 -06001912static int ipw_send_scan_request_ext(struct ipw_priv *priv,
1913 struct ipw_scan_request_ext *request)
1914{
1915 struct host_cmd cmd = {
1916 .cmd = IPW_CMD_SCAN_REQUEST_EXT,
1917 .len = sizeof(*request)
1918 };
1919
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001920 memcpy(&cmd.param, request, sizeof(*request));
James Ketrenos43f66a62005-03-25 12:31:53 -06001921 if (ipw_send_cmd(priv, &cmd)) {
1922 IPW_ERROR("failed to send SCAN_REQUEST_EXT command\n");
1923 return -1;
1924 }
Jeff Garzikbf794512005-07-31 13:07:26 -04001925
1926 queue_delayed_work(priv->workqueue, &priv->scan_check,
James Ketrenos43f66a62005-03-25 12:31:53 -06001927 IPW_SCAN_CHECK_WATCHDOG);
1928 return 0;
1929}
1930
1931static int ipw_send_scan_abort(struct ipw_priv *priv)
1932{
1933 struct host_cmd cmd = {
1934 .cmd = IPW_CMD_SCAN_ABORT,
1935 .len = 0
1936 };
1937
1938 if (!priv) {
1939 IPW_ERROR("Invalid args\n");
1940 return -1;
1941 }
1942
1943 if (ipw_send_cmd(priv, &cmd)) {
1944 IPW_ERROR("failed to send SCAN_ABORT command\n");
1945 return -1;
1946 }
Jeff Garzikbf794512005-07-31 13:07:26 -04001947
James Ketrenos43f66a62005-03-25 12:31:53 -06001948 return 0;
1949}
1950
1951static int ipw_set_sensitivity(struct ipw_priv *priv, u16 sens)
1952{
1953 struct host_cmd cmd = {
1954 .cmd = IPW_CMD_SENSITIVITY_CALIB,
1955 .len = sizeof(struct ipw_sensitivity_calib)
1956 };
1957 struct ipw_sensitivity_calib *calib = (struct ipw_sensitivity_calib *)
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001958 &cmd.param;
James Ketrenos43f66a62005-03-25 12:31:53 -06001959 calib->beacon_rssi_raw = sens;
1960 if (ipw_send_cmd(priv, &cmd)) {
1961 IPW_ERROR("failed to send SENSITIVITY CALIB command\n");
1962 return -1;
1963 }
1964
1965 return 0;
1966}
1967
1968static int ipw_send_associate(struct ipw_priv *priv,
1969 struct ipw_associate *associate)
1970{
1971 struct host_cmd cmd = {
1972 .cmd = IPW_CMD_ASSOCIATE,
1973 .len = sizeof(*associate)
1974 };
1975
James Ketrenosa613bff2005-08-24 21:43:11 -05001976 struct ipw_associate tmp_associate;
1977 memcpy(&tmp_associate, associate, sizeof(*associate));
1978 tmp_associate.policy_support =
1979 cpu_to_le16(tmp_associate.policy_support);
1980 tmp_associate.assoc_tsf_msw = cpu_to_le32(tmp_associate.assoc_tsf_msw);
1981 tmp_associate.assoc_tsf_lsw = cpu_to_le32(tmp_associate.assoc_tsf_lsw);
1982 tmp_associate.capability = cpu_to_le16(tmp_associate.capability);
1983 tmp_associate.listen_interval =
1984 cpu_to_le16(tmp_associate.listen_interval);
1985 tmp_associate.beacon_interval =
1986 cpu_to_le16(tmp_associate.beacon_interval);
1987 tmp_associate.atim_window = cpu_to_le16(tmp_associate.atim_window);
1988
James Ketrenos43f66a62005-03-25 12:31:53 -06001989 if (!priv || !associate) {
1990 IPW_ERROR("Invalid args\n");
1991 return -1;
1992 }
1993
James Ketrenosa613bff2005-08-24 21:43:11 -05001994 memcpy(&cmd.param, &tmp_associate, sizeof(*associate));
James Ketrenos43f66a62005-03-25 12:31:53 -06001995 if (ipw_send_cmd(priv, &cmd)) {
1996 IPW_ERROR("failed to send ASSOCIATE command\n");
1997 return -1;
1998 }
Jeff Garzikbf794512005-07-31 13:07:26 -04001999
James Ketrenos43f66a62005-03-25 12:31:53 -06002000 return 0;
2001}
2002
2003static int ipw_send_supported_rates(struct ipw_priv *priv,
2004 struct ipw_supported_rates *rates)
2005{
2006 struct host_cmd cmd = {
2007 .cmd = IPW_CMD_SUPPORTED_RATES,
2008 .len = sizeof(*rates)
2009 };
2010
2011 if (!priv || !rates) {
2012 IPW_ERROR("Invalid args\n");
2013 return -1;
2014 }
2015
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002016 memcpy(&cmd.param, rates, sizeof(*rates));
James Ketrenos43f66a62005-03-25 12:31:53 -06002017 if (ipw_send_cmd(priv, &cmd)) {
2018 IPW_ERROR("failed to send SUPPORTED_RATES command\n");
2019 return -1;
2020 }
Jeff Garzikbf794512005-07-31 13:07:26 -04002021
James Ketrenos43f66a62005-03-25 12:31:53 -06002022 return 0;
2023}
2024
2025static int ipw_set_random_seed(struct ipw_priv *priv)
2026{
2027 struct host_cmd cmd = {
2028 .cmd = IPW_CMD_SEED_NUMBER,
2029 .len = sizeof(u32)
2030 };
2031
2032 if (!priv) {
2033 IPW_ERROR("Invalid args\n");
2034 return -1;
2035 }
2036
2037 get_random_bytes(&cmd.param, sizeof(u32));
2038
2039 if (ipw_send_cmd(priv, &cmd)) {
2040 IPW_ERROR("failed to send SEED_NUMBER command\n");
2041 return -1;
2042 }
Jeff Garzikbf794512005-07-31 13:07:26 -04002043
James Ketrenos43f66a62005-03-25 12:31:53 -06002044 return 0;
2045}
2046
James Ketrenos43f66a62005-03-25 12:31:53 -06002047static int ipw_send_card_disable(struct ipw_priv *priv, u32 phy_off)
2048{
2049 struct host_cmd cmd = {
2050 .cmd = IPW_CMD_CARD_DISABLE,
2051 .len = sizeof(u32)
2052 };
2053
2054 if (!priv) {
2055 IPW_ERROR("Invalid args\n");
2056 return -1;
2057 }
2058
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002059 *((u32 *) & cmd.param) = phy_off;
James Ketrenos43f66a62005-03-25 12:31:53 -06002060
2061 if (ipw_send_cmd(priv, &cmd)) {
2062 IPW_ERROR("failed to send CARD_DISABLE command\n");
2063 return -1;
2064 }
Jeff Garzikbf794512005-07-31 13:07:26 -04002065
James Ketrenos43f66a62005-03-25 12:31:53 -06002066 return 0;
2067}
James Ketrenos43f66a62005-03-25 12:31:53 -06002068
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002069static int ipw_send_tx_power(struct ipw_priv *priv, struct ipw_tx_power *power)
James Ketrenos43f66a62005-03-25 12:31:53 -06002070{
2071 struct host_cmd cmd = {
2072 .cmd = IPW_CMD_TX_POWER,
2073 .len = sizeof(*power)
2074 };
2075
2076 if (!priv || !power) {
2077 IPW_ERROR("Invalid args\n");
2078 return -1;
2079 }
2080
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002081 memcpy(&cmd.param, power, sizeof(*power));
James Ketrenos43f66a62005-03-25 12:31:53 -06002082 if (ipw_send_cmd(priv, &cmd)) {
2083 IPW_ERROR("failed to send TX_POWER command\n");
2084 return -1;
2085 }
Jeff Garzikbf794512005-07-31 13:07:26 -04002086
James Ketrenos43f66a62005-03-25 12:31:53 -06002087 return 0;
2088}
2089
2090static int ipw_send_rts_threshold(struct ipw_priv *priv, u16 rts)
2091{
2092 struct ipw_rts_threshold rts_threshold = {
2093 .rts_threshold = rts,
2094 };
2095 struct host_cmd cmd = {
2096 .cmd = IPW_CMD_RTS_THRESHOLD,
2097 .len = sizeof(rts_threshold)
2098 };
2099
2100 if (!priv) {
2101 IPW_ERROR("Invalid args\n");
2102 return -1;
2103 }
2104
2105 memcpy(&cmd.param, &rts_threshold, sizeof(rts_threshold));
2106 if (ipw_send_cmd(priv, &cmd)) {
2107 IPW_ERROR("failed to send RTS_THRESHOLD command\n");
2108 return -1;
2109 }
2110
2111 return 0;
2112}
2113
2114static int ipw_send_frag_threshold(struct ipw_priv *priv, u16 frag)
2115{
2116 struct ipw_frag_threshold frag_threshold = {
2117 .frag_threshold = frag,
2118 };
2119 struct host_cmd cmd = {
2120 .cmd = IPW_CMD_FRAG_THRESHOLD,
2121 .len = sizeof(frag_threshold)
2122 };
2123
2124 if (!priv) {
2125 IPW_ERROR("Invalid args\n");
2126 return -1;
2127 }
2128
2129 memcpy(&cmd.param, &frag_threshold, sizeof(frag_threshold));
2130 if (ipw_send_cmd(priv, &cmd)) {
2131 IPW_ERROR("failed to send FRAG_THRESHOLD command\n");
2132 return -1;
2133 }
2134
2135 return 0;
2136}
2137
2138static int ipw_send_power_mode(struct ipw_priv *priv, u32 mode)
2139{
2140 struct host_cmd cmd = {
2141 .cmd = IPW_CMD_POWER_MODE,
2142 .len = sizeof(u32)
2143 };
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002144 u32 *param = (u32 *) (&cmd.param);
James Ketrenos43f66a62005-03-25 12:31:53 -06002145
2146 if (!priv) {
2147 IPW_ERROR("Invalid args\n");
2148 return -1;
2149 }
Jeff Garzikbf794512005-07-31 13:07:26 -04002150
James Ketrenos43f66a62005-03-25 12:31:53 -06002151 /* If on battery, set to 3, if AC set to CAM, else user
2152 * level */
2153 switch (mode) {
2154 case IPW_POWER_BATTERY:
2155 *param = IPW_POWER_INDEX_3;
2156 break;
2157 case IPW_POWER_AC:
2158 *param = IPW_POWER_MODE_CAM;
2159 break;
2160 default:
2161 *param = mode;
2162 break;
2163 }
2164
2165 if (ipw_send_cmd(priv, &cmd)) {
2166 IPW_ERROR("failed to send POWER_MODE command\n");
2167 return -1;
2168 }
2169
2170 return 0;
2171}
2172
2173/*
2174 * The IPW device contains a Microwire compatible EEPROM that stores
2175 * various data like the MAC address. Usually the firmware has exclusive
2176 * access to the eeprom, but during device initialization (before the
2177 * device driver has sent the HostComplete command to the firmware) the
2178 * device driver has read access to the EEPROM by way of indirect addressing
2179 * through a couple of memory mapped registers.
2180 *
2181 * The following is a simplified implementation for pulling data out of the
2182 * the eeprom, along with some helper functions to find information in
2183 * the per device private data's copy of the eeprom.
2184 *
2185 * NOTE: To better understand how these functions work (i.e what is a chip
2186 * select and why do have to keep driving the eeprom clock?), read
2187 * just about any data sheet for a Microwire compatible EEPROM.
2188 */
2189
2190/* write a 32 bit value into the indirect accessor register */
2191static inline void eeprom_write_reg(struct ipw_priv *p, u32 data)
2192{
2193 ipw_write_reg32(p, FW_MEM_REG_EEPROM_ACCESS, data);
Jeff Garzikbf794512005-07-31 13:07:26 -04002194
James Ketrenos43f66a62005-03-25 12:31:53 -06002195 /* the eeprom requires some time to complete the operation */
2196 udelay(p->eeprom_delay);
2197
2198 return;
2199}
2200
2201/* perform a chip select operation */
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002202static inline void eeprom_cs(struct ipw_priv *priv)
James Ketrenos43f66a62005-03-25 12:31:53 -06002203{
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002204 eeprom_write_reg(priv, 0);
2205 eeprom_write_reg(priv, EEPROM_BIT_CS);
2206 eeprom_write_reg(priv, EEPROM_BIT_CS | EEPROM_BIT_SK);
2207 eeprom_write_reg(priv, EEPROM_BIT_CS);
James Ketrenos43f66a62005-03-25 12:31:53 -06002208}
2209
2210/* perform a chip select operation */
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002211static inline void eeprom_disable_cs(struct ipw_priv *priv)
James Ketrenos43f66a62005-03-25 12:31:53 -06002212{
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002213 eeprom_write_reg(priv, EEPROM_BIT_CS);
2214 eeprom_write_reg(priv, 0);
2215 eeprom_write_reg(priv, EEPROM_BIT_SK);
James Ketrenos43f66a62005-03-25 12:31:53 -06002216}
2217
2218/* push a single bit down to the eeprom */
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002219static inline void eeprom_write_bit(struct ipw_priv *p, u8 bit)
James Ketrenos43f66a62005-03-25 12:31:53 -06002220{
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002221 int d = (bit ? EEPROM_BIT_DI : 0);
2222 eeprom_write_reg(p, EEPROM_BIT_CS | d);
2223 eeprom_write_reg(p, EEPROM_BIT_CS | d | EEPROM_BIT_SK);
James Ketrenos43f66a62005-03-25 12:31:53 -06002224}
2225
2226/* push an opcode followed by an address down to the eeprom */
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002227static void eeprom_op(struct ipw_priv *priv, u8 op, u8 addr)
James Ketrenos43f66a62005-03-25 12:31:53 -06002228{
2229 int i;
2230
2231 eeprom_cs(priv);
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002232 eeprom_write_bit(priv, 1);
2233 eeprom_write_bit(priv, op & 2);
2234 eeprom_write_bit(priv, op & 1);
2235 for (i = 7; i >= 0; i--) {
2236 eeprom_write_bit(priv, addr & (1 << i));
James Ketrenos43f66a62005-03-25 12:31:53 -06002237 }
2238}
2239
2240/* pull 16 bits off the eeprom, one bit at a time */
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002241static u16 eeprom_read_u16(struct ipw_priv *priv, u8 addr)
James Ketrenos43f66a62005-03-25 12:31:53 -06002242{
2243 int i;
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002244 u16 r = 0;
Jeff Garzikbf794512005-07-31 13:07:26 -04002245
James Ketrenos43f66a62005-03-25 12:31:53 -06002246 /* Send READ Opcode */
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002247 eeprom_op(priv, EEPROM_CMD_READ, addr);
James Ketrenos43f66a62005-03-25 12:31:53 -06002248
2249 /* Send dummy bit */
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002250 eeprom_write_reg(priv, EEPROM_BIT_CS);
James Ketrenos43f66a62005-03-25 12:31:53 -06002251
2252 /* Read the byte off the eeprom one bit at a time */
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002253 for (i = 0; i < 16; i++) {
James Ketrenos43f66a62005-03-25 12:31:53 -06002254 u32 data = 0;
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002255 eeprom_write_reg(priv, EEPROM_BIT_CS | EEPROM_BIT_SK);
2256 eeprom_write_reg(priv, EEPROM_BIT_CS);
2257 data = ipw_read_reg32(priv, FW_MEM_REG_EEPROM_ACCESS);
2258 r = (r << 1) | ((data & EEPROM_BIT_DO) ? 1 : 0);
James Ketrenos43f66a62005-03-25 12:31:53 -06002259 }
Jeff Garzikbf794512005-07-31 13:07:26 -04002260
James Ketrenos43f66a62005-03-25 12:31:53 -06002261 /* Send another dummy bit */
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002262 eeprom_write_reg(priv, 0);
James Ketrenos43f66a62005-03-25 12:31:53 -06002263 eeprom_disable_cs(priv);
Jeff Garzikbf794512005-07-31 13:07:26 -04002264
James Ketrenos43f66a62005-03-25 12:31:53 -06002265 return r;
2266}
2267
2268/* helper function for pulling the mac address out of the private */
2269/* data's copy of the eeprom data */
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002270static void eeprom_parse_mac(struct ipw_priv *priv, u8 * mac)
James Ketrenos43f66a62005-03-25 12:31:53 -06002271{
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002272 u8 *ee = (u8 *) priv->eeprom;
James Ketrenos43f66a62005-03-25 12:31:53 -06002273 memcpy(mac, &ee[EEPROM_MAC_ADDRESS], 6);
2274}
2275
2276/*
2277 * Either the device driver (i.e. the host) or the firmware can
2278 * load eeprom data into the designated region in SRAM. If neither
2279 * happens then the FW will shutdown with a fatal error.
2280 *
2281 * In order to signal the FW to load the EEPROM, the EEPROM_LOAD_DISABLE
2282 * bit needs region of shared SRAM needs to be non-zero.
2283 */
2284static void ipw_eeprom_init_sram(struct ipw_priv *priv)
2285{
2286 int i;
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002287 u16 *eeprom = (u16 *) priv->eeprom;
Jeff Garzikbf794512005-07-31 13:07:26 -04002288
James Ketrenos43f66a62005-03-25 12:31:53 -06002289 IPW_DEBUG_TRACE(">>\n");
2290
2291 /* read entire contents of eeprom into private buffer */
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002292 for (i = 0; i < 128; i++)
James Ketrenosa613bff2005-08-24 21:43:11 -05002293 eeprom[i] = le16_to_cpu(eeprom_read_u16(priv, (u8) i));
James Ketrenos43f66a62005-03-25 12:31:53 -06002294
Jeff Garzikbf794512005-07-31 13:07:26 -04002295 /*
2296 If the data looks correct, then copy it to our private
James Ketrenos43f66a62005-03-25 12:31:53 -06002297 copy. Otherwise let the firmware know to perform the operation
2298 on it's own
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002299 */
James Ketrenos43f66a62005-03-25 12:31:53 -06002300 if ((priv->eeprom + EEPROM_VERSION) != 0) {
2301 IPW_DEBUG_INFO("Writing EEPROM data into SRAM\n");
2302
2303 /* write the eeprom data to sram */
James Ketrenosb095c382005-08-24 22:04:42 -05002304 for (i = 0; i < IPW_EEPROM_IMAGE_SIZE; i++)
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002305 ipw_write8(priv, IPW_EEPROM_DATA + i, priv->eeprom[i]);
James Ketrenos43f66a62005-03-25 12:31:53 -06002306
2307 /* Do not load eeprom data on fatal error or suspend */
2308 ipw_write32(priv, IPW_EEPROM_LOAD_DISABLE, 0);
2309 } else {
2310 IPW_DEBUG_INFO("Enabling FW initializationg of SRAM\n");
2311
2312 /* Load eeprom data on fatal error or suspend */
2313 ipw_write32(priv, IPW_EEPROM_LOAD_DISABLE, 1);
2314 }
2315
2316 IPW_DEBUG_TRACE("<<\n");
2317}
2318
James Ketrenos43f66a62005-03-25 12:31:53 -06002319static inline void ipw_zero_memory(struct ipw_priv *priv, u32 start, u32 count)
2320{
2321 count >>= 2;
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002322 if (!count)
2323 return;
James Ketrenosb095c382005-08-24 22:04:42 -05002324 _ipw_write32(priv, IPW_AUTOINC_ADDR, start);
Jeff Garzikbf794512005-07-31 13:07:26 -04002325 while (count--)
James Ketrenosb095c382005-08-24 22:04:42 -05002326 _ipw_write32(priv, IPW_AUTOINC_DATA, 0);
James Ketrenos43f66a62005-03-25 12:31:53 -06002327}
2328
2329static inline void ipw_fw_dma_reset_command_blocks(struct ipw_priv *priv)
2330{
James Ketrenosb095c382005-08-24 22:04:42 -05002331 ipw_zero_memory(priv, IPW_SHARED_SRAM_DMA_CONTROL,
Jeff Garzikbf794512005-07-31 13:07:26 -04002332 CB_NUMBER_OF_ELEMENTS_SMALL *
James Ketrenos43f66a62005-03-25 12:31:53 -06002333 sizeof(struct command_block));
2334}
2335
2336static int ipw_fw_dma_enable(struct ipw_priv *priv)
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002337{ /* start dma engine but no transfers yet */
James Ketrenos43f66a62005-03-25 12:31:53 -06002338
2339 IPW_DEBUG_FW(">> : \n");
Jeff Garzikbf794512005-07-31 13:07:26 -04002340
James Ketrenos43f66a62005-03-25 12:31:53 -06002341 /* Start the dma */
2342 ipw_fw_dma_reset_command_blocks(priv);
Jeff Garzikbf794512005-07-31 13:07:26 -04002343
James Ketrenos43f66a62005-03-25 12:31:53 -06002344 /* Write CB base address */
James Ketrenosb095c382005-08-24 22:04:42 -05002345 ipw_write_reg32(priv, IPW_DMA_I_CB_BASE, IPW_SHARED_SRAM_DMA_CONTROL);
James Ketrenos43f66a62005-03-25 12:31:53 -06002346
2347 IPW_DEBUG_FW("<< : \n");
2348 return 0;
2349}
2350
2351static void ipw_fw_dma_abort(struct ipw_priv *priv)
2352{
2353 u32 control = 0;
2354
2355 IPW_DEBUG_FW(">> :\n");
Jeff Garzikbf794512005-07-31 13:07:26 -04002356
2357 //set the Stop and Abort bit
James Ketrenos43f66a62005-03-25 12:31:53 -06002358 control = DMA_CONTROL_SMALL_CB_CONST_VALUE | DMA_CB_STOP_AND_ABORT;
James Ketrenosb095c382005-08-24 22:04:42 -05002359 ipw_write_reg32(priv, IPW_DMA_I_DMA_CONTROL, control);
James Ketrenos43f66a62005-03-25 12:31:53 -06002360 priv->sram_desc.last_cb_index = 0;
Jeff Garzikbf794512005-07-31 13:07:26 -04002361
James Ketrenos43f66a62005-03-25 12:31:53 -06002362 IPW_DEBUG_FW("<< \n");
2363}
2364
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002365static int ipw_fw_dma_write_command_block(struct ipw_priv *priv, int index,
2366 struct command_block *cb)
James Ketrenos43f66a62005-03-25 12:31:53 -06002367{
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002368 u32 address =
James Ketrenosb095c382005-08-24 22:04:42 -05002369 IPW_SHARED_SRAM_DMA_CONTROL +
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002370 (sizeof(struct command_block) * index);
James Ketrenos43f66a62005-03-25 12:31:53 -06002371 IPW_DEBUG_FW(">> :\n");
2372
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002373 ipw_write_indirect(priv, address, (u8 *) cb,
2374 (int)sizeof(struct command_block));
James Ketrenos43f66a62005-03-25 12:31:53 -06002375
2376 IPW_DEBUG_FW("<< :\n");
2377 return 0;
2378
2379}
2380
2381static int ipw_fw_dma_kick(struct ipw_priv *priv)
2382{
2383 u32 control = 0;
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002384 u32 index = 0;
James Ketrenos43f66a62005-03-25 12:31:53 -06002385
2386 IPW_DEBUG_FW(">> :\n");
Jeff Garzikbf794512005-07-31 13:07:26 -04002387
James Ketrenos43f66a62005-03-25 12:31:53 -06002388 for (index = 0; index < priv->sram_desc.last_cb_index; index++)
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002389 ipw_fw_dma_write_command_block(priv, index,
2390 &priv->sram_desc.cb_list[index]);
James Ketrenos43f66a62005-03-25 12:31:53 -06002391
2392 /* Enable the DMA in the CSR register */
James Ketrenosb095c382005-08-24 22:04:42 -05002393 ipw_clear_bit(priv, IPW_RESET_REG,
2394 IPW_RESET_REG_MASTER_DISABLED |
2395 IPW_RESET_REG_STOP_MASTER);
Jeff Garzikbf794512005-07-31 13:07:26 -04002396
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002397 /* Set the Start bit. */
James Ketrenos43f66a62005-03-25 12:31:53 -06002398 control = DMA_CONTROL_SMALL_CB_CONST_VALUE | DMA_CB_START;
James Ketrenosb095c382005-08-24 22:04:42 -05002399 ipw_write_reg32(priv, IPW_DMA_I_DMA_CONTROL, control);
James Ketrenos43f66a62005-03-25 12:31:53 -06002400
2401 IPW_DEBUG_FW("<< :\n");
2402 return 0;
2403}
2404
2405static void ipw_fw_dma_dump_command_block(struct ipw_priv *priv)
2406{
2407 u32 address;
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002408 u32 register_value = 0;
2409 u32 cb_fields_address = 0;
James Ketrenos43f66a62005-03-25 12:31:53 -06002410
2411 IPW_DEBUG_FW(">> :\n");
James Ketrenosb095c382005-08-24 22:04:42 -05002412 address = ipw_read_reg32(priv, IPW_DMA_I_CURRENT_CB);
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002413 IPW_DEBUG_FW_INFO("Current CB is 0x%x \n", address);
James Ketrenos43f66a62005-03-25 12:31:53 -06002414
2415 /* Read the DMA Controlor register */
James Ketrenosb095c382005-08-24 22:04:42 -05002416 register_value = ipw_read_reg32(priv, IPW_DMA_I_DMA_CONTROL);
2417 IPW_DEBUG_FW_INFO("IPW_DMA_I_DMA_CONTROL is 0x%x \n", register_value);
James Ketrenos43f66a62005-03-25 12:31:53 -06002418
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002419 /* Print the CB values */
James Ketrenos43f66a62005-03-25 12:31:53 -06002420 cb_fields_address = address;
2421 register_value = ipw_read_reg32(priv, cb_fields_address);
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002422 IPW_DEBUG_FW_INFO("Current CB ControlField is 0x%x \n", register_value);
James Ketrenos43f66a62005-03-25 12:31:53 -06002423
2424 cb_fields_address += sizeof(u32);
2425 register_value = ipw_read_reg32(priv, cb_fields_address);
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002426 IPW_DEBUG_FW_INFO("Current CB Source Field is 0x%x \n", register_value);
James Ketrenos43f66a62005-03-25 12:31:53 -06002427
2428 cb_fields_address += sizeof(u32);
2429 register_value = ipw_read_reg32(priv, cb_fields_address);
2430 IPW_DEBUG_FW_INFO("Current CB Destination Field is 0x%x \n",
2431 register_value);
2432
2433 cb_fields_address += sizeof(u32);
2434 register_value = ipw_read_reg32(priv, cb_fields_address);
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002435 IPW_DEBUG_FW_INFO("Current CB Status Field is 0x%x \n", register_value);
James Ketrenos43f66a62005-03-25 12:31:53 -06002436
2437 IPW_DEBUG_FW(">> :\n");
2438}
2439
2440static int ipw_fw_dma_command_block_index(struct ipw_priv *priv)
2441{
2442 u32 current_cb_address = 0;
2443 u32 current_cb_index = 0;
2444
2445 IPW_DEBUG_FW("<< :\n");
James Ketrenosb095c382005-08-24 22:04:42 -05002446 current_cb_address = ipw_read_reg32(priv, IPW_DMA_I_CURRENT_CB);
Jeff Garzikbf794512005-07-31 13:07:26 -04002447
James Ketrenosb095c382005-08-24 22:04:42 -05002448 current_cb_index = (current_cb_address - IPW_SHARED_SRAM_DMA_CONTROL) /
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002449 sizeof(struct command_block);
Jeff Garzikbf794512005-07-31 13:07:26 -04002450
James Ketrenos43f66a62005-03-25 12:31:53 -06002451 IPW_DEBUG_FW_INFO("Current CB index 0x%x address = 0x%X \n",
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002452 current_cb_index, current_cb_address);
James Ketrenos43f66a62005-03-25 12:31:53 -06002453
2454 IPW_DEBUG_FW(">> :\n");
2455 return current_cb_index;
2456
2457}
2458
2459static int ipw_fw_dma_add_command_block(struct ipw_priv *priv,
2460 u32 src_address,
2461 u32 dest_address,
2462 u32 length,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002463 int interrupt_enabled, int is_last)
James Ketrenos43f66a62005-03-25 12:31:53 -06002464{
2465
Jeff Garzikbf794512005-07-31 13:07:26 -04002466 u32 control = CB_VALID | CB_SRC_LE | CB_DEST_LE | CB_SRC_AUTOINC |
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002467 CB_SRC_IO_GATED | CB_DEST_AUTOINC | CB_SRC_SIZE_LONG |
2468 CB_DEST_SIZE_LONG;
James Ketrenos43f66a62005-03-25 12:31:53 -06002469 struct command_block *cb;
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002470 u32 last_cb_element = 0;
James Ketrenos43f66a62005-03-25 12:31:53 -06002471
2472 IPW_DEBUG_FW_INFO("src_address=0x%x dest_address=0x%x length=0x%x\n",
2473 src_address, dest_address, length);
2474
2475 if (priv->sram_desc.last_cb_index >= CB_NUMBER_OF_ELEMENTS_SMALL)
2476 return -1;
2477
2478 last_cb_element = priv->sram_desc.last_cb_index;
2479 cb = &priv->sram_desc.cb_list[last_cb_element];
2480 priv->sram_desc.last_cb_index++;
2481
2482 /* Calculate the new CB control word */
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002483 if (interrupt_enabled)
James Ketrenos43f66a62005-03-25 12:31:53 -06002484 control |= CB_INT_ENABLED;
2485
2486 if (is_last)
2487 control |= CB_LAST_VALID;
Jeff Garzikbf794512005-07-31 13:07:26 -04002488
James Ketrenos43f66a62005-03-25 12:31:53 -06002489 control |= length;
2490
2491 /* Calculate the CB Element's checksum value */
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002492 cb->status = control ^ src_address ^ dest_address;
James Ketrenos43f66a62005-03-25 12:31:53 -06002493
2494 /* Copy the Source and Destination addresses */
2495 cb->dest_addr = dest_address;
2496 cb->source_addr = src_address;
2497
2498 /* Copy the Control Word last */
2499 cb->control = control;
2500
2501 return 0;
2502}
2503
2504static int ipw_fw_dma_add_buffer(struct ipw_priv *priv,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002505 u32 src_phys, u32 dest_address, u32 length)
James Ketrenos43f66a62005-03-25 12:31:53 -06002506{
2507 u32 bytes_left = length;
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002508 u32 src_offset = 0;
2509 u32 dest_offset = 0;
James Ketrenos43f66a62005-03-25 12:31:53 -06002510 int status = 0;
2511 IPW_DEBUG_FW(">> \n");
2512 IPW_DEBUG_FW_INFO("src_phys=0x%x dest_address=0x%x length=0x%x\n",
2513 src_phys, dest_address, length);
2514 while (bytes_left > CB_MAX_LENGTH) {
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002515 status = ipw_fw_dma_add_command_block(priv,
2516 src_phys + src_offset,
2517 dest_address +
2518 dest_offset,
2519 CB_MAX_LENGTH, 0, 0);
James Ketrenos43f66a62005-03-25 12:31:53 -06002520 if (status) {
2521 IPW_DEBUG_FW_INFO(": Failed\n");
2522 return -1;
Jeff Garzikbf794512005-07-31 13:07:26 -04002523 } else
James Ketrenos43f66a62005-03-25 12:31:53 -06002524 IPW_DEBUG_FW_INFO(": Added new cb\n");
2525
2526 src_offset += CB_MAX_LENGTH;
2527 dest_offset += CB_MAX_LENGTH;
2528 bytes_left -= CB_MAX_LENGTH;
2529 }
2530
2531 /* add the buffer tail */
2532 if (bytes_left > 0) {
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002533 status =
2534 ipw_fw_dma_add_command_block(priv, src_phys + src_offset,
2535 dest_address + dest_offset,
2536 bytes_left, 0, 0);
James Ketrenos43f66a62005-03-25 12:31:53 -06002537 if (status) {
2538 IPW_DEBUG_FW_INFO(": Failed on the buffer tail\n");
2539 return -1;
Jeff Garzikbf794512005-07-31 13:07:26 -04002540 } else
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002541 IPW_DEBUG_FW_INFO
2542 (": Adding new cb - the buffer tail\n");
James Ketrenos43f66a62005-03-25 12:31:53 -06002543 }
Jeff Garzikbf794512005-07-31 13:07:26 -04002544
James Ketrenos43f66a62005-03-25 12:31:53 -06002545 IPW_DEBUG_FW("<< \n");
2546 return 0;
2547}
2548
2549static int ipw_fw_dma_wait(struct ipw_priv *priv)
2550{
2551 u32 current_index = 0;
2552 u32 watchdog = 0;
2553
2554 IPW_DEBUG_FW(">> : \n");
2555
2556 current_index = ipw_fw_dma_command_block_index(priv);
Jeff Garzikbf794512005-07-31 13:07:26 -04002557 IPW_DEBUG_FW_INFO("sram_desc.last_cb_index:0x%8X\n",
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002558 (int)priv->sram_desc.last_cb_index);
James Ketrenos43f66a62005-03-25 12:31:53 -06002559
2560 while (current_index < priv->sram_desc.last_cb_index) {
2561 udelay(50);
2562 current_index = ipw_fw_dma_command_block_index(priv);
2563
2564 watchdog++;
2565
2566 if (watchdog > 400) {
2567 IPW_DEBUG_FW_INFO("Timeout\n");
2568 ipw_fw_dma_dump_command_block(priv);
2569 ipw_fw_dma_abort(priv);
2570 return -1;
2571 }
2572 }
2573
2574 ipw_fw_dma_abort(priv);
2575
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002576 /*Disable the DMA in the CSR register */
James Ketrenosb095c382005-08-24 22:04:42 -05002577 ipw_set_bit(priv, IPW_RESET_REG,
2578 IPW_RESET_REG_MASTER_DISABLED | IPW_RESET_REG_STOP_MASTER);
James Ketrenos43f66a62005-03-25 12:31:53 -06002579
2580 IPW_DEBUG_FW("<< dmaWaitSync \n");
2581 return 0;
2582}
2583
Jeff Garzikbf794512005-07-31 13:07:26 -04002584static void ipw_remove_current_network(struct ipw_priv *priv)
James Ketrenos43f66a62005-03-25 12:31:53 -06002585{
2586 struct list_head *element, *safe;
Jeff Garzikbf794512005-07-31 13:07:26 -04002587 struct ieee80211_network *network = NULL;
James Ketrenosa613bff2005-08-24 21:43:11 -05002588 unsigned long flags;
2589
2590 spin_lock_irqsave(&priv->ieee->lock, flags);
James Ketrenos43f66a62005-03-25 12:31:53 -06002591 list_for_each_safe(element, safe, &priv->ieee->network_list) {
2592 network = list_entry(element, struct ieee80211_network, list);
2593 if (!memcmp(network->bssid, priv->bssid, ETH_ALEN)) {
2594 list_del(element);
Jeff Garzikbf794512005-07-31 13:07:26 -04002595 list_add_tail(&network->list,
James Ketrenos43f66a62005-03-25 12:31:53 -06002596 &priv->ieee->network_free_list);
2597 }
2598 }
James Ketrenosa613bff2005-08-24 21:43:11 -05002599 spin_unlock_irqrestore(&priv->ieee->lock, flags);
James Ketrenos43f66a62005-03-25 12:31:53 -06002600}
2601
2602/**
Jeff Garzikbf794512005-07-31 13:07:26 -04002603 * Check that card is still alive.
James Ketrenos43f66a62005-03-25 12:31:53 -06002604 * Reads debug register from domain0.
2605 * If card is present, pre-defined value should
2606 * be found there.
Jeff Garzikbf794512005-07-31 13:07:26 -04002607 *
James Ketrenos43f66a62005-03-25 12:31:53 -06002608 * @param priv
2609 * @return 1 if card is present, 0 otherwise
2610 */
2611static inline int ipw_alive(struct ipw_priv *priv)
2612{
2613 return ipw_read32(priv, 0x90) == 0xd55555d5;
2614}
2615
2616static inline int ipw_poll_bit(struct ipw_priv *priv, u32 addr, u32 mask,
2617 int timeout)
2618{
2619 int i = 0;
2620
2621 do {
Jeff Garzikbf794512005-07-31 13:07:26 -04002622 if ((ipw_read32(priv, addr) & mask) == mask)
James Ketrenos43f66a62005-03-25 12:31:53 -06002623 return i;
2624 mdelay(10);
2625 i += 10;
2626 } while (i < timeout);
Jeff Garzikbf794512005-07-31 13:07:26 -04002627
James Ketrenos43f66a62005-03-25 12:31:53 -06002628 return -ETIME;
2629}
2630
Jeff Garzikbf794512005-07-31 13:07:26 -04002631/* These functions load the firmware and micro code for the operation of
James Ketrenos43f66a62005-03-25 12:31:53 -06002632 * the ipw hardware. It assumes the buffer has all the bits for the
2633 * image and the caller is handling the memory allocation and clean up.
2634 */
2635
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002636static int ipw_stop_master(struct ipw_priv *priv)
James Ketrenos43f66a62005-03-25 12:31:53 -06002637{
2638 int rc;
Jeff Garzikbf794512005-07-31 13:07:26 -04002639
James Ketrenos43f66a62005-03-25 12:31:53 -06002640 IPW_DEBUG_TRACE(">> \n");
2641 /* stop master. typical delay - 0 */
James Ketrenosb095c382005-08-24 22:04:42 -05002642 ipw_set_bit(priv, IPW_RESET_REG, IPW_RESET_REG_STOP_MASTER);
James Ketrenos43f66a62005-03-25 12:31:53 -06002643
James Ketrenosb095c382005-08-24 22:04:42 -05002644 rc = ipw_poll_bit(priv, IPW_RESET_REG,
2645 IPW_RESET_REG_MASTER_DISABLED, 100);
James Ketrenos43f66a62005-03-25 12:31:53 -06002646 if (rc < 0) {
2647 IPW_ERROR("stop master failed in 10ms\n");
2648 return -1;
2649 }
2650
2651 IPW_DEBUG_INFO("stop master %dms\n", rc);
2652
2653 return rc;
2654}
2655
2656static void ipw_arc_release(struct ipw_priv *priv)
2657{
2658 IPW_DEBUG_TRACE(">> \n");
2659 mdelay(5);
2660
James Ketrenosb095c382005-08-24 22:04:42 -05002661 ipw_clear_bit(priv, IPW_RESET_REG, CBD_RESET_REG_PRINCETON_RESET);
James Ketrenos43f66a62005-03-25 12:31:53 -06002662
2663 /* no one knows timing, for safety add some delay */
2664 mdelay(5);
2665}
2666
2667struct fw_header {
2668 u32 version;
2669 u32 mode;
2670};
2671
2672struct fw_chunk {
2673 u32 address;
2674 u32 length;
2675};
2676
2677#define IPW_FW_MAJOR_VERSION 2
James Ketrenosb095c382005-08-24 22:04:42 -05002678#define IPW_FW_MINOR_VERSION 3
James Ketrenos43f66a62005-03-25 12:31:53 -06002679
2680#define IPW_FW_MINOR(x) ((x & 0xff) >> 8)
2681#define IPW_FW_MAJOR(x) (x & 0xff)
2682
2683#define IPW_FW_VERSION ((IPW_FW_MINOR_VERSION << 8) | \
2684 IPW_FW_MAJOR_VERSION)
2685
2686#define IPW_FW_PREFIX "ipw-" __stringify(IPW_FW_MAJOR_VERSION) \
2687"." __stringify(IPW_FW_MINOR_VERSION) "-"
2688
2689#if IPW_FW_MAJOR_VERSION >= 2 && IPW_FW_MINOR_VERSION > 0
2690#define IPW_FW_NAME(x) IPW_FW_PREFIX "" x ".fw"
2691#else
2692#define IPW_FW_NAME(x) "ipw2200_" x ".fw"
2693#endif
2694
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002695static int ipw_load_ucode(struct ipw_priv *priv, u8 * data, size_t len)
James Ketrenos43f66a62005-03-25 12:31:53 -06002696{
2697 int rc = 0, i, addr;
2698 u8 cr = 0;
2699 u16 *image;
2700
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002701 image = (u16 *) data;
Jeff Garzikbf794512005-07-31 13:07:26 -04002702
James Ketrenos43f66a62005-03-25 12:31:53 -06002703 IPW_DEBUG_TRACE(">> \n");
2704
2705 rc = ipw_stop_master(priv);
2706
2707 if (rc < 0)
2708 return rc;
Jeff Garzikbf794512005-07-31 13:07:26 -04002709
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002710// spin_lock_irqsave(&priv->lock, flags);
Jeff Garzikbf794512005-07-31 13:07:26 -04002711
James Ketrenosb095c382005-08-24 22:04:42 -05002712 for (addr = IPW_SHARED_LOWER_BOUND;
2713 addr < IPW_REGISTER_DOMAIN1_END; addr += 4) {
James Ketrenos43f66a62005-03-25 12:31:53 -06002714 ipw_write32(priv, addr, 0);
2715 }
2716
2717 /* no ucode (yet) */
2718 memset(&priv->dino_alive, 0, sizeof(priv->dino_alive));
2719 /* destroy DMA queues */
2720 /* reset sequence */
2721
James Ketrenosb095c382005-08-24 22:04:42 -05002722 ipw_write_reg32(priv, IPW_MEM_HALT_AND_RESET, IPW_BIT_HALT_RESET_ON);
James Ketrenos43f66a62005-03-25 12:31:53 -06002723 ipw_arc_release(priv);
James Ketrenosb095c382005-08-24 22:04:42 -05002724 ipw_write_reg32(priv, IPW_MEM_HALT_AND_RESET, IPW_BIT_HALT_RESET_OFF);
James Ketrenos43f66a62005-03-25 12:31:53 -06002725 mdelay(1);
2726
2727 /* reset PHY */
James Ketrenosb095c382005-08-24 22:04:42 -05002728 ipw_write_reg32(priv, IPW_INTERNAL_CMD_EVENT, IPW_BASEBAND_POWER_DOWN);
James Ketrenos43f66a62005-03-25 12:31:53 -06002729 mdelay(1);
Jeff Garzikbf794512005-07-31 13:07:26 -04002730
James Ketrenosb095c382005-08-24 22:04:42 -05002731 ipw_write_reg32(priv, IPW_INTERNAL_CMD_EVENT, 0);
James Ketrenos43f66a62005-03-25 12:31:53 -06002732 mdelay(1);
Jeff Garzikbf794512005-07-31 13:07:26 -04002733
James Ketrenos43f66a62005-03-25 12:31:53 -06002734 /* enable ucode store */
2735 ipw_write_reg8(priv, DINO_CONTROL_REG, 0x0);
2736 ipw_write_reg8(priv, DINO_CONTROL_REG, DINO_ENABLE_CS);
2737 mdelay(1);
2738
2739 /* write ucode */
2740 /**
2741 * @bug
2742 * Do NOT set indirect address register once and then
2743 * store data to indirect data register in the loop.
2744 * It seems very reasonable, but in this case DINO do not
2745 * accept ucode. It is essential to set address each time.
2746 */
2747 /* load new ipw uCode */
2748 for (i = 0; i < len / 2; i++)
James Ketrenosb095c382005-08-24 22:04:42 -05002749 ipw_write_reg16(priv, IPW_BASEBAND_CONTROL_STORE,
James Ketrenosa613bff2005-08-24 21:43:11 -05002750 cpu_to_le16(image[i]));
James Ketrenos43f66a62005-03-25 12:31:53 -06002751
James Ketrenos43f66a62005-03-25 12:31:53 -06002752 /* enable DINO */
James Ketrenosb095c382005-08-24 22:04:42 -05002753 ipw_write_reg8(priv, IPW_BASEBAND_CONTROL_STATUS, 0);
2754 ipw_write_reg8(priv, IPW_BASEBAND_CONTROL_STATUS, DINO_ENABLE_SYSTEM);
James Ketrenos43f66a62005-03-25 12:31:53 -06002755
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002756 /* this is where the igx / win driver deveates from the VAP driver. */
James Ketrenos43f66a62005-03-25 12:31:53 -06002757
2758 /* wait for alive response */
2759 for (i = 0; i < 100; i++) {
2760 /* poll for incoming data */
James Ketrenosb095c382005-08-24 22:04:42 -05002761 cr = ipw_read_reg8(priv, IPW_BASEBAND_CONTROL_STATUS);
James Ketrenos43f66a62005-03-25 12:31:53 -06002762 if (cr & DINO_RXFIFO_DATA)
2763 break;
2764 mdelay(1);
2765 }
2766
2767 if (cr & DINO_RXFIFO_DATA) {
2768 /* alive_command_responce size is NOT multiple of 4 */
2769 u32 response_buffer[(sizeof(priv->dino_alive) + 3) / 4];
Jeff Garzikbf794512005-07-31 13:07:26 -04002770
2771 for (i = 0; i < ARRAY_SIZE(response_buffer); i++)
James Ketrenos43f66a62005-03-25 12:31:53 -06002772 response_buffer[i] =
James Ketrenosa613bff2005-08-24 21:43:11 -05002773 le32_to_cpu(ipw_read_reg32(priv,
James Ketrenosb095c382005-08-24 22:04:42 -05002774 IPW_BASEBAND_RX_FIFO_READ));
James Ketrenos43f66a62005-03-25 12:31:53 -06002775 memcpy(&priv->dino_alive, response_buffer,
2776 sizeof(priv->dino_alive));
2777 if (priv->dino_alive.alive_command == 1
2778 && priv->dino_alive.ucode_valid == 1) {
2779 rc = 0;
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002780 IPW_DEBUG_INFO
2781 ("Microcode OK, rev. %d (0x%x) dev. %d (0x%x) "
2782 "of %02d/%02d/%02d %02d:%02d\n",
2783 priv->dino_alive.software_revision,
2784 priv->dino_alive.software_revision,
2785 priv->dino_alive.device_identifier,
2786 priv->dino_alive.device_identifier,
2787 priv->dino_alive.time_stamp[0],
2788 priv->dino_alive.time_stamp[1],
2789 priv->dino_alive.time_stamp[2],
2790 priv->dino_alive.time_stamp[3],
2791 priv->dino_alive.time_stamp[4]);
James Ketrenos43f66a62005-03-25 12:31:53 -06002792 } else {
2793 IPW_DEBUG_INFO("Microcode is not alive\n");
2794 rc = -EINVAL;
2795 }
2796 } else {
2797 IPW_DEBUG_INFO("No alive response from DINO\n");
2798 rc = -ETIME;
2799 }
2800
2801 /* disable DINO, otherwise for some reason
2802 firmware have problem getting alive resp. */
James Ketrenosb095c382005-08-24 22:04:42 -05002803 ipw_write_reg8(priv, IPW_BASEBAND_CONTROL_STATUS, 0);
James Ketrenos43f66a62005-03-25 12:31:53 -06002804
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002805// spin_unlock_irqrestore(&priv->lock, flags);
James Ketrenos43f66a62005-03-25 12:31:53 -06002806
2807 return rc;
2808}
2809
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002810static int ipw_load_firmware(struct ipw_priv *priv, u8 * data, size_t len)
James Ketrenos43f66a62005-03-25 12:31:53 -06002811{
2812 int rc = -1;
2813 int offset = 0;
2814 struct fw_chunk *chunk;
2815 dma_addr_t shared_phys;
2816 u8 *shared_virt;
2817
2818 IPW_DEBUG_TRACE("<< : \n");
2819 shared_virt = pci_alloc_consistent(priv->pci_dev, len, &shared_phys);
2820
2821 if (!shared_virt)
2822 return -ENOMEM;
2823
2824 memmove(shared_virt, data, len);
2825
2826 /* Start the Dma */
2827 rc = ipw_fw_dma_enable(priv);
2828
2829 if (priv->sram_desc.last_cb_index > 0) {
2830 /* the DMA is already ready this would be a bug. */
2831 BUG();
2832 goto out;
2833 }
2834
2835 do {
2836 chunk = (struct fw_chunk *)(data + offset);
2837 offset += sizeof(struct fw_chunk);
2838 /* build DMA packet and queue up for sending */
Jeff Garzikbf794512005-07-31 13:07:26 -04002839 /* dma to chunk->address, the chunk->length bytes from data +
James Ketrenos43f66a62005-03-25 12:31:53 -06002840 * offeset*/
2841 /* Dma loading */
2842 rc = ipw_fw_dma_add_buffer(priv, shared_phys + offset,
James Ketrenosa613bff2005-08-24 21:43:11 -05002843 le32_to_cpu(chunk->address),
2844 le32_to_cpu(chunk->length));
James Ketrenos43f66a62005-03-25 12:31:53 -06002845 if (rc) {
2846 IPW_DEBUG_INFO("dmaAddBuffer Failed\n");
2847 goto out;
2848 }
Jeff Garzikbf794512005-07-31 13:07:26 -04002849
James Ketrenosa613bff2005-08-24 21:43:11 -05002850 offset += le32_to_cpu(chunk->length);
James Ketrenos43f66a62005-03-25 12:31:53 -06002851 } while (offset < len);
2852
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002853 /* Run the DMA and wait for the answer */
James Ketrenos43f66a62005-03-25 12:31:53 -06002854 rc = ipw_fw_dma_kick(priv);
2855 if (rc) {
2856 IPW_ERROR("dmaKick Failed\n");
2857 goto out;
2858 }
2859
2860 rc = ipw_fw_dma_wait(priv);
2861 if (rc) {
2862 IPW_ERROR("dmaWaitSync Failed\n");
2863 goto out;
2864 }
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002865 out:
2866 pci_free_consistent(priv->pci_dev, len, shared_virt, shared_phys);
James Ketrenos43f66a62005-03-25 12:31:53 -06002867 return rc;
2868}
2869
2870/* stop nic */
2871static int ipw_stop_nic(struct ipw_priv *priv)
2872{
2873 int rc = 0;
2874
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002875 /* stop */
James Ketrenosb095c382005-08-24 22:04:42 -05002876 ipw_write32(priv, IPW_RESET_REG, IPW_RESET_REG_STOP_MASTER);
Jeff Garzikbf794512005-07-31 13:07:26 -04002877
James Ketrenosb095c382005-08-24 22:04:42 -05002878 rc = ipw_poll_bit(priv, IPW_RESET_REG,
2879 IPW_RESET_REG_MASTER_DISABLED, 500);
James Ketrenos43f66a62005-03-25 12:31:53 -06002880 if (rc < 0) {
2881 IPW_ERROR("wait for reg master disabled failed\n");
2882 return rc;
Jeff Garzikbf794512005-07-31 13:07:26 -04002883 }
James Ketrenos43f66a62005-03-25 12:31:53 -06002884
James Ketrenosb095c382005-08-24 22:04:42 -05002885 ipw_set_bit(priv, IPW_RESET_REG, CBD_RESET_REG_PRINCETON_RESET);
Jeff Garzikbf794512005-07-31 13:07:26 -04002886
James Ketrenos43f66a62005-03-25 12:31:53 -06002887 return rc;
2888}
2889
2890static void ipw_start_nic(struct ipw_priv *priv)
2891{
2892 IPW_DEBUG_TRACE(">>\n");
2893
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002894 /* prvHwStartNic release ARC */
James Ketrenosb095c382005-08-24 22:04:42 -05002895 ipw_clear_bit(priv, IPW_RESET_REG,
2896 IPW_RESET_REG_MASTER_DISABLED |
2897 IPW_RESET_REG_STOP_MASTER |
James Ketrenos43f66a62005-03-25 12:31:53 -06002898 CBD_RESET_REG_PRINCETON_RESET);
Jeff Garzikbf794512005-07-31 13:07:26 -04002899
James Ketrenos43f66a62005-03-25 12:31:53 -06002900 /* enable power management */
James Ketrenosb095c382005-08-24 22:04:42 -05002901 ipw_set_bit(priv, IPW_GP_CNTRL_RW,
2902 IPW_GP_CNTRL_BIT_HOST_ALLOWS_STANDBY);
James Ketrenos43f66a62005-03-25 12:31:53 -06002903
2904 IPW_DEBUG_TRACE("<<\n");
2905}
Jeff Garzikbf794512005-07-31 13:07:26 -04002906
James Ketrenos43f66a62005-03-25 12:31:53 -06002907static int ipw_init_nic(struct ipw_priv *priv)
2908{
2909 int rc;
2910
2911 IPW_DEBUG_TRACE(">>\n");
Jeff Garzikbf794512005-07-31 13:07:26 -04002912 /* reset */
James Ketrenos43f66a62005-03-25 12:31:53 -06002913 /*prvHwInitNic */
2914 /* set "initialization complete" bit to move adapter to D0 state */
James Ketrenosb095c382005-08-24 22:04:42 -05002915 ipw_set_bit(priv, IPW_GP_CNTRL_RW, IPW_GP_CNTRL_BIT_INIT_DONE);
James Ketrenos43f66a62005-03-25 12:31:53 -06002916
2917 /* low-level PLL activation */
James Ketrenosb095c382005-08-24 22:04:42 -05002918 ipw_write32(priv, IPW_READ_INT_REGISTER,
2919 IPW_BIT_INT_HOST_SRAM_READ_INT_REGISTER);
James Ketrenos43f66a62005-03-25 12:31:53 -06002920
2921 /* wait for clock stabilization */
James Ketrenosb095c382005-08-24 22:04:42 -05002922 rc = ipw_poll_bit(priv, IPW_GP_CNTRL_RW,
2923 IPW_GP_CNTRL_BIT_CLOCK_READY, 250);
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002924 if (rc < 0)
James Ketrenos43f66a62005-03-25 12:31:53 -06002925 IPW_DEBUG_INFO("FAILED wait for clock stablization\n");
2926
2927 /* assert SW reset */
James Ketrenosb095c382005-08-24 22:04:42 -05002928 ipw_set_bit(priv, IPW_RESET_REG, IPW_RESET_REG_SW_RESET);
James Ketrenos43f66a62005-03-25 12:31:53 -06002929
2930 udelay(10);
2931
2932 /* set "initialization complete" bit to move adapter to D0 state */
James Ketrenosb095c382005-08-24 22:04:42 -05002933 ipw_set_bit(priv, IPW_GP_CNTRL_RW, IPW_GP_CNTRL_BIT_INIT_DONE);
James Ketrenos43f66a62005-03-25 12:31:53 -06002934
2935 IPW_DEBUG_TRACE(">>\n");
2936 return 0;
2937}
2938
Jeff Garzikbf794512005-07-31 13:07:26 -04002939/* Call this function from process context, it will sleep in request_firmware.
James Ketrenos43f66a62005-03-25 12:31:53 -06002940 * Probe is an ok place to call this from.
2941 */
2942static int ipw_reset_nic(struct ipw_priv *priv)
2943{
2944 int rc = 0;
James Ketrenosa613bff2005-08-24 21:43:11 -05002945 unsigned long flags;
James Ketrenos43f66a62005-03-25 12:31:53 -06002946
2947 IPW_DEBUG_TRACE(">>\n");
Jeff Garzikbf794512005-07-31 13:07:26 -04002948
James Ketrenos43f66a62005-03-25 12:31:53 -06002949 rc = ipw_init_nic(priv);
Jeff Garzikbf794512005-07-31 13:07:26 -04002950
James Ketrenosa613bff2005-08-24 21:43:11 -05002951 spin_lock_irqsave(&priv->lock, flags);
James Ketrenos43f66a62005-03-25 12:31:53 -06002952 /* Clear the 'host command active' bit... */
2953 priv->status &= ~STATUS_HCMD_ACTIVE;
2954 wake_up_interruptible(&priv->wait_command_queue);
James Ketrenosa613bff2005-08-24 21:43:11 -05002955 spin_unlock_irqrestore(&priv->lock, flags);
James Ketrenos43f66a62005-03-25 12:31:53 -06002956
2957 IPW_DEBUG_TRACE("<<\n");
2958 return rc;
Jeff Garzikbf794512005-07-31 13:07:26 -04002959}
James Ketrenos43f66a62005-03-25 12:31:53 -06002960
Jeff Garzikbf794512005-07-31 13:07:26 -04002961static int ipw_get_fw(struct ipw_priv *priv,
James Ketrenos43f66a62005-03-25 12:31:53 -06002962 const struct firmware **fw, const char *name)
2963{
2964 struct fw_header *header;
2965 int rc;
2966
2967 /* ask firmware_class module to get the boot firmware off disk */
2968 rc = request_firmware(fw, name, &priv->pci_dev->dev);
2969 if (rc < 0) {
2970 IPW_ERROR("%s load failed: Reason %d\n", name, rc);
2971 return rc;
Jeff Garzikbf794512005-07-31 13:07:26 -04002972 }
James Ketrenos43f66a62005-03-25 12:31:53 -06002973
2974 header = (struct fw_header *)(*fw)->data;
James Ketrenosa613bff2005-08-24 21:43:11 -05002975 if (IPW_FW_MAJOR(le32_to_cpu(header->version)) != IPW_FW_MAJOR_VERSION) {
James Ketrenos43f66a62005-03-25 12:31:53 -06002976 IPW_ERROR("'%s' firmware version not compatible (%d != %d)\n",
2977 name,
James Ketrenosa613bff2005-08-24 21:43:11 -05002978 IPW_FW_MAJOR(le32_to_cpu(header->version)),
2979 IPW_FW_MAJOR_VERSION);
James Ketrenos43f66a62005-03-25 12:31:53 -06002980 return -EINVAL;
2981 }
2982
Jiri Bencaaa4d302005-06-07 14:58:41 +02002983 IPW_DEBUG_INFO("Loading firmware '%s' file v%d.%d (%zd bytes)\n",
James Ketrenos43f66a62005-03-25 12:31:53 -06002984 name,
James Ketrenosa613bff2005-08-24 21:43:11 -05002985 IPW_FW_MAJOR(le32_to_cpu(header->version)),
2986 IPW_FW_MINOR(le32_to_cpu(header->version)),
James Ketrenos43f66a62005-03-25 12:31:53 -06002987 (*fw)->size - sizeof(struct fw_header));
2988 return 0;
2989}
2990
James Ketrenosb095c382005-08-24 22:04:42 -05002991#define IPW_RX_BUF_SIZE (3000)
James Ketrenos43f66a62005-03-25 12:31:53 -06002992
2993static inline void ipw_rx_queue_reset(struct ipw_priv *priv,
2994 struct ipw_rx_queue *rxq)
2995{
2996 unsigned long flags;
2997 int i;
2998
2999 spin_lock_irqsave(&rxq->lock, flags);
3000
3001 INIT_LIST_HEAD(&rxq->rx_free);
3002 INIT_LIST_HEAD(&rxq->rx_used);
3003
3004 /* Fill the rx_used queue with _all_ of the Rx buffers */
3005 for (i = 0; i < RX_FREE_BUFFERS + RX_QUEUE_SIZE; i++) {
3006 /* In the reset function, these buffers may have been allocated
3007 * to an SKB, so we need to unmap and free potential storage */
3008 if (rxq->pool[i].skb != NULL) {
3009 pci_unmap_single(priv->pci_dev, rxq->pool[i].dma_addr,
James Ketrenosb095c382005-08-24 22:04:42 -05003010 IPW_RX_BUF_SIZE, PCI_DMA_FROMDEVICE);
James Ketrenos43f66a62005-03-25 12:31:53 -06003011 dev_kfree_skb(rxq->pool[i].skb);
James Ketrenosa613bff2005-08-24 21:43:11 -05003012 rxq->pool[i].skb = NULL;
James Ketrenos43f66a62005-03-25 12:31:53 -06003013 }
3014 list_add_tail(&rxq->pool[i].list, &rxq->rx_used);
3015 }
Jeff Garzikbf794512005-07-31 13:07:26 -04003016
James Ketrenos43f66a62005-03-25 12:31:53 -06003017 /* Set us so that we have processed and used all buffers, but have
3018 * not restocked the Rx queue with fresh buffers */
3019 rxq->read = rxq->write = 0;
3020 rxq->processed = RX_QUEUE_SIZE - 1;
3021 rxq->free_count = 0;
3022 spin_unlock_irqrestore(&rxq->lock, flags);
3023}
3024
3025#ifdef CONFIG_PM
3026static int fw_loaded = 0;
3027static const struct firmware *bootfw = NULL;
3028static const struct firmware *firmware = NULL;
3029static const struct firmware *ucode = NULL;
3030#endif
3031
3032static int ipw_load(struct ipw_priv *priv)
3033{
3034#ifndef CONFIG_PM
3035 const struct firmware *bootfw = NULL;
3036 const struct firmware *firmware = NULL;
3037 const struct firmware *ucode = NULL;
3038#endif
3039 int rc = 0, retries = 3;
3040
3041#ifdef CONFIG_PM
3042 if (!fw_loaded) {
3043#endif
3044 rc = ipw_get_fw(priv, &bootfw, IPW_FW_NAME("boot"));
Jeff Garzikbf794512005-07-31 13:07:26 -04003045 if (rc)
James Ketrenos43f66a62005-03-25 12:31:53 -06003046 goto error;
Jeff Garzikbf794512005-07-31 13:07:26 -04003047
James Ketrenos43f66a62005-03-25 12:31:53 -06003048 switch (priv->ieee->iw_mode) {
3049 case IW_MODE_ADHOC:
Jeff Garzikbf794512005-07-31 13:07:26 -04003050 rc = ipw_get_fw(priv, &ucode,
James Ketrenos43f66a62005-03-25 12:31:53 -06003051 IPW_FW_NAME("ibss_ucode"));
Jeff Garzikbf794512005-07-31 13:07:26 -04003052 if (rc)
James Ketrenos43f66a62005-03-25 12:31:53 -06003053 goto error;
Jeff Garzikbf794512005-07-31 13:07:26 -04003054
James Ketrenos43f66a62005-03-25 12:31:53 -06003055 rc = ipw_get_fw(priv, &firmware, IPW_FW_NAME("ibss"));
3056 break;
Jeff Garzikbf794512005-07-31 13:07:26 -04003057
James Ketrenosb095c382005-08-24 22:04:42 -05003058#ifdef CONFIG_IPW2200_MONITOR
James Ketrenos43f66a62005-03-25 12:31:53 -06003059 case IW_MODE_MONITOR:
Jeff Garzikbf794512005-07-31 13:07:26 -04003060 rc = ipw_get_fw(priv, &ucode,
James Ketrenosea2b26e2005-08-24 21:25:16 -05003061 IPW_FW_NAME("sniffer_ucode"));
Jeff Garzikbf794512005-07-31 13:07:26 -04003062 if (rc)
James Ketrenos43f66a62005-03-25 12:31:53 -06003063 goto error;
Jeff Garzikbf794512005-07-31 13:07:26 -04003064
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003065 rc = ipw_get_fw(priv, &firmware,
3066 IPW_FW_NAME("sniffer"));
James Ketrenos43f66a62005-03-25 12:31:53 -06003067 break;
3068#endif
3069 case IW_MODE_INFRA:
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003070 rc = ipw_get_fw(priv, &ucode, IPW_FW_NAME("bss_ucode"));
Jeff Garzikbf794512005-07-31 13:07:26 -04003071 if (rc)
James Ketrenos43f66a62005-03-25 12:31:53 -06003072 goto error;
Jeff Garzikbf794512005-07-31 13:07:26 -04003073
James Ketrenos43f66a62005-03-25 12:31:53 -06003074 rc = ipw_get_fw(priv, &firmware, IPW_FW_NAME("bss"));
3075 break;
Jeff Garzikbf794512005-07-31 13:07:26 -04003076
James Ketrenos43f66a62005-03-25 12:31:53 -06003077 default:
3078 rc = -EINVAL;
3079 }
3080
Jeff Garzikbf794512005-07-31 13:07:26 -04003081 if (rc)
James Ketrenos43f66a62005-03-25 12:31:53 -06003082 goto error;
3083
3084#ifdef CONFIG_PM
3085 fw_loaded = 1;
3086 }
3087#endif
3088
3089 if (!priv->rxq)
3090 priv->rxq = ipw_rx_queue_alloc(priv);
3091 else
3092 ipw_rx_queue_reset(priv, priv->rxq);
3093 if (!priv->rxq) {
3094 IPW_ERROR("Unable to initialize Rx queue\n");
3095 goto error;
3096 }
3097
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003098 retry:
James Ketrenos43f66a62005-03-25 12:31:53 -06003099 /* Ensure interrupts are disabled */
James Ketrenosb095c382005-08-24 22:04:42 -05003100 ipw_write32(priv, IPW_INTA_MASK_R, ~IPW_INTA_MASK_ALL);
James Ketrenos43f66a62005-03-25 12:31:53 -06003101 priv->status &= ~STATUS_INT_ENABLED;
3102
3103 /* ack pending interrupts */
James Ketrenosb095c382005-08-24 22:04:42 -05003104 ipw_write32(priv, IPW_INTA_RW, IPW_INTA_MASK_ALL);
Jeff Garzikbf794512005-07-31 13:07:26 -04003105
James Ketrenos43f66a62005-03-25 12:31:53 -06003106 ipw_stop_nic(priv);
3107
3108 rc = ipw_reset_nic(priv);
3109 if (rc) {
3110 IPW_ERROR("Unable to reset NIC\n");
3111 goto error;
3112 }
3113
James Ketrenosb095c382005-08-24 22:04:42 -05003114 ipw_zero_memory(priv, IPW_NIC_SRAM_LOWER_BOUND,
3115 IPW_NIC_SRAM_UPPER_BOUND - IPW_NIC_SRAM_LOWER_BOUND);
James Ketrenos43f66a62005-03-25 12:31:53 -06003116
3117 /* DMA the initial boot firmware into the device */
Jeff Garzikbf794512005-07-31 13:07:26 -04003118 rc = ipw_load_firmware(priv, bootfw->data + sizeof(struct fw_header),
James Ketrenos43f66a62005-03-25 12:31:53 -06003119 bootfw->size - sizeof(struct fw_header));
3120 if (rc < 0) {
3121 IPW_ERROR("Unable to load boot firmware\n");
3122 goto error;
3123 }
3124
3125 /* kick start the device */
3126 ipw_start_nic(priv);
3127
3128 /* wait for the device to finish it's initial startup sequence */
James Ketrenosb095c382005-08-24 22:04:42 -05003129 rc = ipw_poll_bit(priv, IPW_INTA_RW,
3130 IPW_INTA_BIT_FW_INITIALIZATION_DONE, 500);
James Ketrenos43f66a62005-03-25 12:31:53 -06003131 if (rc < 0) {
3132 IPW_ERROR("device failed to boot initial fw image\n");
3133 goto error;
3134 }
3135 IPW_DEBUG_INFO("initial device response after %dms\n", rc);
3136
Jeff Garzikbf794512005-07-31 13:07:26 -04003137 /* ack fw init done interrupt */
James Ketrenosb095c382005-08-24 22:04:42 -05003138 ipw_write32(priv, IPW_INTA_RW, IPW_INTA_BIT_FW_INITIALIZATION_DONE);
James Ketrenos43f66a62005-03-25 12:31:53 -06003139
3140 /* DMA the ucode into the device */
Jeff Garzikbf794512005-07-31 13:07:26 -04003141 rc = ipw_load_ucode(priv, ucode->data + sizeof(struct fw_header),
James Ketrenos43f66a62005-03-25 12:31:53 -06003142 ucode->size - sizeof(struct fw_header));
3143 if (rc < 0) {
3144 IPW_ERROR("Unable to load ucode\n");
3145 goto error;
3146 }
Jeff Garzikbf794512005-07-31 13:07:26 -04003147
James Ketrenos43f66a62005-03-25 12:31:53 -06003148 /* stop nic */
3149 ipw_stop_nic(priv);
3150
3151 /* DMA bss firmware into the device */
Jeff Garzikbf794512005-07-31 13:07:26 -04003152 rc = ipw_load_firmware(priv, firmware->data +
3153 sizeof(struct fw_header),
James Ketrenos43f66a62005-03-25 12:31:53 -06003154 firmware->size - sizeof(struct fw_header));
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003155 if (rc < 0) {
James Ketrenos43f66a62005-03-25 12:31:53 -06003156 IPW_ERROR("Unable to load firmware\n");
3157 goto error;
3158 }
3159
3160 ipw_write32(priv, IPW_EEPROM_LOAD_DISABLE, 0);
3161
3162 rc = ipw_queue_reset(priv);
3163 if (rc) {
3164 IPW_ERROR("Unable to initialize queues\n");
3165 goto error;
3166 }
3167
3168 /* Ensure interrupts are disabled */
James Ketrenosb095c382005-08-24 22:04:42 -05003169 ipw_write32(priv, IPW_INTA_MASK_R, ~IPW_INTA_MASK_ALL);
James Ketrenosc848d0a2005-08-24 21:56:24 -05003170 /* ack pending interrupts */
James Ketrenosb095c382005-08-24 22:04:42 -05003171 ipw_write32(priv, IPW_INTA_RW, IPW_INTA_MASK_ALL);
Jeff Garzikbf794512005-07-31 13:07:26 -04003172
James Ketrenos43f66a62005-03-25 12:31:53 -06003173 /* kick start the device */
3174 ipw_start_nic(priv);
3175
James Ketrenosb095c382005-08-24 22:04:42 -05003176 if (ipw_read32(priv, IPW_INTA_RW) & IPW_INTA_BIT_PARITY_ERROR) {
James Ketrenos43f66a62005-03-25 12:31:53 -06003177 if (retries > 0) {
3178 IPW_WARNING("Parity error. Retrying init.\n");
3179 retries--;
3180 goto retry;
3181 }
3182
3183 IPW_ERROR("TODO: Handle parity error -- schedule restart?\n");
3184 rc = -EIO;
3185 goto error;
3186 }
3187
3188 /* wait for the device */
James Ketrenosb095c382005-08-24 22:04:42 -05003189 rc = ipw_poll_bit(priv, IPW_INTA_RW,
3190 IPW_INTA_BIT_FW_INITIALIZATION_DONE, 500);
James Ketrenos43f66a62005-03-25 12:31:53 -06003191 if (rc < 0) {
3192 IPW_ERROR("device failed to start after 500ms\n");
3193 goto error;
3194 }
3195 IPW_DEBUG_INFO("device response after %dms\n", rc);
3196
3197 /* ack fw init done interrupt */
James Ketrenosb095c382005-08-24 22:04:42 -05003198 ipw_write32(priv, IPW_INTA_RW, IPW_INTA_BIT_FW_INITIALIZATION_DONE);
James Ketrenos43f66a62005-03-25 12:31:53 -06003199
3200 /* read eeprom data and initialize the eeprom region of sram */
3201 priv->eeprom_delay = 1;
Jeff Garzikbf794512005-07-31 13:07:26 -04003202 ipw_eeprom_init_sram(priv);
James Ketrenos43f66a62005-03-25 12:31:53 -06003203
3204 /* enable interrupts */
3205 ipw_enable_interrupts(priv);
3206
3207 /* Ensure our queue has valid packets */
3208 ipw_rx_queue_replenish(priv);
3209
James Ketrenosb095c382005-08-24 22:04:42 -05003210 ipw_write32(priv, IPW_RX_READ_INDEX, priv->rxq->read);
James Ketrenos43f66a62005-03-25 12:31:53 -06003211
3212 /* ack pending interrupts */
James Ketrenosb095c382005-08-24 22:04:42 -05003213 ipw_write32(priv, IPW_INTA_RW, IPW_INTA_MASK_ALL);
James Ketrenos43f66a62005-03-25 12:31:53 -06003214
3215#ifndef CONFIG_PM
3216 release_firmware(bootfw);
3217 release_firmware(ucode);
3218 release_firmware(firmware);
3219#endif
3220 return 0;
3221
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003222 error:
James Ketrenos43f66a62005-03-25 12:31:53 -06003223 if (priv->rxq) {
3224 ipw_rx_queue_free(priv, priv->rxq);
3225 priv->rxq = NULL;
3226 }
3227 ipw_tx_queue_free(priv);
3228 if (bootfw)
3229 release_firmware(bootfw);
3230 if (ucode)
3231 release_firmware(ucode);
3232 if (firmware)
3233 release_firmware(firmware);
3234#ifdef CONFIG_PM
3235 fw_loaded = 0;
3236 bootfw = ucode = firmware = NULL;
3237#endif
3238
3239 return rc;
3240}
3241
Jeff Garzikbf794512005-07-31 13:07:26 -04003242/**
James Ketrenos43f66a62005-03-25 12:31:53 -06003243 * DMA services
3244 *
3245 * Theory of operation
3246 *
3247 * A queue is a circular buffers with 'Read' and 'Write' pointers.
3248 * 2 empty entries always kept in the buffer to protect from overflow.
3249 *
3250 * For Tx queue, there are low mark and high mark limits. If, after queuing
Jeff Garzikbf794512005-07-31 13:07:26 -04003251 * the packet for Tx, free space become < low mark, Tx queue stopped. When
3252 * reclaiming packets (on 'tx done IRQ), if free space become > high mark,
James Ketrenos43f66a62005-03-25 12:31:53 -06003253 * Tx queue resumed.
3254 *
3255 * The IPW operates with six queues, one receive queue in the device's
3256 * sram, one transmit queue for sending commands to the device firmware,
Jeff Garzikbf794512005-07-31 13:07:26 -04003257 * and four transmit queues for data.
James Ketrenos43f66a62005-03-25 12:31:53 -06003258 *
Jeff Garzikbf794512005-07-31 13:07:26 -04003259 * The four transmit queues allow for performing quality of service (qos)
James Ketrenos43f66a62005-03-25 12:31:53 -06003260 * transmissions as per the 802.11 protocol. Currently Linux does not
Jeff Garzikbf794512005-07-31 13:07:26 -04003261 * provide a mechanism to the user for utilizing prioritized queues, so
James Ketrenos43f66a62005-03-25 12:31:53 -06003262 * we only utilize the first data transmit queue (queue1).
3263 */
3264
3265/**
3266 * Driver allocates buffers of this size for Rx
3267 */
3268
3269static inline int ipw_queue_space(const struct clx2_queue *q)
3270{
3271 int s = q->last_used - q->first_empty;
3272 if (s <= 0)
3273 s += q->n_bd;
3274 s -= 2; /* keep some reserve to not confuse empty and full situations */
3275 if (s < 0)
3276 s = 0;
3277 return s;
3278}
3279
3280static inline int ipw_queue_inc_wrap(int index, int n_bd)
3281{
3282 return (++index == n_bd) ? 0 : index;
3283}
3284
3285/**
3286 * Initialize common DMA queue structure
Jeff Garzikbf794512005-07-31 13:07:26 -04003287 *
James Ketrenos43f66a62005-03-25 12:31:53 -06003288 * @param q queue to init
3289 * @param count Number of BD's to allocate. Should be power of 2
3290 * @param read_register Address for 'read' register
3291 * (not offset within BAR, full address)
3292 * @param write_register Address for 'write' register
3293 * (not offset within BAR, full address)
3294 * @param base_register Address for 'base' register
3295 * (not offset within BAR, full address)
3296 * @param size Address for 'size' register
3297 * (not offset within BAR, full address)
3298 */
Jeff Garzikbf794512005-07-31 13:07:26 -04003299static void ipw_queue_init(struct ipw_priv *priv, struct clx2_queue *q,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003300 int count, u32 read, u32 write, u32 base, u32 size)
James Ketrenos43f66a62005-03-25 12:31:53 -06003301{
3302 q->n_bd = count;
3303
3304 q->low_mark = q->n_bd / 4;
3305 if (q->low_mark < 4)
3306 q->low_mark = 4;
3307
3308 q->high_mark = q->n_bd / 8;
3309 if (q->high_mark < 2)
3310 q->high_mark = 2;
3311
3312 q->first_empty = q->last_used = 0;
3313 q->reg_r = read;
3314 q->reg_w = write;
3315
3316 ipw_write32(priv, base, q->dma_addr);
3317 ipw_write32(priv, size, count);
3318 ipw_write32(priv, read, 0);
3319 ipw_write32(priv, write, 0);
3320
3321 _ipw_read32(priv, 0x90);
3322}
3323
Jeff Garzikbf794512005-07-31 13:07:26 -04003324static int ipw_queue_tx_init(struct ipw_priv *priv,
James Ketrenos43f66a62005-03-25 12:31:53 -06003325 struct clx2_tx_queue *q,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003326 int count, u32 read, u32 write, u32 base, u32 size)
James Ketrenos43f66a62005-03-25 12:31:53 -06003327{
3328 struct pci_dev *dev = priv->pci_dev;
3329
3330 q->txb = kmalloc(sizeof(q->txb[0]) * count, GFP_KERNEL);
3331 if (!q->txb) {
3332 IPW_ERROR("vmalloc for auxilary BD structures failed\n");
3333 return -ENOMEM;
3334 }
3335
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003336 q->bd =
3337 pci_alloc_consistent(dev, sizeof(q->bd[0]) * count, &q->q.dma_addr);
James Ketrenos43f66a62005-03-25 12:31:53 -06003338 if (!q->bd) {
Jiri Bencaaa4d302005-06-07 14:58:41 +02003339 IPW_ERROR("pci_alloc_consistent(%zd) failed\n",
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003340 sizeof(q->bd[0]) * count);
James Ketrenos43f66a62005-03-25 12:31:53 -06003341 kfree(q->txb);
3342 q->txb = NULL;
3343 return -ENOMEM;
3344 }
3345
3346 ipw_queue_init(priv, &q->q, count, read, write, base, size);
3347 return 0;
3348}
3349
3350/**
3351 * Free one TFD, those at index [txq->q.last_used].
3352 * Do NOT advance any indexes
Jeff Garzikbf794512005-07-31 13:07:26 -04003353 *
James Ketrenos43f66a62005-03-25 12:31:53 -06003354 * @param dev
3355 * @param txq
3356 */
3357static void ipw_queue_tx_free_tfd(struct ipw_priv *priv,
3358 struct clx2_tx_queue *txq)
3359{
3360 struct tfd_frame *bd = &txq->bd[txq->q.last_used];
3361 struct pci_dev *dev = priv->pci_dev;
3362 int i;
Jeff Garzikbf794512005-07-31 13:07:26 -04003363
James Ketrenos43f66a62005-03-25 12:31:53 -06003364 /* classify bd */
3365 if (bd->control_flags.message_type == TX_HOST_COMMAND_TYPE)
3366 /* nothing to cleanup after for host commands */
3367 return;
3368
3369 /* sanity check */
James Ketrenosa613bff2005-08-24 21:43:11 -05003370 if (le32_to_cpu(bd->u.data.num_chunks) > NUM_TFD_CHUNKS) {
3371 IPW_ERROR("Too many chunks: %i\n",
3372 le32_to_cpu(bd->u.data.num_chunks));
James Ketrenos43f66a62005-03-25 12:31:53 -06003373 /** @todo issue fatal error, it is quite serious situation */
3374 return;
3375 }
3376
3377 /* unmap chunks if any */
James Ketrenosa613bff2005-08-24 21:43:11 -05003378 for (i = 0; i < le32_to_cpu(bd->u.data.num_chunks); i++) {
3379 pci_unmap_single(dev, le32_to_cpu(bd->u.data.chunk_ptr[i]),
3380 le16_to_cpu(bd->u.data.chunk_len[i]),
3381 PCI_DMA_TODEVICE);
James Ketrenos43f66a62005-03-25 12:31:53 -06003382 if (txq->txb[txq->q.last_used]) {
3383 ieee80211_txb_free(txq->txb[txq->q.last_used]);
3384 txq->txb[txq->q.last_used] = NULL;
3385 }
3386 }
3387}
3388
3389/**
3390 * Deallocate DMA queue.
Jeff Garzikbf794512005-07-31 13:07:26 -04003391 *
James Ketrenos43f66a62005-03-25 12:31:53 -06003392 * Empty queue by removing and destroying all BD's.
3393 * Free all buffers.
Jeff Garzikbf794512005-07-31 13:07:26 -04003394 *
James Ketrenos43f66a62005-03-25 12:31:53 -06003395 * @param dev
3396 * @param q
3397 */
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003398static void ipw_queue_tx_free(struct ipw_priv *priv, struct clx2_tx_queue *txq)
James Ketrenos43f66a62005-03-25 12:31:53 -06003399{
3400 struct clx2_queue *q = &txq->q;
3401 struct pci_dev *dev = priv->pci_dev;
3402
Jeff Garzikbf794512005-07-31 13:07:26 -04003403 if (q->n_bd == 0)
3404 return;
James Ketrenos43f66a62005-03-25 12:31:53 -06003405
3406 /* first, empty all BD's */
3407 for (; q->first_empty != q->last_used;
3408 q->last_used = ipw_queue_inc_wrap(q->last_used, q->n_bd)) {
3409 ipw_queue_tx_free_tfd(priv, txq);
3410 }
Jeff Garzikbf794512005-07-31 13:07:26 -04003411
James Ketrenos43f66a62005-03-25 12:31:53 -06003412 /* free buffers belonging to queue itself */
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003413 pci_free_consistent(dev, sizeof(txq->bd[0]) * q->n_bd, txq->bd,
James Ketrenos43f66a62005-03-25 12:31:53 -06003414 q->dma_addr);
3415 kfree(txq->txb);
3416
3417 /* 0 fill whole structure */
3418 memset(txq, 0, sizeof(*txq));
3419}
3420
James Ketrenos43f66a62005-03-25 12:31:53 -06003421/**
3422 * Destroy all DMA queues and structures
Jeff Garzikbf794512005-07-31 13:07:26 -04003423 *
James Ketrenos43f66a62005-03-25 12:31:53 -06003424 * @param priv
3425 */
3426static void ipw_tx_queue_free(struct ipw_priv *priv)
3427{
3428 /* Tx CMD queue */
3429 ipw_queue_tx_free(priv, &priv->txq_cmd);
3430
3431 /* Tx queues */
3432 ipw_queue_tx_free(priv, &priv->txq[0]);
3433 ipw_queue_tx_free(priv, &priv->txq[1]);
3434 ipw_queue_tx_free(priv, &priv->txq[2]);
3435 ipw_queue_tx_free(priv, &priv->txq[3]);
3436}
3437
3438static void inline __maybe_wake_tx(struct ipw_priv *priv)
3439{
3440 if (netif_running(priv->net_dev)) {
3441 switch (priv->port_type) {
3442 case DCR_TYPE_MU_BSS:
3443 case DCR_TYPE_MU_IBSS:
James Ketrenosa613bff2005-08-24 21:43:11 -05003444 if (!(priv->status & STATUS_ASSOCIATED))
James Ketrenos43f66a62005-03-25 12:31:53 -06003445 return;
James Ketrenos43f66a62005-03-25 12:31:53 -06003446 }
3447 netif_wake_queue(priv->net_dev);
3448 }
3449
3450}
3451
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003452static inline void ipw_create_bssid(struct ipw_priv *priv, u8 * bssid)
James Ketrenos43f66a62005-03-25 12:31:53 -06003453{
3454 /* First 3 bytes are manufacturer */
3455 bssid[0] = priv->mac_addr[0];
3456 bssid[1] = priv->mac_addr[1];
3457 bssid[2] = priv->mac_addr[2];
3458
3459 /* Last bytes are random */
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003460 get_random_bytes(&bssid[3], ETH_ALEN - 3);
James Ketrenos43f66a62005-03-25 12:31:53 -06003461
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003462 bssid[0] &= 0xfe; /* clear multicast bit */
3463 bssid[0] |= 0x02; /* set local assignment bit (IEEE802) */
James Ketrenos43f66a62005-03-25 12:31:53 -06003464}
3465
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003466static inline u8 ipw_add_station(struct ipw_priv *priv, u8 * bssid)
James Ketrenos43f66a62005-03-25 12:31:53 -06003467{
3468 struct ipw_station_entry entry;
3469 int i;
3470
3471 for (i = 0; i < priv->num_stations; i++) {
3472 if (!memcmp(priv->stations[i], bssid, ETH_ALEN)) {
3473 /* Another node is active in network */
3474 priv->missed_adhoc_beacons = 0;
3475 if (!(priv->config & CFG_STATIC_CHANNEL))
3476 /* when other nodes drop out, we drop out */
3477 priv->config &= ~CFG_ADHOC_PERSIST;
3478
3479 return i;
3480 }
3481 }
3482
3483 if (i == MAX_STATIONS)
3484 return IPW_INVALID_STATION;
3485
3486 IPW_DEBUG_SCAN("Adding AdHoc station: " MAC_FMT "\n", MAC_ARG(bssid));
3487
3488 entry.reserved = 0;
3489 entry.support_mode = 0;
3490 memcpy(entry.mac_addr, bssid, ETH_ALEN);
3491 memcpy(priv->stations[i], bssid, ETH_ALEN);
3492 ipw_write_direct(priv, IPW_STATION_TABLE_LOWER + i * sizeof(entry),
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003493 &entry, sizeof(entry));
James Ketrenos43f66a62005-03-25 12:31:53 -06003494 priv->num_stations++;
3495
3496 return i;
3497}
3498
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003499static inline u8 ipw_find_station(struct ipw_priv *priv, u8 * bssid)
James Ketrenos43f66a62005-03-25 12:31:53 -06003500{
3501 int i;
3502
Jeff Garzikbf794512005-07-31 13:07:26 -04003503 for (i = 0; i < priv->num_stations; i++)
3504 if (!memcmp(priv->stations[i], bssid, ETH_ALEN))
James Ketrenos43f66a62005-03-25 12:31:53 -06003505 return i;
3506
3507 return IPW_INVALID_STATION;
3508}
3509
3510static void ipw_send_disassociate(struct ipw_priv *priv, int quiet)
3511{
3512 int err;
3513
3514 if (!(priv->status & (STATUS_ASSOCIATING | STATUS_ASSOCIATED))) {
3515 IPW_DEBUG_ASSOC("Disassociating while not associated.\n");
3516 return;
3517 }
3518
3519 IPW_DEBUG_ASSOC("Disassocation attempt from " MAC_FMT " "
3520 "on channel %d.\n",
Jeff Garzikbf794512005-07-31 13:07:26 -04003521 MAC_ARG(priv->assoc_request.bssid),
James Ketrenos43f66a62005-03-25 12:31:53 -06003522 priv->assoc_request.channel);
3523
3524 priv->status &= ~(STATUS_ASSOCIATING | STATUS_ASSOCIATED);
3525 priv->status |= STATUS_DISASSOCIATING;
3526
3527 if (quiet)
3528 priv->assoc_request.assoc_type = HC_DISASSOC_QUIET;
3529 else
3530 priv->assoc_request.assoc_type = HC_DISASSOCIATE;
3531 err = ipw_send_associate(priv, &priv->assoc_request);
3532 if (err) {
3533 IPW_DEBUG_HC("Attempt to send [dis]associate command "
3534 "failed.\n");
3535 return;
3536 }
3537
3538}
3539
James Ketrenosc848d0a2005-08-24 21:56:24 -05003540static int ipw_disassociate(void *data)
James Ketrenos43f66a62005-03-25 12:31:53 -06003541{
James Ketrenosc848d0a2005-08-24 21:56:24 -05003542 struct ipw_priv *priv = data;
3543 if (!(priv->status & (STATUS_ASSOCIATED | STATUS_ASSOCIATING)))
3544 return 0;
James Ketrenos43f66a62005-03-25 12:31:53 -06003545 ipw_send_disassociate(data, 0);
James Ketrenosc848d0a2005-08-24 21:56:24 -05003546 return 1;
3547}
3548
3549static void ipw_bg_disassociate(void *data)
3550{
3551 struct ipw_priv *priv = data;
3552 down(&priv->sem);
3553 ipw_disassociate(data);
3554 up(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06003555}
3556
James Ketrenos43f66a62005-03-25 12:31:53 -06003557struct ipw_status_code {
3558 u16 status;
3559 const char *reason;
3560};
3561
3562static const struct ipw_status_code ipw_status_codes[] = {
3563 {0x00, "Successful"},
3564 {0x01, "Unspecified failure"},
3565 {0x0A, "Cannot support all requested capabilities in the "
3566 "Capability information field"},
3567 {0x0B, "Reassociation denied due to inability to confirm that "
3568 "association exists"},
3569 {0x0C, "Association denied due to reason outside the scope of this "
3570 "standard"},
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003571 {0x0D,
3572 "Responding station does not support the specified authentication "
James Ketrenos43f66a62005-03-25 12:31:53 -06003573 "algorithm"},
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003574 {0x0E,
3575 "Received an Authentication frame with authentication sequence "
James Ketrenos43f66a62005-03-25 12:31:53 -06003576 "transaction sequence number out of expected sequence"},
3577 {0x0F, "Authentication rejected because of challenge failure"},
3578 {0x10, "Authentication rejected due to timeout waiting for next "
3579 "frame in sequence"},
3580 {0x11, "Association denied because AP is unable to handle additional "
3581 "associated stations"},
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003582 {0x12,
3583 "Association denied due to requesting station not supporting all "
James Ketrenos43f66a62005-03-25 12:31:53 -06003584 "of the datarates in the BSSBasicServiceSet Parameter"},
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003585 {0x13,
3586 "Association denied due to requesting station not supporting "
James Ketrenos43f66a62005-03-25 12:31:53 -06003587 "short preamble operation"},
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003588 {0x14,
3589 "Association denied due to requesting station not supporting "
James Ketrenos43f66a62005-03-25 12:31:53 -06003590 "PBCC encoding"},
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003591 {0x15,
3592 "Association denied due to requesting station not supporting "
James Ketrenos43f66a62005-03-25 12:31:53 -06003593 "channel agility"},
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003594 {0x19,
3595 "Association denied due to requesting station not supporting "
James Ketrenos43f66a62005-03-25 12:31:53 -06003596 "short slot operation"},
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003597 {0x1A,
3598 "Association denied due to requesting station not supporting "
James Ketrenos43f66a62005-03-25 12:31:53 -06003599 "DSSS-OFDM operation"},
3600 {0x28, "Invalid Information Element"},
3601 {0x29, "Group Cipher is not valid"},
3602 {0x2A, "Pairwise Cipher is not valid"},
3603 {0x2B, "AKMP is not valid"},
3604 {0x2C, "Unsupported RSN IE version"},
3605 {0x2D, "Invalid RSN IE Capabilities"},
3606 {0x2E, "Cipher suite is rejected per security policy"},
3607};
3608
3609#ifdef CONFIG_IPW_DEBUG
Jeff Garzikbf794512005-07-31 13:07:26 -04003610static const char *ipw_get_status_code(u16 status)
James Ketrenos43f66a62005-03-25 12:31:53 -06003611{
3612 int i;
Jeff Garzikbf794512005-07-31 13:07:26 -04003613 for (i = 0; i < ARRAY_SIZE(ipw_status_codes); i++)
James Ketrenosea2b26e2005-08-24 21:25:16 -05003614 if (ipw_status_codes[i].status == (status & 0xff))
James Ketrenos43f66a62005-03-25 12:31:53 -06003615 return ipw_status_codes[i].reason;
3616 return "Unknown status value.";
3617}
3618#endif
3619
3620static void inline average_init(struct average *avg)
3621{
3622 memset(avg, 0, sizeof(*avg));
3623}
3624
3625static void inline average_add(struct average *avg, s16 val)
3626{
3627 avg->sum -= avg->entries[avg->pos];
3628 avg->sum += val;
3629 avg->entries[avg->pos++] = val;
3630 if (unlikely(avg->pos == AVG_ENTRIES)) {
3631 avg->init = 1;
3632 avg->pos = 0;
3633 }
3634}
3635
3636static s16 inline average_value(struct average *avg)
3637{
3638 if (!unlikely(avg->init)) {
3639 if (avg->pos)
3640 return avg->sum / avg->pos;
3641 return 0;
3642 }
3643
3644 return avg->sum / AVG_ENTRIES;
3645}
3646
3647static void ipw_reset_stats(struct ipw_priv *priv)
3648{
3649 u32 len = sizeof(u32);
3650
3651 priv->quality = 0;
3652
3653 average_init(&priv->average_missed_beacons);
3654 average_init(&priv->average_rssi);
3655 average_init(&priv->average_noise);
3656
3657 priv->last_rate = 0;
3658 priv->last_missed_beacons = 0;
3659 priv->last_rx_packets = 0;
3660 priv->last_tx_packets = 0;
3661 priv->last_tx_failures = 0;
Jeff Garzikbf794512005-07-31 13:07:26 -04003662
James Ketrenos43f66a62005-03-25 12:31:53 -06003663 /* Firmware managed, reset only when NIC is restarted, so we have to
3664 * normalize on the current value */
Jeff Garzikbf794512005-07-31 13:07:26 -04003665 ipw_get_ordinal(priv, IPW_ORD_STAT_RX_ERR_CRC,
James Ketrenos43f66a62005-03-25 12:31:53 -06003666 &priv->last_rx_err, &len);
Jeff Garzikbf794512005-07-31 13:07:26 -04003667 ipw_get_ordinal(priv, IPW_ORD_STAT_TX_FAILURE,
James Ketrenos43f66a62005-03-25 12:31:53 -06003668 &priv->last_tx_failures, &len);
3669
3670 /* Driver managed, reset with each association */
3671 priv->missed_adhoc_beacons = 0;
3672 priv->missed_beacons = 0;
3673 priv->tx_packets = 0;
3674 priv->rx_packets = 0;
3675
3676}
3677
James Ketrenos43f66a62005-03-25 12:31:53 -06003678static inline u32 ipw_get_max_rate(struct ipw_priv *priv)
3679{
3680 u32 i = 0x80000000;
3681 u32 mask = priv->rates_mask;
3682 /* If currently associated in B mode, restrict the maximum
3683 * rate match to B rates */
3684 if (priv->assoc_request.ieee_mode == IPW_B_MODE)
3685 mask &= IEEE80211_CCK_RATES_MASK;
3686
3687 /* TODO: Verify that the rate is supported by the current rates
3688 * list. */
3689
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003690 while (i && !(mask & i))
3691 i >>= 1;
James Ketrenos43f66a62005-03-25 12:31:53 -06003692 switch (i) {
James Ketrenosea2b26e2005-08-24 21:25:16 -05003693 case IEEE80211_CCK_RATE_1MB_MASK:
3694 return 1000000;
3695 case IEEE80211_CCK_RATE_2MB_MASK:
3696 return 2000000;
3697 case IEEE80211_CCK_RATE_5MB_MASK:
3698 return 5500000;
3699 case IEEE80211_OFDM_RATE_6MB_MASK:
3700 return 6000000;
3701 case IEEE80211_OFDM_RATE_9MB_MASK:
3702 return 9000000;
3703 case IEEE80211_CCK_RATE_11MB_MASK:
3704 return 11000000;
3705 case IEEE80211_OFDM_RATE_12MB_MASK:
3706 return 12000000;
3707 case IEEE80211_OFDM_RATE_18MB_MASK:
3708 return 18000000;
3709 case IEEE80211_OFDM_RATE_24MB_MASK:
3710 return 24000000;
3711 case IEEE80211_OFDM_RATE_36MB_MASK:
3712 return 36000000;
3713 case IEEE80211_OFDM_RATE_48MB_MASK:
3714 return 48000000;
3715 case IEEE80211_OFDM_RATE_54MB_MASK:
3716 return 54000000;
James Ketrenos43f66a62005-03-25 12:31:53 -06003717 }
3718
Jeff Garzikbf794512005-07-31 13:07:26 -04003719 if (priv->ieee->mode == IEEE_B)
James Ketrenos43f66a62005-03-25 12:31:53 -06003720 return 11000000;
3721 else
3722 return 54000000;
3723}
3724
3725static u32 ipw_get_current_rate(struct ipw_priv *priv)
3726{
3727 u32 rate, len = sizeof(rate);
3728 int err;
3729
Jeff Garzikbf794512005-07-31 13:07:26 -04003730 if (!(priv->status & STATUS_ASSOCIATED))
James Ketrenos43f66a62005-03-25 12:31:53 -06003731 return 0;
3732
3733 if (priv->tx_packets > IPW_REAL_RATE_RX_PACKET_THRESHOLD) {
Jeff Garzikbf794512005-07-31 13:07:26 -04003734 err = ipw_get_ordinal(priv, IPW_ORD_STAT_TX_CURR_RATE, &rate,
James Ketrenos43f66a62005-03-25 12:31:53 -06003735 &len);
3736 if (err) {
3737 IPW_DEBUG_INFO("failed querying ordinals.\n");
3738 return 0;
3739 }
Jeff Garzikbf794512005-07-31 13:07:26 -04003740 } else
James Ketrenos43f66a62005-03-25 12:31:53 -06003741 return ipw_get_max_rate(priv);
3742
3743 switch (rate) {
James Ketrenosea2b26e2005-08-24 21:25:16 -05003744 case IPW_TX_RATE_1MB:
3745 return 1000000;
3746 case IPW_TX_RATE_2MB:
3747 return 2000000;
3748 case IPW_TX_RATE_5MB:
3749 return 5500000;
3750 case IPW_TX_RATE_6MB:
3751 return 6000000;
3752 case IPW_TX_RATE_9MB:
3753 return 9000000;
3754 case IPW_TX_RATE_11MB:
3755 return 11000000;
3756 case IPW_TX_RATE_12MB:
3757 return 12000000;
3758 case IPW_TX_RATE_18MB:
3759 return 18000000;
3760 case IPW_TX_RATE_24MB:
3761 return 24000000;
3762 case IPW_TX_RATE_36MB:
3763 return 36000000;
3764 case IPW_TX_RATE_48MB:
3765 return 48000000;
3766 case IPW_TX_RATE_54MB:
3767 return 54000000;
James Ketrenos43f66a62005-03-25 12:31:53 -06003768 }
3769
3770 return 0;
3771}
3772
James Ketrenos43f66a62005-03-25 12:31:53 -06003773#define IPW_STATS_INTERVAL (2 * HZ)
3774static void ipw_gather_stats(struct ipw_priv *priv)
3775{
3776 u32 rx_err, rx_err_delta, rx_packets_delta;
3777 u32 tx_failures, tx_failures_delta, tx_packets_delta;
3778 u32 missed_beacons_percent, missed_beacons_delta;
3779 u32 quality = 0;
3780 u32 len = sizeof(u32);
3781 s16 rssi;
Jeff Garzikbf794512005-07-31 13:07:26 -04003782 u32 beacon_quality, signal_quality, tx_quality, rx_quality,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003783 rate_quality;
James Ketrenosea2b26e2005-08-24 21:25:16 -05003784 u32 max_rate;
James Ketrenos43f66a62005-03-25 12:31:53 -06003785
3786 if (!(priv->status & STATUS_ASSOCIATED)) {
3787 priv->quality = 0;
3788 return;
3789 }
3790
3791 /* Update the statistics */
Jeff Garzikbf794512005-07-31 13:07:26 -04003792 ipw_get_ordinal(priv, IPW_ORD_STAT_MISSED_BEACONS,
James Ketrenos43f66a62005-03-25 12:31:53 -06003793 &priv->missed_beacons, &len);
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003794 missed_beacons_delta = priv->missed_beacons - priv->last_missed_beacons;
James Ketrenos43f66a62005-03-25 12:31:53 -06003795 priv->last_missed_beacons = priv->missed_beacons;
3796 if (priv->assoc_request.beacon_interval) {
3797 missed_beacons_percent = missed_beacons_delta *
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003798 (HZ * priv->assoc_request.beacon_interval) /
3799 (IPW_STATS_INTERVAL * 10);
James Ketrenos43f66a62005-03-25 12:31:53 -06003800 } else {
3801 missed_beacons_percent = 0;
3802 }
3803 average_add(&priv->average_missed_beacons, missed_beacons_percent);
3804
3805 ipw_get_ordinal(priv, IPW_ORD_STAT_RX_ERR_CRC, &rx_err, &len);
3806 rx_err_delta = rx_err - priv->last_rx_err;
3807 priv->last_rx_err = rx_err;
3808
3809 ipw_get_ordinal(priv, IPW_ORD_STAT_TX_FAILURE, &tx_failures, &len);
3810 tx_failures_delta = tx_failures - priv->last_tx_failures;
3811 priv->last_tx_failures = tx_failures;
3812
3813 rx_packets_delta = priv->rx_packets - priv->last_rx_packets;
3814 priv->last_rx_packets = priv->rx_packets;
3815
3816 tx_packets_delta = priv->tx_packets - priv->last_tx_packets;
3817 priv->last_tx_packets = priv->tx_packets;
3818
3819 /* Calculate quality based on the following:
Jeff Garzikbf794512005-07-31 13:07:26 -04003820 *
James Ketrenos43f66a62005-03-25 12:31:53 -06003821 * Missed beacon: 100% = 0, 0% = 70% missed
3822 * Rate: 60% = 1Mbs, 100% = Max
3823 * Rx and Tx errors represent a straight % of total Rx/Tx
3824 * RSSI: 100% = > -50, 0% = < -80
3825 * Rx errors: 100% = 0, 0% = 50% missed
Jeff Garzikbf794512005-07-31 13:07:26 -04003826 *
James Ketrenos43f66a62005-03-25 12:31:53 -06003827 * The lowest computed quality is used.
3828 *
3829 */
3830#define BEACON_THRESHOLD 5
3831 beacon_quality = 100 - missed_beacons_percent;
3832 if (beacon_quality < BEACON_THRESHOLD)
3833 beacon_quality = 0;
3834 else
Jeff Garzikbf794512005-07-31 13:07:26 -04003835 beacon_quality = (beacon_quality - BEACON_THRESHOLD) * 100 /
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003836 (100 - BEACON_THRESHOLD);
Jeff Garzikbf794512005-07-31 13:07:26 -04003837 IPW_DEBUG_STATS("Missed beacon: %3d%% (%d%%)\n",
James Ketrenos43f66a62005-03-25 12:31:53 -06003838 beacon_quality, missed_beacons_percent);
Jeff Garzikbf794512005-07-31 13:07:26 -04003839
James Ketrenos43f66a62005-03-25 12:31:53 -06003840 priv->last_rate = ipw_get_current_rate(priv);
James Ketrenosea2b26e2005-08-24 21:25:16 -05003841 max_rate = ipw_get_max_rate(priv);
3842 rate_quality = priv->last_rate * 40 / max_rate + 60;
James Ketrenos43f66a62005-03-25 12:31:53 -06003843 IPW_DEBUG_STATS("Rate quality : %3d%% (%dMbs)\n",
3844 rate_quality, priv->last_rate / 1000000);
Jeff Garzikbf794512005-07-31 13:07:26 -04003845
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003846 if (rx_packets_delta > 100 && rx_packets_delta + rx_err_delta)
Jeff Garzikbf794512005-07-31 13:07:26 -04003847 rx_quality = 100 - (rx_err_delta * 100) /
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003848 (rx_packets_delta + rx_err_delta);
James Ketrenos43f66a62005-03-25 12:31:53 -06003849 else
3850 rx_quality = 100;
3851 IPW_DEBUG_STATS("Rx quality : %3d%% (%u errors, %u packets)\n",
3852 rx_quality, rx_err_delta, rx_packets_delta);
Jeff Garzikbf794512005-07-31 13:07:26 -04003853
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003854 if (tx_packets_delta > 100 && tx_packets_delta + tx_failures_delta)
Jeff Garzikbf794512005-07-31 13:07:26 -04003855 tx_quality = 100 - (tx_failures_delta * 100) /
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003856 (tx_packets_delta + tx_failures_delta);
James Ketrenos43f66a62005-03-25 12:31:53 -06003857 else
3858 tx_quality = 100;
3859 IPW_DEBUG_STATS("Tx quality : %3d%% (%u errors, %u packets)\n",
3860 tx_quality, tx_failures_delta, tx_packets_delta);
Jeff Garzikbf794512005-07-31 13:07:26 -04003861
James Ketrenos43f66a62005-03-25 12:31:53 -06003862 rssi = average_value(&priv->average_rssi);
James Ketrenosc848d0a2005-08-24 21:56:24 -05003863 signal_quality =
3864 (100 *
3865 (priv->ieee->perfect_rssi - priv->ieee->worst_rssi) *
3866 (priv->ieee->perfect_rssi - priv->ieee->worst_rssi) -
3867 (priv->ieee->perfect_rssi - rssi) *
3868 (15 * (priv->ieee->perfect_rssi - priv->ieee->worst_rssi) +
3869 62 * (priv->ieee->perfect_rssi - rssi))) /
3870 ((priv->ieee->perfect_rssi - priv->ieee->worst_rssi) *
3871 (priv->ieee->perfect_rssi - priv->ieee->worst_rssi));
3872 if (signal_quality > 100)
James Ketrenos43f66a62005-03-25 12:31:53 -06003873 signal_quality = 100;
James Ketrenosc848d0a2005-08-24 21:56:24 -05003874 else if (signal_quality < 1)
James Ketrenos43f66a62005-03-25 12:31:53 -06003875 signal_quality = 0;
James Ketrenosea2b26e2005-08-24 21:25:16 -05003876
James Ketrenos43f66a62005-03-25 12:31:53 -06003877 IPW_DEBUG_STATS("Signal level : %3d%% (%d dBm)\n",
3878 signal_quality, rssi);
Jeff Garzikbf794512005-07-31 13:07:26 -04003879
3880 quality = min(beacon_quality,
James Ketrenos43f66a62005-03-25 12:31:53 -06003881 min(rate_quality,
3882 min(tx_quality, min(rx_quality, signal_quality))));
3883 if (quality == beacon_quality)
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003884 IPW_DEBUG_STATS("Quality (%d%%): Clamped to missed beacons.\n",
3885 quality);
James Ketrenos43f66a62005-03-25 12:31:53 -06003886 if (quality == rate_quality)
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003887 IPW_DEBUG_STATS("Quality (%d%%): Clamped to rate quality.\n",
3888 quality);
James Ketrenos43f66a62005-03-25 12:31:53 -06003889 if (quality == tx_quality)
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003890 IPW_DEBUG_STATS("Quality (%d%%): Clamped to Tx quality.\n",
3891 quality);
James Ketrenos43f66a62005-03-25 12:31:53 -06003892 if (quality == rx_quality)
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003893 IPW_DEBUG_STATS("Quality (%d%%): Clamped to Rx quality.\n",
3894 quality);
James Ketrenos43f66a62005-03-25 12:31:53 -06003895 if (quality == signal_quality)
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003896 IPW_DEBUG_STATS("Quality (%d%%): Clamped to signal quality.\n",
3897 quality);
James Ketrenos43f66a62005-03-25 12:31:53 -06003898
3899 priv->quality = quality;
Jeff Garzikbf794512005-07-31 13:07:26 -04003900
3901 queue_delayed_work(priv->workqueue, &priv->gather_stats,
James Ketrenos43f66a62005-03-25 12:31:53 -06003902 IPW_STATS_INTERVAL);
3903}
3904
James Ketrenosc848d0a2005-08-24 21:56:24 -05003905static void ipw_bg_gather_stats(void *data)
3906{
3907 struct ipw_priv *priv = data;
3908 down(&priv->sem);
3909 ipw_gather_stats(data);
3910 up(&priv->sem);
3911}
3912
James Ketrenosea2b26e2005-08-24 21:25:16 -05003913static inline void ipw_handle_missed_beacon(struct ipw_priv *priv,
3914 int missed_count)
3915{
3916 priv->notif_missed_beacons = missed_count;
3917
3918 if (missed_count > priv->missed_beacon_threshold &&
3919 priv->status & STATUS_ASSOCIATED) {
3920 /* If associated and we've hit the missed
3921 * beacon threshold, disassociate, turn
3922 * off roaming, and abort any active scans */
3923 IPW_DEBUG(IPW_DL_INFO | IPW_DL_NOTIF |
3924 IPW_DL_STATE,
3925 "Missed beacon: %d - disassociate\n", missed_count);
3926 priv->status &= ~STATUS_ROAMING;
James Ketrenosa613bff2005-08-24 21:43:11 -05003927 if (priv->status & STATUS_SCANNING) {
3928 IPW_DEBUG(IPW_DL_INFO | IPW_DL_NOTIF |
3929 IPW_DL_STATE,
3930 "Aborting scan with missed beacon.\n");
James Ketrenosea2b26e2005-08-24 21:25:16 -05003931 queue_work(priv->workqueue, &priv->abort_scan);
James Ketrenosa613bff2005-08-24 21:43:11 -05003932 }
3933
James Ketrenosea2b26e2005-08-24 21:25:16 -05003934 queue_work(priv->workqueue, &priv->disassociate);
3935 return;
3936 }
3937
3938 if (priv->status & STATUS_ROAMING) {
3939 /* If we are currently roaming, then just
3940 * print a debug statement... */
3941 IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE,
3942 "Missed beacon: %d - roam in progress\n",
3943 missed_count);
3944 return;
3945 }
3946
3947 if (missed_count > priv->roaming_threshold) {
3948 /* If we are not already roaming, set the ROAM
3949 * bit in the status and kick off a scan */
3950 IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE,
3951 "Missed beacon: %d - initiate "
3952 "roaming\n", missed_count);
3953 if (!(priv->status & STATUS_ROAMING)) {
3954 priv->status |= STATUS_ROAMING;
3955 if (!(priv->status & STATUS_SCANNING))
3956 queue_work(priv->workqueue,
3957 &priv->request_scan);
3958 }
3959 return;
3960 }
3961
3962 if (priv->status & STATUS_SCANNING) {
3963 /* Stop scan to keep fw from getting
3964 * stuck (only if we aren't roaming --
3965 * otherwise we'll never scan more than 2 or 3
3966 * channels..) */
James Ketrenosb095c382005-08-24 22:04:42 -05003967 IPW_DEBUG(IPW_DL_INFO | IPW_DL_NOTIF | IPW_DL_STATE,
3968 "Aborting scan with missed beacon.\n");
James Ketrenosea2b26e2005-08-24 21:25:16 -05003969 queue_work(priv->workqueue, &priv->abort_scan);
3970 }
3971
3972 IPW_DEBUG_NOTIF("Missed beacon: %d\n", missed_count);
3973
3974}
3975
James Ketrenos43f66a62005-03-25 12:31:53 -06003976/**
3977 * Handle host notification packet.
3978 * Called from interrupt routine
3979 */
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003980static inline void ipw_rx_notification(struct ipw_priv *priv,
James Ketrenos43f66a62005-03-25 12:31:53 -06003981 struct ipw_rx_notification *notif)
3982{
James Ketrenosa613bff2005-08-24 21:43:11 -05003983 notif->size = le16_to_cpu(notif->size);
3984
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003985 IPW_DEBUG_NOTIF("type = %i (%d bytes)\n", notif->subtype, notif->size);
Jeff Garzikbf794512005-07-31 13:07:26 -04003986
James Ketrenos43f66a62005-03-25 12:31:53 -06003987 switch (notif->subtype) {
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003988 case HOST_NOTIFICATION_STATUS_ASSOCIATED:{
3989 struct notif_association *assoc = &notif->u.assoc;
Jeff Garzikbf794512005-07-31 13:07:26 -04003990
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003991 switch (assoc->state) {
3992 case CMAS_ASSOCIATED:{
3993 IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE |
3994 IPW_DL_ASSOC,
3995 "associated: '%s' " MAC_FMT
3996 " \n",
3997 escape_essid(priv->essid,
3998 priv->essid_len),
3999 MAC_ARG(priv->bssid));
Jeff Garzikbf794512005-07-31 13:07:26 -04004000
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004001 switch (priv->ieee->iw_mode) {
4002 case IW_MODE_INFRA:
4003 memcpy(priv->ieee->bssid,
4004 priv->bssid, ETH_ALEN);
4005 break;
James Ketrenos43f66a62005-03-25 12:31:53 -06004006
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004007 case IW_MODE_ADHOC:
4008 memcpy(priv->ieee->bssid,
4009 priv->bssid, ETH_ALEN);
Jeff Garzikbf794512005-07-31 13:07:26 -04004010
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004011 /* clear out the station table */
4012 priv->num_stations = 0;
James Ketrenos43f66a62005-03-25 12:31:53 -06004013
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004014 IPW_DEBUG_ASSOC
4015 ("queueing adhoc check\n");
4016 queue_delayed_work(priv->
4017 workqueue,
4018 &priv->
4019 adhoc_check,
4020 priv->
4021 assoc_request.
4022 beacon_interval);
4023 break;
4024 }
James Ketrenos43f66a62005-03-25 12:31:53 -06004025
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004026 priv->status &= ~STATUS_ASSOCIATING;
4027 priv->status |= STATUS_ASSOCIATED;
James Ketrenos43f66a62005-03-25 12:31:53 -06004028
James Ketrenosb095c382005-08-24 22:04:42 -05004029#ifdef CONFIG_IPW_QOS
4030 if (priv->status & STATUS_AUTH) {
4031 if ((sizeof
4032 (struct
4033 ieee80211_assoc_response_frame)
4034 <= notif->size)
4035 && (notif->size <= 2314)) {
4036 struct
4037 ieee80211_rx_stats
4038 stats = {
4039 .len =
4040 notif->
4041 size - 1,
4042 };
4043
4044 IPW_DEBUG_QOS
4045 ("QoS Associate "
4046 "size %d\n",
4047 notif->size);
4048 ieee80211_rx_mgt(priv->
4049 ieee,
4050 (struct
4051 ieee80211_hdr
4052 *)
4053 &notif->u.raw, &stats);
4054 }
4055 }
4056#endif
4057
James Ketrenosa613bff2005-08-24 21:43:11 -05004058 schedule_work(&priv->link_up);
James Ketrenos43f66a62005-03-25 12:31:53 -06004059
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004060 break;
4061 }
4062
4063 case CMAS_AUTHENTICATED:{
4064 if (priv->
4065 status & (STATUS_ASSOCIATED |
4066 STATUS_AUTH)) {
4067#ifdef CONFIG_IPW_DEBUG
4068 struct notif_authenticate *auth
4069 = &notif->u.auth;
4070 IPW_DEBUG(IPW_DL_NOTIF |
4071 IPW_DL_STATE |
4072 IPW_DL_ASSOC,
4073 "deauthenticated: '%s' "
4074 MAC_FMT
4075 ": (0x%04X) - %s \n",
4076 escape_essid(priv->
4077 essid,
4078 priv->
4079 essid_len),
4080 MAC_ARG(priv->bssid),
4081 ntohs(auth->status),
4082 ipw_get_status_code
4083 (ntohs
4084 (auth->status)));
4085#endif
4086
4087 priv->status &=
4088 ~(STATUS_ASSOCIATING |
4089 STATUS_AUTH |
4090 STATUS_ASSOCIATED);
4091
James Ketrenosa613bff2005-08-24 21:43:11 -05004092 schedule_work(&priv->link_down);
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004093 break;
4094 }
4095
4096 IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE |
4097 IPW_DL_ASSOC,
4098 "authenticated: '%s' " MAC_FMT
4099 "\n",
4100 escape_essid(priv->essid,
4101 priv->essid_len),
4102 MAC_ARG(priv->bssid));
4103 break;
4104 }
4105
4106 case CMAS_INIT:{
James Ketrenosea2b26e2005-08-24 21:25:16 -05004107 if (priv->status & STATUS_AUTH) {
4108 struct
4109 ieee80211_assoc_response
4110 *resp;
4111 resp =
4112 (struct
4113 ieee80211_assoc_response
4114 *)&notif->u.raw;
4115 IPW_DEBUG(IPW_DL_NOTIF |
4116 IPW_DL_STATE |
4117 IPW_DL_ASSOC,
4118 "association failed (0x%04X): %s\n",
4119 ntohs(resp->status),
4120 ipw_get_status_code
4121 (ntohs
4122 (resp->status)));
4123 }
4124
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004125 IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE |
4126 IPW_DL_ASSOC,
4127 "disassociated: '%s' " MAC_FMT
4128 " \n",
4129 escape_essid(priv->essid,
4130 priv->essid_len),
4131 MAC_ARG(priv->bssid));
4132
4133 priv->status &=
4134 ~(STATUS_DISASSOCIATING |
4135 STATUS_ASSOCIATING |
4136 STATUS_ASSOCIATED | STATUS_AUTH);
James Ketrenosb095c382005-08-24 22:04:42 -05004137 if (priv->assoc_network
4138 && (priv->assoc_network->
4139 capability &
4140 WLAN_CAPABILITY_IBSS))
4141 ipw_remove_current_network
4142 (priv);
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004143
James Ketrenosa613bff2005-08-24 21:43:11 -05004144 schedule_work(&priv->link_down);
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004145
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004146 break;
4147 }
4148
James Ketrenosb095c382005-08-24 22:04:42 -05004149 case CMAS_RX_ASSOC_RESP:
4150 break;
4151
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004152 default:
4153 IPW_ERROR("assoc: unknown (%d)\n",
4154 assoc->state);
4155 break;
4156 }
4157
James Ketrenos43f66a62005-03-25 12:31:53 -06004158 break;
4159 }
Jeff Garzikbf794512005-07-31 13:07:26 -04004160
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004161 case HOST_NOTIFICATION_STATUS_AUTHENTICATE:{
4162 struct notif_authenticate *auth = &notif->u.auth;
4163 switch (auth->state) {
4164 case CMAS_AUTHENTICATED:
4165 IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE,
4166 "authenticated: '%s' " MAC_FMT " \n",
4167 escape_essid(priv->essid,
4168 priv->essid_len),
4169 MAC_ARG(priv->bssid));
4170 priv->status |= STATUS_AUTH;
4171 break;
4172
4173 case CMAS_INIT:
4174 if (priv->status & STATUS_AUTH) {
4175 IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE |
4176 IPW_DL_ASSOC,
4177 "authentication failed (0x%04X): %s\n",
4178 ntohs(auth->status),
4179 ipw_get_status_code(ntohs
4180 (auth->
4181 status)));
4182 }
4183 IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE |
4184 IPW_DL_ASSOC,
4185 "deauthenticated: '%s' " MAC_FMT "\n",
4186 escape_essid(priv->essid,
4187 priv->essid_len),
4188 MAC_ARG(priv->bssid));
James Ketrenos43f66a62005-03-25 12:31:53 -06004189
4190 priv->status &= ~(STATUS_ASSOCIATING |
4191 STATUS_AUTH |
4192 STATUS_ASSOCIATED);
4193
James Ketrenosa613bff2005-08-24 21:43:11 -05004194 schedule_work(&priv->link_down);
James Ketrenos43f66a62005-03-25 12:31:53 -06004195 break;
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004196
4197 case CMAS_TX_AUTH_SEQ_1:
4198 IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE |
4199 IPW_DL_ASSOC, "AUTH_SEQ_1\n");
4200 break;
4201 case CMAS_RX_AUTH_SEQ_2:
4202 IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE |
4203 IPW_DL_ASSOC, "AUTH_SEQ_2\n");
4204 break;
4205 case CMAS_AUTH_SEQ_1_PASS:
4206 IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE |
4207 IPW_DL_ASSOC, "AUTH_SEQ_1_PASS\n");
4208 break;
4209 case CMAS_AUTH_SEQ_1_FAIL:
4210 IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE |
4211 IPW_DL_ASSOC, "AUTH_SEQ_1_FAIL\n");
4212 break;
4213 case CMAS_TX_AUTH_SEQ_3:
4214 IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE |
4215 IPW_DL_ASSOC, "AUTH_SEQ_3\n");
4216 break;
4217 case CMAS_RX_AUTH_SEQ_4:
4218 IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE |
4219 IPW_DL_ASSOC, "RX_AUTH_SEQ_4\n");
4220 break;
4221 case CMAS_AUTH_SEQ_2_PASS:
4222 IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE |
4223 IPW_DL_ASSOC, "AUTH_SEQ_2_PASS\n");
4224 break;
4225 case CMAS_AUTH_SEQ_2_FAIL:
4226 IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE |
4227 IPW_DL_ASSOC, "AUT_SEQ_2_FAIL\n");
4228 break;
4229 case CMAS_TX_ASSOC:
4230 IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE |
4231 IPW_DL_ASSOC, "TX_ASSOC\n");
4232 break;
4233 case CMAS_RX_ASSOC_RESP:
4234 IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE |
4235 IPW_DL_ASSOC, "RX_ASSOC_RESP\n");
James Ketrenosb095c382005-08-24 22:04:42 -05004236
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004237 break;
4238 case CMAS_ASSOCIATED:
4239 IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE |
4240 IPW_DL_ASSOC, "ASSOCIATED\n");
4241 break;
4242 default:
4243 IPW_DEBUG_NOTIF("auth: failure - %d\n",
4244 auth->state);
4245 break;
4246 }
4247 break;
4248 }
4249
4250 case HOST_NOTIFICATION_STATUS_SCAN_CHANNEL_RESULT:{
4251 struct notif_channel_result *x =
4252 &notif->u.channel_result;
4253
4254 if (notif->size == sizeof(*x)) {
4255 IPW_DEBUG_SCAN("Scan result for channel %d\n",
4256 x->channel_num);
4257 } else {
4258 IPW_DEBUG_SCAN("Scan result of wrong size %d "
4259 "(should be %zd)\n",
4260 notif->size, sizeof(*x));
4261 }
4262 break;
4263 }
4264
4265 case HOST_NOTIFICATION_STATUS_SCAN_COMPLETED:{
4266 struct notif_scan_complete *x = &notif->u.scan_complete;
4267 if (notif->size == sizeof(*x)) {
4268 IPW_DEBUG_SCAN
4269 ("Scan completed: type %d, %d channels, "
4270 "%d status\n", x->scan_type,
4271 x->num_channels, x->status);
4272 } else {
4273 IPW_ERROR("Scan completed of wrong size %d "
4274 "(should be %zd)\n",
4275 notif->size, sizeof(*x));
4276 }
4277
4278 priv->status &=
4279 ~(STATUS_SCANNING | STATUS_SCAN_ABORTING);
4280
4281 cancel_delayed_work(&priv->scan_check);
4282
James Ketrenosb095c382005-08-24 22:04:42 -05004283 if (priv->status & STATUS_EXIT_PENDING)
4284 break;
4285
4286 priv->ieee->scans++;
4287
4288#ifdef CONFIG_IPW2200_MONITOR
4289 if (priv->ieee->iw_mode == IW_MODE_MONITOR) {
4290 queue_work(priv->workqueue,
4291 &priv->request_scan);
4292 break;
4293 }
4294#endif /* CONFIG_IPW2200_MONITOR */
4295
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004296 if (!(priv->status & (STATUS_ASSOCIATED |
4297 STATUS_ASSOCIATING |
4298 STATUS_ROAMING |
4299 STATUS_DISASSOCIATING)))
4300 queue_work(priv->workqueue, &priv->associate);
4301 else if (priv->status & STATUS_ROAMING) {
4302 /* If a scan completed and we are in roam mode, then
4303 * the scan that completed was the one requested as a
4304 * result of entering roam... so, schedule the
4305 * roam work */
4306 queue_work(priv->workqueue, &priv->roam);
4307 } else if (priv->status & STATUS_SCAN_PENDING)
4308 queue_work(priv->workqueue,
4309 &priv->request_scan);
James Ketrenosa613bff2005-08-24 21:43:11 -05004310 else if (priv->config & CFG_BACKGROUND_SCAN
4311 && priv->status & STATUS_ASSOCIATED)
4312 queue_delayed_work(priv->workqueue,
4313 &priv->request_scan, HZ);
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004314 break;
4315 }
4316
4317 case HOST_NOTIFICATION_STATUS_FRAG_LENGTH:{
4318 struct notif_frag_length *x = &notif->u.frag_len;
4319
James Ketrenosa613bff2005-08-24 21:43:11 -05004320 if (notif->size == sizeof(*x))
4321 IPW_ERROR("Frag length: %d\n",
4322 le16_to_cpu(x->frag_length));
4323 else
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004324 IPW_ERROR("Frag length of wrong size %d "
4325 "(should be %zd)\n",
4326 notif->size, sizeof(*x));
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004327 break;
4328 }
4329
4330 case HOST_NOTIFICATION_STATUS_LINK_DETERIORATION:{
4331 struct notif_link_deterioration *x =
4332 &notif->u.link_deterioration;
4333 if (notif->size == sizeof(*x)) {
4334 IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE,
4335 "link deterioration: '%s' " MAC_FMT
4336 " \n", escape_essid(priv->essid,
4337 priv->essid_len),
4338 MAC_ARG(priv->bssid));
4339 memcpy(&priv->last_link_deterioration, x,
4340 sizeof(*x));
4341 } else {
4342 IPW_ERROR("Link Deterioration of wrong size %d "
4343 "(should be %zd)\n",
4344 notif->size, sizeof(*x));
4345 }
4346 break;
4347 }
4348
4349 case HOST_NOTIFICATION_DINO_CONFIG_RESPONSE:{
4350 IPW_ERROR("Dino config\n");
4351 if (priv->hcmd
James Ketrenosa613bff2005-08-24 21:43:11 -05004352 && priv->hcmd->cmd != HOST_CMD_DINO_CONFIG)
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004353 IPW_ERROR("Unexpected DINO_CONFIG_RESPONSE\n");
James Ketrenosa613bff2005-08-24 21:43:11 -05004354
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004355 break;
4356 }
4357
4358 case HOST_NOTIFICATION_STATUS_BEACON_STATE:{
4359 struct notif_beacon_state *x = &notif->u.beacon_state;
4360 if (notif->size != sizeof(*x)) {
4361 IPW_ERROR
4362 ("Beacon state of wrong size %d (should "
4363 "be %zd)\n", notif->size, sizeof(*x));
4364 break;
Jeff Garzikbf794512005-07-31 13:07:26 -04004365 }
James Ketrenos43f66a62005-03-25 12:31:53 -06004366
James Ketrenosa613bff2005-08-24 21:43:11 -05004367 if (le32_to_cpu(x->state) ==
4368 HOST_NOTIFICATION_STATUS_BEACON_MISSING)
4369 ipw_handle_missed_beacon(priv,
4370 le32_to_cpu(x->
4371 number));
Jeff Garzikbf794512005-07-31 13:07:26 -04004372
James Ketrenos43f66a62005-03-25 12:31:53 -06004373 break;
4374 }
Jeff Garzikbf794512005-07-31 13:07:26 -04004375
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004376 case HOST_NOTIFICATION_STATUS_TGI_TX_KEY:{
4377 struct notif_tgi_tx_key *x = &notif->u.tgi_tx_key;
4378 if (notif->size == sizeof(*x)) {
4379 IPW_ERROR("TGi Tx Key: state 0x%02x sec type "
4380 "0x%02x station %d\n",
4381 x->key_state, x->security_type,
4382 x->station_index);
4383 break;
James Ketrenos43f66a62005-03-25 12:31:53 -06004384 }
4385
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004386 IPW_ERROR
4387 ("TGi Tx Key of wrong size %d (should be %zd)\n",
4388 notif->size, sizeof(*x));
4389 break;
4390 }
4391
4392 case HOST_NOTIFICATION_CALIB_KEEP_RESULTS:{
4393 struct notif_calibration *x = &notif->u.calibration;
4394
4395 if (notif->size == sizeof(*x)) {
4396 memcpy(&priv->calib, x, sizeof(*x));
4397 IPW_DEBUG_INFO("TODO: Calibration\n");
4398 break;
James Ketrenos43f66a62005-03-25 12:31:53 -06004399 }
4400
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004401 IPW_ERROR
4402 ("Calibration of wrong size %d (should be %zd)\n",
4403 notif->size, sizeof(*x));
James Ketrenos43f66a62005-03-25 12:31:53 -06004404 break;
Jeff Garzikbf794512005-07-31 13:07:26 -04004405 }
James Ketrenos43f66a62005-03-25 12:31:53 -06004406
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004407 case HOST_NOTIFICATION_NOISE_STATS:{
4408 if (notif->size == sizeof(u32)) {
4409 priv->last_noise =
James Ketrenosa613bff2005-08-24 21:43:11 -05004410 (u8) (le32_to_cpu(notif->u.noise.value) &
4411 0xff);
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004412 average_add(&priv->average_noise,
4413 priv->last_noise);
4414 break;
4415 }
James Ketrenos43f66a62005-03-25 12:31:53 -06004416
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004417 IPW_ERROR
4418 ("Noise stat is wrong size %d (should be %zd)\n",
4419 notif->size, sizeof(u32));
James Ketrenos43f66a62005-03-25 12:31:53 -06004420 break;
Jeff Garzikbf794512005-07-31 13:07:26 -04004421 }
4422
James Ketrenos43f66a62005-03-25 12:31:53 -06004423 default:
4424 IPW_ERROR("Unknown notification: "
4425 "subtype=%d,flags=0x%2x,size=%d\n",
4426 notif->subtype, notif->flags, notif->size);
4427 }
4428}
4429
4430/**
4431 * Destroys all DMA structures and initialise them again
Jeff Garzikbf794512005-07-31 13:07:26 -04004432 *
James Ketrenos43f66a62005-03-25 12:31:53 -06004433 * @param priv
4434 * @return error code
4435 */
4436static int ipw_queue_reset(struct ipw_priv *priv)
4437{
4438 int rc = 0;
4439 /** @todo customize queue sizes */
4440 int nTx = 64, nTxCmd = 8;
4441 ipw_tx_queue_free(priv);
4442 /* Tx CMD queue */
4443 rc = ipw_queue_tx_init(priv, &priv->txq_cmd, nTxCmd,
James Ketrenosb095c382005-08-24 22:04:42 -05004444 IPW_TX_CMD_QUEUE_READ_INDEX,
4445 IPW_TX_CMD_QUEUE_WRITE_INDEX,
4446 IPW_TX_CMD_QUEUE_BD_BASE,
4447 IPW_TX_CMD_QUEUE_BD_SIZE);
James Ketrenos43f66a62005-03-25 12:31:53 -06004448 if (rc) {
4449 IPW_ERROR("Tx Cmd queue init failed\n");
4450 goto error;
4451 }
4452 /* Tx queue(s) */
4453 rc = ipw_queue_tx_init(priv, &priv->txq[0], nTx,
James Ketrenosb095c382005-08-24 22:04:42 -05004454 IPW_TX_QUEUE_0_READ_INDEX,
4455 IPW_TX_QUEUE_0_WRITE_INDEX,
4456 IPW_TX_QUEUE_0_BD_BASE, IPW_TX_QUEUE_0_BD_SIZE);
James Ketrenos43f66a62005-03-25 12:31:53 -06004457 if (rc) {
4458 IPW_ERROR("Tx 0 queue init failed\n");
4459 goto error;
4460 }
4461 rc = ipw_queue_tx_init(priv, &priv->txq[1], nTx,
James Ketrenosb095c382005-08-24 22:04:42 -05004462 IPW_TX_QUEUE_1_READ_INDEX,
4463 IPW_TX_QUEUE_1_WRITE_INDEX,
4464 IPW_TX_QUEUE_1_BD_BASE, IPW_TX_QUEUE_1_BD_SIZE);
James Ketrenos43f66a62005-03-25 12:31:53 -06004465 if (rc) {
4466 IPW_ERROR("Tx 1 queue init failed\n");
4467 goto error;
4468 }
4469 rc = ipw_queue_tx_init(priv, &priv->txq[2], nTx,
James Ketrenosb095c382005-08-24 22:04:42 -05004470 IPW_TX_QUEUE_2_READ_INDEX,
4471 IPW_TX_QUEUE_2_WRITE_INDEX,
4472 IPW_TX_QUEUE_2_BD_BASE, IPW_TX_QUEUE_2_BD_SIZE);
James Ketrenos43f66a62005-03-25 12:31:53 -06004473 if (rc) {
4474 IPW_ERROR("Tx 2 queue init failed\n");
4475 goto error;
4476 }
4477 rc = ipw_queue_tx_init(priv, &priv->txq[3], nTx,
James Ketrenosb095c382005-08-24 22:04:42 -05004478 IPW_TX_QUEUE_3_READ_INDEX,
4479 IPW_TX_QUEUE_3_WRITE_INDEX,
4480 IPW_TX_QUEUE_3_BD_BASE, IPW_TX_QUEUE_3_BD_SIZE);
James Ketrenos43f66a62005-03-25 12:31:53 -06004481 if (rc) {
4482 IPW_ERROR("Tx 3 queue init failed\n");
4483 goto error;
4484 }
4485 /* statistics */
4486 priv->rx_bufs_min = 0;
4487 priv->rx_pend_max = 0;
4488 return rc;
4489
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004490 error:
James Ketrenos43f66a62005-03-25 12:31:53 -06004491 ipw_tx_queue_free(priv);
4492 return rc;
4493}
4494
4495/**
4496 * Reclaim Tx queue entries no more used by NIC.
Jeff Garzikbf794512005-07-31 13:07:26 -04004497 *
James Ketrenos43f66a62005-03-25 12:31:53 -06004498 * When FW adwances 'R' index, all entries between old and
4499 * new 'R' index need to be reclaimed. As result, some free space
4500 * forms. If there is enough free space (> low mark), wake Tx queue.
Jeff Garzikbf794512005-07-31 13:07:26 -04004501 *
James Ketrenos43f66a62005-03-25 12:31:53 -06004502 * @note Need to protect against garbage in 'R' index
4503 * @param priv
4504 * @param txq
4505 * @param qindex
4506 * @return Number of used entries remains in the queue
4507 */
Jeff Garzikbf794512005-07-31 13:07:26 -04004508static int ipw_queue_tx_reclaim(struct ipw_priv *priv,
James Ketrenos43f66a62005-03-25 12:31:53 -06004509 struct clx2_tx_queue *txq, int qindex)
4510{
4511 u32 hw_tail;
4512 int used;
4513 struct clx2_queue *q = &txq->q;
4514
4515 hw_tail = ipw_read32(priv, q->reg_r);
4516 if (hw_tail >= q->n_bd) {
4517 IPW_ERROR
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004518 ("Read index for DMA queue (%d) is out of range [0-%d)\n",
4519 hw_tail, q->n_bd);
James Ketrenos43f66a62005-03-25 12:31:53 -06004520 goto done;
4521 }
4522 for (; q->last_used != hw_tail;
4523 q->last_used = ipw_queue_inc_wrap(q->last_used, q->n_bd)) {
4524 ipw_queue_tx_free_tfd(priv, txq);
4525 priv->tx_packets++;
4526 }
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004527 done:
James Ketrenosa613bff2005-08-24 21:43:11 -05004528 if (ipw_queue_space(q) > q->low_mark && qindex >= 0)
James Ketrenos43f66a62005-03-25 12:31:53 -06004529 __maybe_wake_tx(priv);
James Ketrenos43f66a62005-03-25 12:31:53 -06004530 used = q->first_empty - q->last_used;
4531 if (used < 0)
4532 used += q->n_bd;
4533
4534 return used;
4535}
4536
4537static int ipw_queue_tx_hcmd(struct ipw_priv *priv, int hcmd, void *buf,
4538 int len, int sync)
4539{
4540 struct clx2_tx_queue *txq = &priv->txq_cmd;
4541 struct clx2_queue *q = &txq->q;
4542 struct tfd_frame *tfd;
4543
4544 if (ipw_queue_space(q) < (sync ? 1 : 2)) {
4545 IPW_ERROR("No space for Tx\n");
4546 return -EBUSY;
4547 }
4548
4549 tfd = &txq->bd[q->first_empty];
4550 txq->txb[q->first_empty] = NULL;
4551
4552 memset(tfd, 0, sizeof(*tfd));
4553 tfd->control_flags.message_type = TX_HOST_COMMAND_TYPE;
4554 tfd->control_flags.control_bits = TFD_NEED_IRQ_MASK;
4555 priv->hcmd_seq++;
4556 tfd->u.cmd.index = hcmd;
4557 tfd->u.cmd.length = len;
4558 memcpy(tfd->u.cmd.payload, buf, len);
4559 q->first_empty = ipw_queue_inc_wrap(q->first_empty, q->n_bd);
4560 ipw_write32(priv, q->reg_w, q->first_empty);
4561 _ipw_read32(priv, 0x90);
4562
4563 return 0;
4564}
4565
Jeff Garzikbf794512005-07-31 13:07:26 -04004566/*
James Ketrenos43f66a62005-03-25 12:31:53 -06004567 * Rx theory of operation
4568 *
4569 * The host allocates 32 DMA target addresses and passes the host address
James Ketrenosb095c382005-08-24 22:04:42 -05004570 * to the firmware at register IPW_RFDS_TABLE_LOWER + N * RFD_SIZE where N is
James Ketrenos43f66a62005-03-25 12:31:53 -06004571 * 0 to 31
4572 *
4573 * Rx Queue Indexes
4574 * The host/firmware share two index registers for managing the Rx buffers.
4575 *
Jeff Garzikbf794512005-07-31 13:07:26 -04004576 * The READ index maps to the first position that the firmware may be writing
4577 * to -- the driver can read up to (but not including) this position and get
4578 * good data.
James Ketrenos43f66a62005-03-25 12:31:53 -06004579 * The READ index is managed by the firmware once the card is enabled.
4580 *
4581 * The WRITE index maps to the last position the driver has read from -- the
4582 * position preceding WRITE is the last slot the firmware can place a packet.
4583 *
4584 * The queue is empty (no good data) if WRITE = READ - 1, and is full if
Jeff Garzikbf794512005-07-31 13:07:26 -04004585 * WRITE = READ.
James Ketrenos43f66a62005-03-25 12:31:53 -06004586 *
Jeff Garzikbf794512005-07-31 13:07:26 -04004587 * During initialization the host sets up the READ queue position to the first
James Ketrenos43f66a62005-03-25 12:31:53 -06004588 * INDEX position, and WRITE to the last (READ - 1 wrapped)
4589 *
4590 * When the firmware places a packet in a buffer it will advance the READ index
4591 * and fire the RX interrupt. The driver can then query the READ index and
4592 * process as many packets as possible, moving the WRITE index forward as it
4593 * resets the Rx queue buffers with new memory.
Jeff Garzikbf794512005-07-31 13:07:26 -04004594 *
James Ketrenos43f66a62005-03-25 12:31:53 -06004595 * The management in the driver is as follows:
Jeff Garzikbf794512005-07-31 13:07:26 -04004596 * + A list of pre-allocated SKBs is stored in ipw->rxq->rx_free. When
James Ketrenos43f66a62005-03-25 12:31:53 -06004597 * ipw->rxq->free_count drops to or below RX_LOW_WATERMARK, work is scheduled
Jeff Garzikbf794512005-07-31 13:07:26 -04004598 * to replensish the ipw->rxq->rx_free.
James Ketrenos43f66a62005-03-25 12:31:53 -06004599 * + In ipw_rx_queue_replenish (scheduled) if 'processed' != 'read' then the
4600 * ipw->rxq is replenished and the READ INDEX is updated (updating the
4601 * 'processed' and 'read' driver indexes as well)
4602 * + A received packet is processed and handed to the kernel network stack,
4603 * detached from the ipw->rxq. The driver 'processed' index is updated.
4604 * + The Host/Firmware ipw->rxq is replenished at tasklet time from the rx_free
Jeff Garzikbf794512005-07-31 13:07:26 -04004605 * list. If there are no allocated buffers in ipw->rxq->rx_free, the READ
4606 * INDEX is not incremented and ipw->status(RX_STALLED) is set. If there
James Ketrenos43f66a62005-03-25 12:31:53 -06004607 * were enough free buffers and RX_STALLED is set it is cleared.
4608 *
4609 *
4610 * Driver sequence:
4611 *
Jeff Garzikbf794512005-07-31 13:07:26 -04004612 * ipw_rx_queue_alloc() Allocates rx_free
James Ketrenos43f66a62005-03-25 12:31:53 -06004613 * ipw_rx_queue_replenish() Replenishes rx_free list from rx_used, and calls
4614 * ipw_rx_queue_restock
4615 * ipw_rx_queue_restock() Moves available buffers from rx_free into Rx
4616 * queue, updates firmware pointers, and updates
4617 * the WRITE index. If insufficient rx_free buffers
4618 * are available, schedules ipw_rx_queue_replenish
4619 *
4620 * -- enable interrupts --
4621 * ISR - ipw_rx() Detach ipw_rx_mem_buffers from pool up to the
Jeff Garzikbf794512005-07-31 13:07:26 -04004622 * READ INDEX, detaching the SKB from the pool.
James Ketrenos43f66a62005-03-25 12:31:53 -06004623 * Moves the packet buffer from queue to rx_used.
4624 * Calls ipw_rx_queue_restock to refill any empty
4625 * slots.
4626 * ...
4627 *
4628 */
4629
Jeff Garzikbf794512005-07-31 13:07:26 -04004630/*
James Ketrenos43f66a62005-03-25 12:31:53 -06004631 * If there are slots in the RX queue that need to be restocked,
4632 * and we have free pre-allocated buffers, fill the ranks as much
4633 * as we can pulling from rx_free.
4634 *
4635 * This moves the 'write' index forward to catch up with 'processed', and
4636 * also updates the memory address in the firmware to reference the new
4637 * target buffer.
4638 */
4639static void ipw_rx_queue_restock(struct ipw_priv *priv)
4640{
4641 struct ipw_rx_queue *rxq = priv->rxq;
4642 struct list_head *element;
4643 struct ipw_rx_mem_buffer *rxb;
4644 unsigned long flags;
4645 int write;
4646
4647 spin_lock_irqsave(&rxq->lock, flags);
4648 write = rxq->write;
4649 while ((rxq->write != rxq->processed) && (rxq->free_count)) {
4650 element = rxq->rx_free.next;
4651 rxb = list_entry(element, struct ipw_rx_mem_buffer, list);
4652 list_del(element);
4653
James Ketrenosb095c382005-08-24 22:04:42 -05004654 ipw_write32(priv, IPW_RFDS_TABLE_LOWER + rxq->write * RFD_SIZE,
James Ketrenos43f66a62005-03-25 12:31:53 -06004655 rxb->dma_addr);
4656 rxq->queue[rxq->write] = rxb;
4657 rxq->write = (rxq->write + 1) % RX_QUEUE_SIZE;
4658 rxq->free_count--;
4659 }
4660 spin_unlock_irqrestore(&rxq->lock, flags);
4661
Jeff Garzikbf794512005-07-31 13:07:26 -04004662 /* If the pre-allocated buffer pool is dropping low, schedule to
James Ketrenos43f66a62005-03-25 12:31:53 -06004663 * refill it */
4664 if (rxq->free_count <= RX_LOW_WATERMARK)
4665 queue_work(priv->workqueue, &priv->rx_replenish);
4666
4667 /* If we've added more space for the firmware to place data, tell it */
Jeff Garzikbf794512005-07-31 13:07:26 -04004668 if (write != rxq->write)
James Ketrenosb095c382005-08-24 22:04:42 -05004669 ipw_write32(priv, IPW_RX_WRITE_INDEX, rxq->write);
James Ketrenos43f66a62005-03-25 12:31:53 -06004670}
4671
4672/*
4673 * Move all used packet from rx_used to rx_free, allocating a new SKB for each.
Jeff Garzikbf794512005-07-31 13:07:26 -04004674 * Also restock the Rx queue via ipw_rx_queue_restock.
4675 *
James Ketrenos43f66a62005-03-25 12:31:53 -06004676 * This is called as a scheduled work item (except for during intialization)
4677 */
4678static void ipw_rx_queue_replenish(void *data)
4679{
4680 struct ipw_priv *priv = data;
4681 struct ipw_rx_queue *rxq = priv->rxq;
4682 struct list_head *element;
4683 struct ipw_rx_mem_buffer *rxb;
4684 unsigned long flags;
4685
4686 spin_lock_irqsave(&rxq->lock, flags);
4687 while (!list_empty(&rxq->rx_used)) {
4688 element = rxq->rx_used.next;
4689 rxb = list_entry(element, struct ipw_rx_mem_buffer, list);
James Ketrenosb095c382005-08-24 22:04:42 -05004690 rxb->skb = alloc_skb(IPW_RX_BUF_SIZE, GFP_ATOMIC);
James Ketrenos43f66a62005-03-25 12:31:53 -06004691 if (!rxb->skb) {
4692 printk(KERN_CRIT "%s: Can not allocate SKB buffers.\n",
4693 priv->net_dev->name);
4694 /* We don't reschedule replenish work here -- we will
4695 * call the restock method and if it still needs
4696 * more buffers it will schedule replenish */
4697 break;
4698 }
4699 list_del(element);
Jeff Garzikbf794512005-07-31 13:07:26 -04004700
James Ketrenos43f66a62005-03-25 12:31:53 -06004701 rxb->rxb = (struct ipw_rx_buffer *)rxb->skb->data;
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004702 rxb->dma_addr =
4703 pci_map_single(priv->pci_dev, rxb->skb->data,
James Ketrenosb095c382005-08-24 22:04:42 -05004704 IPW_RX_BUF_SIZE, PCI_DMA_FROMDEVICE);
Jeff Garzikbf794512005-07-31 13:07:26 -04004705
James Ketrenos43f66a62005-03-25 12:31:53 -06004706 list_add_tail(&rxb->list, &rxq->rx_free);
4707 rxq->free_count++;
4708 }
4709 spin_unlock_irqrestore(&rxq->lock, flags);
4710
4711 ipw_rx_queue_restock(priv);
4712}
4713
James Ketrenosc848d0a2005-08-24 21:56:24 -05004714static void ipw_bg_rx_queue_replenish(void *data)
4715{
4716 struct ipw_priv *priv = data;
4717 down(&priv->sem);
4718 ipw_rx_queue_replenish(data);
4719 up(&priv->sem);
4720}
4721
James Ketrenos43f66a62005-03-25 12:31:53 -06004722/* Assumes that the skb field of the buffers in 'pool' is kept accurate.
4723 * If an SKB has been detached, the POOL needs to have it's SKB set to NULL
Jeff Garzikbf794512005-07-31 13:07:26 -04004724 * This free routine walks the list of POOL entries and if SKB is set to
James Ketrenos43f66a62005-03-25 12:31:53 -06004725 * non NULL it is unmapped and freed
4726 */
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004727static void ipw_rx_queue_free(struct ipw_priv *priv, struct ipw_rx_queue *rxq)
James Ketrenos43f66a62005-03-25 12:31:53 -06004728{
4729 int i;
4730
4731 if (!rxq)
4732 return;
Jeff Garzikbf794512005-07-31 13:07:26 -04004733
James Ketrenos43f66a62005-03-25 12:31:53 -06004734 for (i = 0; i < RX_QUEUE_SIZE + RX_FREE_BUFFERS; i++) {
4735 if (rxq->pool[i].skb != NULL) {
4736 pci_unmap_single(priv->pci_dev, rxq->pool[i].dma_addr,
James Ketrenosb095c382005-08-24 22:04:42 -05004737 IPW_RX_BUF_SIZE, PCI_DMA_FROMDEVICE);
James Ketrenos43f66a62005-03-25 12:31:53 -06004738 dev_kfree_skb(rxq->pool[i].skb);
4739 }
4740 }
4741
4742 kfree(rxq);
4743}
4744
4745static struct ipw_rx_queue *ipw_rx_queue_alloc(struct ipw_priv *priv)
4746{
4747 struct ipw_rx_queue *rxq;
4748 int i;
4749
4750 rxq = (struct ipw_rx_queue *)kmalloc(sizeof(*rxq), GFP_KERNEL);
Panagiotis Issarisad18b0e2005-09-05 04:14:10 +02004751 if (unlikely(!rxq)) {
4752 IPW_ERROR("memory allocation failed\n");
4753 return NULL;
4754 }
James Ketrenos43f66a62005-03-25 12:31:53 -06004755 memset(rxq, 0, sizeof(*rxq));
4756 spin_lock_init(&rxq->lock);
4757 INIT_LIST_HEAD(&rxq->rx_free);
4758 INIT_LIST_HEAD(&rxq->rx_used);
4759
4760 /* Fill the rx_used queue with _all_ of the Rx buffers */
Jeff Garzikbf794512005-07-31 13:07:26 -04004761 for (i = 0; i < RX_FREE_BUFFERS + RX_QUEUE_SIZE; i++)
James Ketrenos43f66a62005-03-25 12:31:53 -06004762 list_add_tail(&rxq->pool[i].list, &rxq->rx_used);
4763
4764 /* Set us so that we have processed and used all buffers, but have
4765 * not restocked the Rx queue with fresh buffers */
4766 rxq->read = rxq->write = 0;
4767 rxq->processed = RX_QUEUE_SIZE - 1;
4768 rxq->free_count = 0;
4769
4770 return rxq;
4771}
4772
4773static int ipw_is_rate_in_mask(struct ipw_priv *priv, int ieee_mode, u8 rate)
4774{
4775 rate &= ~IEEE80211_BASIC_RATE_MASK;
4776 if (ieee_mode == IEEE_A) {
4777 switch (rate) {
Jeff Garzikbf794512005-07-31 13:07:26 -04004778 case IEEE80211_OFDM_RATE_6MB:
4779 return priv->rates_mask & IEEE80211_OFDM_RATE_6MB_MASK ?
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004780 1 : 0;
Jeff Garzikbf794512005-07-31 13:07:26 -04004781 case IEEE80211_OFDM_RATE_9MB:
4782 return priv->rates_mask & IEEE80211_OFDM_RATE_9MB_MASK ?
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004783 1 : 0;
Jeff Garzikbf794512005-07-31 13:07:26 -04004784 case IEEE80211_OFDM_RATE_12MB:
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004785 return priv->
4786 rates_mask & IEEE80211_OFDM_RATE_12MB_MASK ? 1 : 0;
Jeff Garzikbf794512005-07-31 13:07:26 -04004787 case IEEE80211_OFDM_RATE_18MB:
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004788 return priv->
4789 rates_mask & IEEE80211_OFDM_RATE_18MB_MASK ? 1 : 0;
Jeff Garzikbf794512005-07-31 13:07:26 -04004790 case IEEE80211_OFDM_RATE_24MB:
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004791 return priv->
4792 rates_mask & IEEE80211_OFDM_RATE_24MB_MASK ? 1 : 0;
Jeff Garzikbf794512005-07-31 13:07:26 -04004793 case IEEE80211_OFDM_RATE_36MB:
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004794 return priv->
4795 rates_mask & IEEE80211_OFDM_RATE_36MB_MASK ? 1 : 0;
Jeff Garzikbf794512005-07-31 13:07:26 -04004796 case IEEE80211_OFDM_RATE_48MB:
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004797 return priv->
4798 rates_mask & IEEE80211_OFDM_RATE_48MB_MASK ? 1 : 0;
Jeff Garzikbf794512005-07-31 13:07:26 -04004799 case IEEE80211_OFDM_RATE_54MB:
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004800 return priv->
4801 rates_mask & IEEE80211_OFDM_RATE_54MB_MASK ? 1 : 0;
James Ketrenos43f66a62005-03-25 12:31:53 -06004802 default:
4803 return 0;
4804 }
4805 }
Jeff Garzikbf794512005-07-31 13:07:26 -04004806
James Ketrenos43f66a62005-03-25 12:31:53 -06004807 /* B and G mixed */
4808 switch (rate) {
Jeff Garzikbf794512005-07-31 13:07:26 -04004809 case IEEE80211_CCK_RATE_1MB:
James Ketrenos43f66a62005-03-25 12:31:53 -06004810 return priv->rates_mask & IEEE80211_CCK_RATE_1MB_MASK ? 1 : 0;
Jeff Garzikbf794512005-07-31 13:07:26 -04004811 case IEEE80211_CCK_RATE_2MB:
James Ketrenos43f66a62005-03-25 12:31:53 -06004812 return priv->rates_mask & IEEE80211_CCK_RATE_2MB_MASK ? 1 : 0;
Jeff Garzikbf794512005-07-31 13:07:26 -04004813 case IEEE80211_CCK_RATE_5MB:
James Ketrenos43f66a62005-03-25 12:31:53 -06004814 return priv->rates_mask & IEEE80211_CCK_RATE_5MB_MASK ? 1 : 0;
Jeff Garzikbf794512005-07-31 13:07:26 -04004815 case IEEE80211_CCK_RATE_11MB:
James Ketrenos43f66a62005-03-25 12:31:53 -06004816 return priv->rates_mask & IEEE80211_CCK_RATE_11MB_MASK ? 1 : 0;
4817 }
4818
4819 /* If we are limited to B modulations, bail at this point */
4820 if (ieee_mode == IEEE_B)
4821 return 0;
4822
4823 /* G */
4824 switch (rate) {
Jeff Garzikbf794512005-07-31 13:07:26 -04004825 case IEEE80211_OFDM_RATE_6MB:
James Ketrenos43f66a62005-03-25 12:31:53 -06004826 return priv->rates_mask & IEEE80211_OFDM_RATE_6MB_MASK ? 1 : 0;
Jeff Garzikbf794512005-07-31 13:07:26 -04004827 case IEEE80211_OFDM_RATE_9MB:
James Ketrenos43f66a62005-03-25 12:31:53 -06004828 return priv->rates_mask & IEEE80211_OFDM_RATE_9MB_MASK ? 1 : 0;
Jeff Garzikbf794512005-07-31 13:07:26 -04004829 case IEEE80211_OFDM_RATE_12MB:
James Ketrenos43f66a62005-03-25 12:31:53 -06004830 return priv->rates_mask & IEEE80211_OFDM_RATE_12MB_MASK ? 1 : 0;
Jeff Garzikbf794512005-07-31 13:07:26 -04004831 case IEEE80211_OFDM_RATE_18MB:
James Ketrenos43f66a62005-03-25 12:31:53 -06004832 return priv->rates_mask & IEEE80211_OFDM_RATE_18MB_MASK ? 1 : 0;
Jeff Garzikbf794512005-07-31 13:07:26 -04004833 case IEEE80211_OFDM_RATE_24MB:
James Ketrenos43f66a62005-03-25 12:31:53 -06004834 return priv->rates_mask & IEEE80211_OFDM_RATE_24MB_MASK ? 1 : 0;
Jeff Garzikbf794512005-07-31 13:07:26 -04004835 case IEEE80211_OFDM_RATE_36MB:
James Ketrenos43f66a62005-03-25 12:31:53 -06004836 return priv->rates_mask & IEEE80211_OFDM_RATE_36MB_MASK ? 1 : 0;
Jeff Garzikbf794512005-07-31 13:07:26 -04004837 case IEEE80211_OFDM_RATE_48MB:
James Ketrenos43f66a62005-03-25 12:31:53 -06004838 return priv->rates_mask & IEEE80211_OFDM_RATE_48MB_MASK ? 1 : 0;
Jeff Garzikbf794512005-07-31 13:07:26 -04004839 case IEEE80211_OFDM_RATE_54MB:
James Ketrenos43f66a62005-03-25 12:31:53 -06004840 return priv->rates_mask & IEEE80211_OFDM_RATE_54MB_MASK ? 1 : 0;
4841 }
4842
4843 return 0;
4844}
4845
Jeff Garzikbf794512005-07-31 13:07:26 -04004846static int ipw_compatible_rates(struct ipw_priv *priv,
James Ketrenos43f66a62005-03-25 12:31:53 -06004847 const struct ieee80211_network *network,
4848 struct ipw_supported_rates *rates)
4849{
4850 int num_rates, i;
4851
4852 memset(rates, 0, sizeof(*rates));
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004853 num_rates = min(network->rates_len, (u8) IPW_MAX_RATES);
James Ketrenos43f66a62005-03-25 12:31:53 -06004854 rates->num_rates = 0;
4855 for (i = 0; i < num_rates; i++) {
James Ketrenosa613bff2005-08-24 21:43:11 -05004856 if (!ipw_is_rate_in_mask(priv, network->mode,
4857 network->rates[i])) {
4858
James Ketrenosea2b26e2005-08-24 21:25:16 -05004859 if (network->rates[i] & IEEE80211_BASIC_RATE_MASK) {
James Ketrenosa613bff2005-08-24 21:43:11 -05004860 IPW_DEBUG_SCAN("Adding masked mandatory "
4861 "rate %02X\n",
4862 network->rates[i]);
4863 rates->supported_rates[rates->num_rates++] =
4864 network->rates[i];
4865 continue;
James Ketrenosea2b26e2005-08-24 21:25:16 -05004866 }
4867
James Ketrenos43f66a62005-03-25 12:31:53 -06004868 IPW_DEBUG_SCAN("Rate %02X masked : 0x%08X\n",
4869 network->rates[i], priv->rates_mask);
4870 continue;
4871 }
Jeff Garzikbf794512005-07-31 13:07:26 -04004872
James Ketrenos43f66a62005-03-25 12:31:53 -06004873 rates->supported_rates[rates->num_rates++] = network->rates[i];
4874 }
4875
James Ketrenosa613bff2005-08-24 21:43:11 -05004876 num_rates = min(network->rates_ex_len,
4877 (u8) (IPW_MAX_RATES - num_rates));
James Ketrenos43f66a62005-03-25 12:31:53 -06004878 for (i = 0; i < num_rates; i++) {
James Ketrenosa613bff2005-08-24 21:43:11 -05004879 if (!ipw_is_rate_in_mask(priv, network->mode,
4880 network->rates_ex[i])) {
James Ketrenosea2b26e2005-08-24 21:25:16 -05004881 if (network->rates_ex[i] & IEEE80211_BASIC_RATE_MASK) {
James Ketrenosa613bff2005-08-24 21:43:11 -05004882 IPW_DEBUG_SCAN("Adding masked mandatory "
4883 "rate %02X\n",
4884 network->rates_ex[i]);
4885 rates->supported_rates[rates->num_rates++] =
4886 network->rates[i];
4887 continue;
James Ketrenosea2b26e2005-08-24 21:25:16 -05004888 }
4889
James Ketrenos43f66a62005-03-25 12:31:53 -06004890 IPW_DEBUG_SCAN("Rate %02X masked : 0x%08X\n",
4891 network->rates_ex[i], priv->rates_mask);
4892 continue;
4893 }
Jeff Garzikbf794512005-07-31 13:07:26 -04004894
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004895 rates->supported_rates[rates->num_rates++] =
4896 network->rates_ex[i];
James Ketrenos43f66a62005-03-25 12:31:53 -06004897 }
4898
James Ketrenosea2b26e2005-08-24 21:25:16 -05004899 return 1;
James Ketrenos43f66a62005-03-25 12:31:53 -06004900}
4901
4902static inline void ipw_copy_rates(struct ipw_supported_rates *dest,
4903 const struct ipw_supported_rates *src)
4904{
4905 u8 i;
4906 for (i = 0; i < src->num_rates; i++)
4907 dest->supported_rates[i] = src->supported_rates[i];
4908 dest->num_rates = src->num_rates;
4909}
4910
4911/* TODO: Look at sniffed packets in the air to determine if the basic rate
4912 * mask should ever be used -- right now all callers to add the scan rates are
4913 * set with the modulation = CCK, so BASIC_RATE_MASK is never set... */
4914static void ipw_add_cck_scan_rates(struct ipw_supported_rates *rates,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004915 u8 modulation, u32 rate_mask)
James Ketrenos43f66a62005-03-25 12:31:53 -06004916{
Jeff Garzikbf794512005-07-31 13:07:26 -04004917 u8 basic_mask = (IEEE80211_OFDM_MODULATION == modulation) ?
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004918 IEEE80211_BASIC_RATE_MASK : 0;
Jeff Garzikbf794512005-07-31 13:07:26 -04004919
James Ketrenos43f66a62005-03-25 12:31:53 -06004920 if (rate_mask & IEEE80211_CCK_RATE_1MB_MASK)
Jeff Garzikbf794512005-07-31 13:07:26 -04004921 rates->supported_rates[rates->num_rates++] =
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004922 IEEE80211_BASIC_RATE_MASK | IEEE80211_CCK_RATE_1MB;
James Ketrenos43f66a62005-03-25 12:31:53 -06004923
4924 if (rate_mask & IEEE80211_CCK_RATE_2MB_MASK)
Jeff Garzikbf794512005-07-31 13:07:26 -04004925 rates->supported_rates[rates->num_rates++] =
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004926 IEEE80211_BASIC_RATE_MASK | IEEE80211_CCK_RATE_2MB;
James Ketrenos43f66a62005-03-25 12:31:53 -06004927
4928 if (rate_mask & IEEE80211_CCK_RATE_5MB_MASK)
Jeff Garzikbf794512005-07-31 13:07:26 -04004929 rates->supported_rates[rates->num_rates++] = basic_mask |
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004930 IEEE80211_CCK_RATE_5MB;
James Ketrenos43f66a62005-03-25 12:31:53 -06004931
4932 if (rate_mask & IEEE80211_CCK_RATE_11MB_MASK)
Jeff Garzikbf794512005-07-31 13:07:26 -04004933 rates->supported_rates[rates->num_rates++] = basic_mask |
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004934 IEEE80211_CCK_RATE_11MB;
James Ketrenos43f66a62005-03-25 12:31:53 -06004935}
4936
4937static void ipw_add_ofdm_scan_rates(struct ipw_supported_rates *rates,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004938 u8 modulation, u32 rate_mask)
James Ketrenos43f66a62005-03-25 12:31:53 -06004939{
Jeff Garzikbf794512005-07-31 13:07:26 -04004940 u8 basic_mask = (IEEE80211_OFDM_MODULATION == modulation) ?
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004941 IEEE80211_BASIC_RATE_MASK : 0;
James Ketrenos43f66a62005-03-25 12:31:53 -06004942
4943 if (rate_mask & IEEE80211_OFDM_RATE_6MB_MASK)
Jeff Garzikbf794512005-07-31 13:07:26 -04004944 rates->supported_rates[rates->num_rates++] = basic_mask |
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004945 IEEE80211_OFDM_RATE_6MB;
James Ketrenos43f66a62005-03-25 12:31:53 -06004946
4947 if (rate_mask & IEEE80211_OFDM_RATE_9MB_MASK)
Jeff Garzikbf794512005-07-31 13:07:26 -04004948 rates->supported_rates[rates->num_rates++] =
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004949 IEEE80211_OFDM_RATE_9MB;
James Ketrenos43f66a62005-03-25 12:31:53 -06004950
4951 if (rate_mask & IEEE80211_OFDM_RATE_12MB_MASK)
Jeff Garzikbf794512005-07-31 13:07:26 -04004952 rates->supported_rates[rates->num_rates++] = basic_mask |
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004953 IEEE80211_OFDM_RATE_12MB;
James Ketrenos43f66a62005-03-25 12:31:53 -06004954
4955 if (rate_mask & IEEE80211_OFDM_RATE_18MB_MASK)
Jeff Garzikbf794512005-07-31 13:07:26 -04004956 rates->supported_rates[rates->num_rates++] =
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004957 IEEE80211_OFDM_RATE_18MB;
James Ketrenos43f66a62005-03-25 12:31:53 -06004958
4959 if (rate_mask & IEEE80211_OFDM_RATE_24MB_MASK)
Jeff Garzikbf794512005-07-31 13:07:26 -04004960 rates->supported_rates[rates->num_rates++] = basic_mask |
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004961 IEEE80211_OFDM_RATE_24MB;
James Ketrenos43f66a62005-03-25 12:31:53 -06004962
4963 if (rate_mask & IEEE80211_OFDM_RATE_36MB_MASK)
Jeff Garzikbf794512005-07-31 13:07:26 -04004964 rates->supported_rates[rates->num_rates++] =
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004965 IEEE80211_OFDM_RATE_36MB;
James Ketrenos43f66a62005-03-25 12:31:53 -06004966
4967 if (rate_mask & IEEE80211_OFDM_RATE_48MB_MASK)
Jeff Garzikbf794512005-07-31 13:07:26 -04004968 rates->supported_rates[rates->num_rates++] =
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004969 IEEE80211_OFDM_RATE_48MB;
James Ketrenos43f66a62005-03-25 12:31:53 -06004970
4971 if (rate_mask & IEEE80211_OFDM_RATE_54MB_MASK)
Jeff Garzikbf794512005-07-31 13:07:26 -04004972 rates->supported_rates[rates->num_rates++] =
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004973 IEEE80211_OFDM_RATE_54MB;
James Ketrenos43f66a62005-03-25 12:31:53 -06004974}
4975
4976struct ipw_network_match {
4977 struct ieee80211_network *network;
4978 struct ipw_supported_rates rates;
4979};
4980
James Ketrenosc848d0a2005-08-24 21:56:24 -05004981static int ipw_find_adhoc_network(struct ipw_priv *priv,
4982 struct ipw_network_match *match,
4983 struct ieee80211_network *network,
4984 int roaming)
4985{
4986 struct ipw_supported_rates rates;
4987
4988 /* Verify that this network's capability is compatible with the
4989 * current mode (AdHoc or Infrastructure) */
4990 if ((priv->ieee->iw_mode == IW_MODE_ADHOC &&
4991 !(network->capability & WLAN_CAPABILITY_IBSS))) {
4992 IPW_DEBUG_MERGE("Network '%s (" MAC_FMT ")' excluded due to "
4993 "capability mismatch.\n",
4994 escape_essid(network->ssid, network->ssid_len),
4995 MAC_ARG(network->bssid));
4996 return 0;
4997 }
4998
4999 /* If we do not have an ESSID for this AP, we can not associate with
5000 * it */
5001 if (network->flags & NETWORK_EMPTY_ESSID) {
5002 IPW_DEBUG_MERGE("Network '%s (" MAC_FMT ")' excluded "
5003 "because of hidden ESSID.\n",
5004 escape_essid(network->ssid, network->ssid_len),
5005 MAC_ARG(network->bssid));
5006 return 0;
5007 }
5008
5009 if (unlikely(roaming)) {
5010 /* If we are roaming, then ensure check if this is a valid
5011 * network to try and roam to */
5012 if ((network->ssid_len != match->network->ssid_len) ||
5013 memcmp(network->ssid, match->network->ssid,
5014 network->ssid_len)) {
5015 IPW_DEBUG_MERGE("Netowrk '%s (" MAC_FMT ")' excluded "
5016 "because of non-network ESSID.\n",
5017 escape_essid(network->ssid,
5018 network->ssid_len),
5019 MAC_ARG(network->bssid));
5020 return 0;
5021 }
5022 } else {
5023 /* If an ESSID has been configured then compare the broadcast
5024 * ESSID to ours */
5025 if ((priv->config & CFG_STATIC_ESSID) &&
5026 ((network->ssid_len != priv->essid_len) ||
5027 memcmp(network->ssid, priv->essid,
5028 min(network->ssid_len, priv->essid_len)))) {
5029 char escaped[IW_ESSID_MAX_SIZE * 2 + 1];
5030 strncpy(escaped,
5031 escape_essid(network->ssid, network->ssid_len),
5032 sizeof(escaped));
5033 IPW_DEBUG_MERGE("Network '%s (" MAC_FMT ")' excluded "
5034 "because of ESSID mismatch: '%s'.\n",
5035 escaped, MAC_ARG(network->bssid),
5036 escape_essid(priv->essid,
5037 priv->essid_len));
5038 return 0;
5039 }
5040 }
5041
5042 /* If the old network rate is better than this one, don't bother
5043 * testing everything else. */
5044
5045 if (network->time_stamp[0] < match->network->time_stamp[0]) {
5046 IPW_DEBUG_MERGE
5047 ("Network '%s excluded because newer than current network.\n",
5048 escape_essid(match->network->ssid,
5049 match->network->ssid_len));
5050 return 0;
5051 } else if (network->time_stamp[1] < match->network->time_stamp[1]) {
5052 IPW_DEBUG_MERGE
5053 ("Network '%s excluded because newer than current network.\n",
5054 escape_essid(match->network->ssid,
5055 match->network->ssid_len));
5056 return 0;
5057 }
5058
5059 /* Now go through and see if the requested network is valid... */
5060 if (priv->ieee->scan_age != 0 &&
5061 time_after(jiffies, network->last_scanned + priv->ieee->scan_age)) {
5062 IPW_DEBUG_MERGE("Network '%s (" MAC_FMT ")' excluded "
5063 "because of age: %lums.\n",
5064 escape_essid(network->ssid, network->ssid_len),
5065 MAC_ARG(network->bssid),
5066 (jiffies - network->last_scanned) / (HZ / 100));
5067 return 0;
5068 }
5069
5070 if ((priv->config & CFG_STATIC_CHANNEL) &&
5071 (network->channel != priv->channel)) {
5072 IPW_DEBUG_MERGE("Network '%s (" MAC_FMT ")' excluded "
5073 "because of channel mismatch: %d != %d.\n",
5074 escape_essid(network->ssid, network->ssid_len),
5075 MAC_ARG(network->bssid),
5076 network->channel, priv->channel);
5077 return 0;
5078 }
5079
5080 /* Verify privacy compatability */
5081 if (((priv->capability & CAP_PRIVACY_ON) ? 1 : 0) !=
5082 ((network->capability & WLAN_CAPABILITY_PRIVACY) ? 1 : 0)) {
5083 IPW_DEBUG_MERGE("Network '%s (" MAC_FMT ")' excluded "
5084 "because of privacy mismatch: %s != %s.\n",
5085 escape_essid(network->ssid, network->ssid_len),
5086 MAC_ARG(network->bssid),
5087 priv->capability & CAP_PRIVACY_ON ? "on" :
5088 "off",
5089 network->capability &
5090 WLAN_CAPABILITY_PRIVACY ? "on" : "off");
5091 return 0;
5092 }
5093
5094 if (!memcmp(network->bssid, priv->bssid, ETH_ALEN)) {
5095 IPW_DEBUG_MERGE("Network '%s (" MAC_FMT ")' excluded "
5096 "because of the same BSSID match: " MAC_FMT
5097 ".\n", escape_essid(network->ssid,
5098 network->ssid_len),
5099 MAC_ARG(network->bssid), MAC_ARG(priv->bssid));
5100 return 0;
5101 }
5102
5103 /* Filter out any incompatible freq / mode combinations */
5104 if (!ieee80211_is_valid_mode(priv->ieee, network->mode)) {
5105 IPW_DEBUG_MERGE("Network '%s (" MAC_FMT ")' excluded "
5106 "because of invalid frequency/mode "
5107 "combination.\n",
5108 escape_essid(network->ssid, network->ssid_len),
5109 MAC_ARG(network->bssid));
5110 return 0;
5111 }
5112
5113 /* Ensure that the rates supported by the driver are compatible with
5114 * this AP, including verification of basic rates (mandatory) */
5115 if (!ipw_compatible_rates(priv, network, &rates)) {
5116 IPW_DEBUG_MERGE("Network '%s (" MAC_FMT ")' excluded "
5117 "because configured rate mask excludes "
5118 "AP mandatory rate.\n",
5119 escape_essid(network->ssid, network->ssid_len),
5120 MAC_ARG(network->bssid));
5121 return 0;
5122 }
5123
5124 if (rates.num_rates == 0) {
5125 IPW_DEBUG_MERGE("Network '%s (" MAC_FMT ")' excluded "
5126 "because of no compatible rates.\n",
5127 escape_essid(network->ssid, network->ssid_len),
5128 MAC_ARG(network->bssid));
5129 return 0;
5130 }
5131
5132 /* TODO: Perform any further minimal comparititive tests. We do not
5133 * want to put too much policy logic here; intelligent scan selection
5134 * should occur within a generic IEEE 802.11 user space tool. */
5135
5136 /* Set up 'new' AP to this network */
5137 ipw_copy_rates(&match->rates, &rates);
5138 match->network = network;
5139 IPW_DEBUG_MERGE("Network '%s (" MAC_FMT ")' is a viable match.\n",
5140 escape_essid(network->ssid, network->ssid_len),
5141 MAC_ARG(network->bssid));
5142
5143 return 1;
5144}
5145
5146static void ipw_merge_adhoc_network(void *data)
5147{
5148 struct ipw_priv *priv = data;
5149 struct ieee80211_network *network = NULL;
5150 struct ipw_network_match match = {
5151 .network = priv->assoc_network
5152 };
5153
5154 if ((priv->status & STATUS_ASSOCIATED)
5155 && (priv->ieee->iw_mode == IW_MODE_ADHOC)) {
5156 /* First pass through ROAM process -- look for a better
5157 * network */
5158 unsigned long flags;
5159
5160 spin_lock_irqsave(&priv->ieee->lock, flags);
5161 list_for_each_entry(network, &priv->ieee->network_list, list) {
5162 if (network != priv->assoc_network)
5163 ipw_find_adhoc_network(priv, &match, network,
5164 1);
5165 }
5166 spin_unlock_irqrestore(&priv->ieee->lock, flags);
5167
5168 if (match.network == priv->assoc_network) {
5169 IPW_DEBUG_MERGE("No better ADHOC in this network to "
5170 "merge to.\n");
5171 return;
5172 }
5173
5174 down(&priv->sem);
5175 if ((priv->ieee->iw_mode == IW_MODE_ADHOC)) {
5176 IPW_DEBUG_MERGE("remove network %s\n",
5177 escape_essid(priv->essid,
5178 priv->essid_len));
5179 ipw_remove_current_network(priv);
5180 }
5181
5182 ipw_disassociate(priv);
5183 priv->assoc_network = match.network;
5184 up(&priv->sem);
5185 return;
5186 }
5187
5188}
5189
Jeff Garzik0edd5b42005-09-07 00:48:31 -04005190static int ipw_best_network(struct ipw_priv *priv,
5191 struct ipw_network_match *match,
5192 struct ieee80211_network *network, int roaming)
James Ketrenos43f66a62005-03-25 12:31:53 -06005193{
5194 struct ipw_supported_rates rates;
5195
5196 /* Verify that this network's capability is compatible with the
5197 * current mode (AdHoc or Infrastructure) */
5198 if ((priv->ieee->iw_mode == IW_MODE_INFRA &&
Jouni Malinen24743852005-08-14 20:59:59 -07005199 !(network->capability & WLAN_CAPABILITY_ESS)) ||
James Ketrenos43f66a62005-03-25 12:31:53 -06005200 (priv->ieee->iw_mode == IW_MODE_ADHOC &&
5201 !(network->capability & WLAN_CAPABILITY_IBSS))) {
5202 IPW_DEBUG_ASSOC("Network '%s (" MAC_FMT ")' excluded due to "
Jeff Garzikbf794512005-07-31 13:07:26 -04005203 "capability mismatch.\n",
James Ketrenos43f66a62005-03-25 12:31:53 -06005204 escape_essid(network->ssid, network->ssid_len),
5205 MAC_ARG(network->bssid));
5206 return 0;
5207 }
5208
5209 /* If we do not have an ESSID for this AP, we can not associate with
5210 * it */
5211 if (network->flags & NETWORK_EMPTY_ESSID) {
5212 IPW_DEBUG_ASSOC("Network '%s (" MAC_FMT ")' excluded "
5213 "because of hidden ESSID.\n",
5214 escape_essid(network->ssid, network->ssid_len),
5215 MAC_ARG(network->bssid));
5216 return 0;
5217 }
Jeff Garzikbf794512005-07-31 13:07:26 -04005218
James Ketrenos43f66a62005-03-25 12:31:53 -06005219 if (unlikely(roaming)) {
5220 /* If we are roaming, then ensure check if this is a valid
5221 * network to try and roam to */
5222 if ((network->ssid_len != match->network->ssid_len) ||
Jeff Garzikbf794512005-07-31 13:07:26 -04005223 memcmp(network->ssid, match->network->ssid,
James Ketrenos43f66a62005-03-25 12:31:53 -06005224 network->ssid_len)) {
5225 IPW_DEBUG_ASSOC("Netowrk '%s (" MAC_FMT ")' excluded "
5226 "because of non-network ESSID.\n",
Jeff Garzikbf794512005-07-31 13:07:26 -04005227 escape_essid(network->ssid,
James Ketrenos43f66a62005-03-25 12:31:53 -06005228 network->ssid_len),
5229 MAC_ARG(network->bssid));
5230 return 0;
5231 }
5232 } else {
Jeff Garzikbf794512005-07-31 13:07:26 -04005233 /* If an ESSID has been configured then compare the broadcast
5234 * ESSID to ours */
5235 if ((priv->config & CFG_STATIC_ESSID) &&
James Ketrenos43f66a62005-03-25 12:31:53 -06005236 ((network->ssid_len != priv->essid_len) ||
Jeff Garzikbf794512005-07-31 13:07:26 -04005237 memcmp(network->ssid, priv->essid,
James Ketrenos43f66a62005-03-25 12:31:53 -06005238 min(network->ssid_len, priv->essid_len)))) {
5239 char escaped[IW_ESSID_MAX_SIZE * 2 + 1];
Jeff Garzik0edd5b42005-09-07 00:48:31 -04005240 strncpy(escaped,
5241 escape_essid(network->ssid, network->ssid_len),
James Ketrenos43f66a62005-03-25 12:31:53 -06005242 sizeof(escaped));
5243 IPW_DEBUG_ASSOC("Network '%s (" MAC_FMT ")' excluded "
Jeff Garzikbf794512005-07-31 13:07:26 -04005244 "because of ESSID mismatch: '%s'.\n",
James Ketrenos43f66a62005-03-25 12:31:53 -06005245 escaped, MAC_ARG(network->bssid),
Jeff Garzik0edd5b42005-09-07 00:48:31 -04005246 escape_essid(priv->essid,
5247 priv->essid_len));
James Ketrenos43f66a62005-03-25 12:31:53 -06005248 return 0;
5249 }
5250 }
5251
5252 /* If the old network rate is better than this one, don't bother
5253 * testing everything else. */
Jeff Garzik0edd5b42005-09-07 00:48:31 -04005254 if (match->network && match->network->stats.rssi > network->stats.rssi) {
James Ketrenos43f66a62005-03-25 12:31:53 -06005255 char escaped[IW_ESSID_MAX_SIZE * 2 + 1];
Jeff Garzikbf794512005-07-31 13:07:26 -04005256 strncpy(escaped,
5257 escape_essid(network->ssid, network->ssid_len),
James Ketrenos43f66a62005-03-25 12:31:53 -06005258 sizeof(escaped));
5259 IPW_DEBUG_ASSOC("Network '%s (" MAC_FMT ")' excluded because "
5260 "'%s (" MAC_FMT ")' has a stronger signal.\n",
5261 escaped, MAC_ARG(network->bssid),
5262 escape_essid(match->network->ssid,
5263 match->network->ssid_len),
5264 MAC_ARG(match->network->bssid));
5265 return 0;
5266 }
Jeff Garzikbf794512005-07-31 13:07:26 -04005267
James Ketrenos43f66a62005-03-25 12:31:53 -06005268 /* If this network has already had an association attempt within the
5269 * last 3 seconds, do not try and associate again... */
5270 if (network->last_associate &&
James Ketrenosea2b26e2005-08-24 21:25:16 -05005271 time_after(network->last_associate + (HZ * 3UL), jiffies)) {
James Ketrenos43f66a62005-03-25 12:31:53 -06005272 IPW_DEBUG_ASSOC("Network '%s (" MAC_FMT ")' excluded "
5273 "because of storming (%lu since last "
5274 "assoc attempt).\n",
5275 escape_essid(network->ssid, network->ssid_len),
5276 MAC_ARG(network->bssid),
5277 (jiffies - network->last_associate) / HZ);
5278 return 0;
5279 }
5280
5281 /* Now go through and see if the requested network is valid... */
Jeff Garzikbf794512005-07-31 13:07:26 -04005282 if (priv->ieee->scan_age != 0 &&
James Ketrenosea2b26e2005-08-24 21:25:16 -05005283 time_after(jiffies, network->last_scanned + priv->ieee->scan_age)) {
James Ketrenos43f66a62005-03-25 12:31:53 -06005284 IPW_DEBUG_ASSOC("Network '%s (" MAC_FMT ")' excluded "
5285 "because of age: %lums.\n",
5286 escape_essid(network->ssid, network->ssid_len),
5287 MAC_ARG(network->bssid),
5288 (jiffies - network->last_scanned) / (HZ / 100));
5289 return 0;
Jeff Garzikbf794512005-07-31 13:07:26 -04005290 }
James Ketrenos43f66a62005-03-25 12:31:53 -06005291
Jeff Garzikbf794512005-07-31 13:07:26 -04005292 if ((priv->config & CFG_STATIC_CHANNEL) &&
James Ketrenos43f66a62005-03-25 12:31:53 -06005293 (network->channel != priv->channel)) {
5294 IPW_DEBUG_ASSOC("Network '%s (" MAC_FMT ")' excluded "
5295 "because of channel mismatch: %d != %d.\n",
5296 escape_essid(network->ssid, network->ssid_len),
5297 MAC_ARG(network->bssid),
5298 network->channel, priv->channel);
5299 return 0;
5300 }
Jeff Garzikbf794512005-07-31 13:07:26 -04005301
James Ketrenos43f66a62005-03-25 12:31:53 -06005302 /* Verify privacy compatability */
Jeff Garzikbf794512005-07-31 13:07:26 -04005303 if (((priv->capability & CAP_PRIVACY_ON) ? 1 : 0) !=
James Ketrenos43f66a62005-03-25 12:31:53 -06005304 ((network->capability & WLAN_CAPABILITY_PRIVACY) ? 1 : 0)) {
5305 IPW_DEBUG_ASSOC("Network '%s (" MAC_FMT ")' excluded "
5306 "because of privacy mismatch: %s != %s.\n",
5307 escape_essid(network->ssid, network->ssid_len),
5308 MAC_ARG(network->bssid),
Jeff Garzikbf794512005-07-31 13:07:26 -04005309 priv->capability & CAP_PRIVACY_ON ? "on" :
James Ketrenos43f66a62005-03-25 12:31:53 -06005310 "off",
Jeff Garzikbf794512005-07-31 13:07:26 -04005311 network->capability &
Jeff Garzik0edd5b42005-09-07 00:48:31 -04005312 WLAN_CAPABILITY_PRIVACY ? "on" : "off");
James Ketrenos43f66a62005-03-25 12:31:53 -06005313 return 0;
5314 }
Jeff Garzikbf794512005-07-31 13:07:26 -04005315
5316 if ((priv->config & CFG_STATIC_BSSID) &&
James Ketrenos43f66a62005-03-25 12:31:53 -06005317 memcmp(network->bssid, priv->bssid, ETH_ALEN)) {
5318 IPW_DEBUG_ASSOC("Network '%s (" MAC_FMT ")' excluded "
5319 "because of BSSID mismatch: " MAC_FMT ".\n",
5320 escape_essid(network->ssid, network->ssid_len),
Jeff Garzik0edd5b42005-09-07 00:48:31 -04005321 MAC_ARG(network->bssid), MAC_ARG(priv->bssid));
James Ketrenos43f66a62005-03-25 12:31:53 -06005322 return 0;
5323 }
Jeff Garzikbf794512005-07-31 13:07:26 -04005324
James Ketrenos43f66a62005-03-25 12:31:53 -06005325 /* Filter out any incompatible freq / mode combinations */
5326 if (!ieee80211_is_valid_mode(priv->ieee, network->mode)) {
5327 IPW_DEBUG_ASSOC("Network '%s (" MAC_FMT ")' excluded "
5328 "because of invalid frequency/mode "
5329 "combination.\n",
5330 escape_essid(network->ssid, network->ssid_len),
5331 MAC_ARG(network->bssid));
5332 return 0;
5333 }
Jeff Garzikbf794512005-07-31 13:07:26 -04005334
James Ketrenosea2b26e2005-08-24 21:25:16 -05005335 /* Ensure that the rates supported by the driver are compatible with
5336 * this AP, including verification of basic rates (mandatory) */
5337 if (!ipw_compatible_rates(priv, network, &rates)) {
5338 IPW_DEBUG_ASSOC("Network '%s (" MAC_FMT ")' excluded "
5339 "because configured rate mask excludes "
5340 "AP mandatory rate.\n",
5341 escape_essid(network->ssid, network->ssid_len),
5342 MAC_ARG(network->bssid));
5343 return 0;
5344 }
5345
James Ketrenos43f66a62005-03-25 12:31:53 -06005346 if (rates.num_rates == 0) {
5347 IPW_DEBUG_ASSOC("Network '%s (" MAC_FMT ")' excluded "
5348 "because of no compatible rates.\n",
5349 escape_essid(network->ssid, network->ssid_len),
5350 MAC_ARG(network->bssid));
5351 return 0;
5352 }
Jeff Garzikbf794512005-07-31 13:07:26 -04005353
James Ketrenos43f66a62005-03-25 12:31:53 -06005354 /* TODO: Perform any further minimal comparititive tests. We do not
5355 * want to put too much policy logic here; intelligent scan selection
5356 * should occur within a generic IEEE 802.11 user space tool. */
5357
5358 /* Set up 'new' AP to this network */
5359 ipw_copy_rates(&match->rates, &rates);
5360 match->network = network;
5361
5362 IPW_DEBUG_ASSOC("Network '%s (" MAC_FMT ")' is a viable match.\n",
5363 escape_essid(network->ssid, network->ssid_len),
5364 MAC_ARG(network->bssid));
5365
5366 return 1;
5367}
5368
Jeff Garzikbf794512005-07-31 13:07:26 -04005369static void ipw_adhoc_create(struct ipw_priv *priv,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04005370 struct ieee80211_network *network)
James Ketrenos43f66a62005-03-25 12:31:53 -06005371{
5372 /*
5373 * For the purposes of scanning, we can set our wireless mode
5374 * to trigger scans across combinations of bands, but when it
5375 * comes to creating a new ad-hoc network, we have tell the FW
5376 * exactly which band to use.
5377 *
Jeff Garzikbf794512005-07-31 13:07:26 -04005378 * We also have the possibility of an invalid channel for the
James Ketrenos43f66a62005-03-25 12:31:53 -06005379 * chossen band. Attempting to create a new ad-hoc network
5380 * with an invalid channel for wireless mode will trigger a
5381 * FW fatal error.
5382 */
James Ketrenosb095c382005-08-24 22:04:42 -05005383 if (!ieee80211_is_valid_channel(priv->ieee, priv->channel)) {
5384 const struct ieee80211_geo *geo = ieee80211_get_geo(priv->ieee);
James Ketrenos43f66a62005-03-25 12:31:53 -06005385 IPW_WARNING("Overriding invalid channel\n");
5386 if (priv->ieee->mode & IEEE_A) {
5387 network->mode = IEEE_A;
James Ketrenosb095c382005-08-24 22:04:42 -05005388 priv->channel = geo->a[0].channel;
James Ketrenos43f66a62005-03-25 12:31:53 -06005389 } else if (priv->ieee->mode & IEEE_G) {
5390 network->mode = IEEE_G;
James Ketrenosb095c382005-08-24 22:04:42 -05005391 priv->channel = geo->bg[0].channel;
James Ketrenos43f66a62005-03-25 12:31:53 -06005392 } else {
5393 network->mode = IEEE_B;
James Ketrenosb095c382005-08-24 22:04:42 -05005394 priv->channel = geo->bg[0].channel;
James Ketrenos43f66a62005-03-25 12:31:53 -06005395 }
James Ketrenosb095c382005-08-24 22:04:42 -05005396 } else
5397 network->mode = priv->ieee->mode;
James Ketrenos43f66a62005-03-25 12:31:53 -06005398
5399 network->channel = priv->channel;
5400 priv->config |= CFG_ADHOC_PERSIST;
5401 ipw_create_bssid(priv, network->bssid);
5402 network->ssid_len = priv->essid_len;
5403 memcpy(network->ssid, priv->essid, priv->essid_len);
5404 memset(&network->stats, 0, sizeof(network->stats));
5405 network->capability = WLAN_CAPABILITY_IBSS;
James Ketrenosea2b26e2005-08-24 21:25:16 -05005406 if (!(priv->config & CFG_PREAMBLE_LONG))
5407 network->capability |= WLAN_CAPABILITY_SHORT_PREAMBLE;
James Ketrenos43f66a62005-03-25 12:31:53 -06005408 if (priv->capability & CAP_PRIVACY_ON)
5409 network->capability |= WLAN_CAPABILITY_PRIVACY;
5410 network->rates_len = min(priv->rates.num_rates, MAX_RATES_LENGTH);
Jeff Garzik0edd5b42005-09-07 00:48:31 -04005411 memcpy(network->rates, priv->rates.supported_rates, network->rates_len);
James Ketrenos43f66a62005-03-25 12:31:53 -06005412 network->rates_ex_len = priv->rates.num_rates - network->rates_len;
Jeff Garzikbf794512005-07-31 13:07:26 -04005413 memcpy(network->rates_ex,
James Ketrenos43f66a62005-03-25 12:31:53 -06005414 &priv->rates.supported_rates[network->rates_len],
5415 network->rates_ex_len);
5416 network->last_scanned = 0;
5417 network->flags = 0;
5418 network->last_associate = 0;
5419 network->time_stamp[0] = 0;
5420 network->time_stamp[1] = 0;
Jeff Garzik0edd5b42005-09-07 00:48:31 -04005421 network->beacon_interval = 100; /* Default */
5422 network->listen_interval = 10; /* Default */
5423 network->atim_window = 0; /* Default */
James Ketrenos43f66a62005-03-25 12:31:53 -06005424 network->wpa_ie_len = 0;
5425 network->rsn_ie_len = 0;
James Ketrenos43f66a62005-03-25 12:31:53 -06005426}
5427
James Ketrenosb095c382005-08-24 22:04:42 -05005428static void ipw_send_tgi_tx_key(struct ipw_priv *priv, int type, int index)
5429{
5430 struct ipw_tgi_tx_key *key;
5431 struct host_cmd cmd = {
5432 .cmd = IPW_CMD_TGI_TX_KEY,
5433 .len = sizeof(*key)
5434 };
5435
5436 if (!(priv->ieee->sec.flags & (1 << index)))
5437 return;
5438
5439 key = (struct ipw_tgi_tx_key *)&cmd.param;
5440 key->key_id = index;
5441 memcpy(key->key, priv->ieee->sec.keys[index], SCM_TEMPORAL_KEY_LENGTH);
5442 key->security_type = type;
5443 key->station_index = 0; /* always 0 for BSS */
5444 key->flags = 0;
5445 /* 0 for new key; previous value of counter (after fatal error) */
5446 key->tx_counter[0] = 0;
5447 key->tx_counter[1] = 0;
5448
5449 if (ipw_send_cmd(priv, &cmd)) {
5450 IPW_ERROR("failed to send TGI_TX_KEY command\n");
5451 return;
5452 }
5453}
5454
5455static void ipw_send_wep_keys(struct ipw_priv *priv, int type)
James Ketrenos43f66a62005-03-25 12:31:53 -06005456{
5457 struct ipw_wep_key *key;
5458 int i;
5459 struct host_cmd cmd = {
5460 .cmd = IPW_CMD_WEP_KEY,
5461 .len = sizeof(*key)
5462 };
5463
5464 key = (struct ipw_wep_key *)&cmd.param;
5465 key->cmd_id = DINO_CMD_WEP_KEY;
5466 key->seq_num = 0;
5467
James Ketrenosb095c382005-08-24 22:04:42 -05005468 /* Note: AES keys cannot be set for multiple times.
5469 * Only set it at the first time. */
Jeff Garzikbf794512005-07-31 13:07:26 -04005470 for (i = 0; i < 4; i++) {
James Ketrenosb095c382005-08-24 22:04:42 -05005471 key->key_index = i | type;
5472 if (!(priv->ieee->sec.flags & (1 << i))) {
James Ketrenos43f66a62005-03-25 12:31:53 -06005473 key->key_size = 0;
James Ketrenosb095c382005-08-24 22:04:42 -05005474 continue;
James Ketrenos43f66a62005-03-25 12:31:53 -06005475 }
5476
James Ketrenosb095c382005-08-24 22:04:42 -05005477 key->key_size = priv->ieee->sec.key_sizes[i];
5478 memcpy(key->key, priv->ieee->sec.keys[i], key->key_size);
5479
James Ketrenos43f66a62005-03-25 12:31:53 -06005480 if (ipw_send_cmd(priv, &cmd)) {
5481 IPW_ERROR("failed to send WEP_KEY command\n");
5482 return;
5483 }
Jeff Garzikbf794512005-07-31 13:07:26 -04005484 }
James Ketrenos43f66a62005-03-25 12:31:53 -06005485}
5486
James Ketrenosb095c382005-08-24 22:04:42 -05005487static void ipw_set_hwcrypto_keys(struct ipw_priv *priv)
5488{
5489 switch (priv->ieee->sec.level) {
5490 case SEC_LEVEL_3:
5491 if (priv->ieee->sec.flags & SEC_ACTIVE_KEY)
5492 ipw_send_tgi_tx_key(priv,
5493 DCT_FLAG_EXT_SECURITY_CCM,
5494 priv->ieee->sec.active_key);
5495 ipw_send_wep_keys(priv, DCW_WEP_KEY_SEC_TYPE_CCM);
5496
5497 priv->sys_config.disable_unicast_decryption = 0;
5498 priv->sys_config.disable_multicast_decryption = 0;
5499 priv->ieee->host_decrypt = 0;
5500 if (ipw_send_system_config(priv, &priv->sys_config))
5501 IPW_ERROR("ipw_send_system_config failed\n");
5502
5503 break;
5504 case SEC_LEVEL_2:
5505 if (priv->ieee->sec.flags & SEC_ACTIVE_KEY)
5506 ipw_send_tgi_tx_key(priv,
5507 DCT_FLAG_EXT_SECURITY_TKIP,
5508 priv->ieee->sec.active_key);
5509
5510 priv->sys_config.disable_unicast_decryption = 1;
5511 priv->sys_config.disable_multicast_decryption = 1;
5512 priv->ieee->host_decrypt = 1;
5513 if (ipw_send_system_config(priv, &priv->sys_config))
5514 IPW_ERROR("ipw_send_system_config failed\n");
5515
5516 break;
5517 case SEC_LEVEL_1:
5518 ipw_send_wep_keys(priv, DCW_WEP_KEY_SEC_TYPE_WEP);
5519
5520 priv->sys_config.disable_unicast_decryption = 0;
5521 priv->sys_config.disable_multicast_decryption = 0;
5522 priv->ieee->host_decrypt = 0;
5523 if (ipw_send_system_config(priv, &priv->sys_config))
5524 IPW_ERROR("ipw_send_system_config failed\n");
5525
5526 break;
5527 case SEC_LEVEL_0:
5528 default:
5529 break;
5530 }
5531}
5532
James Ketrenos43f66a62005-03-25 12:31:53 -06005533static void ipw_adhoc_check(void *data)
5534{
5535 struct ipw_priv *priv = data;
Jeff Garzikbf794512005-07-31 13:07:26 -04005536
James Ketrenos43f66a62005-03-25 12:31:53 -06005537 if (priv->missed_adhoc_beacons++ > priv->missed_beacon_threshold &&
5538 !(priv->config & CFG_ADHOC_PERSIST)) {
5539 IPW_DEBUG_SCAN("Disassociating due to missed beacons\n");
5540 ipw_remove_current_network(priv);
5541 ipw_disassociate(priv);
5542 return;
5543 }
5544
Jeff Garzikbf794512005-07-31 13:07:26 -04005545 queue_delayed_work(priv->workqueue, &priv->adhoc_check,
James Ketrenos43f66a62005-03-25 12:31:53 -06005546 priv->assoc_request.beacon_interval);
5547}
5548
James Ketrenosc848d0a2005-08-24 21:56:24 -05005549static void ipw_bg_adhoc_check(void *data)
5550{
5551 struct ipw_priv *priv = data;
5552 down(&priv->sem);
5553 ipw_adhoc_check(data);
5554 up(&priv->sem);
5555}
5556
James Ketrenos43f66a62005-03-25 12:31:53 -06005557#ifdef CONFIG_IPW_DEBUG
5558static void ipw_debug_config(struct ipw_priv *priv)
5559{
5560 IPW_DEBUG_INFO("Scan completed, no valid APs matched "
5561 "[CFG 0x%08X]\n", priv->config);
5562 if (priv->config & CFG_STATIC_CHANNEL)
Jeff Garzik0edd5b42005-09-07 00:48:31 -04005563 IPW_DEBUG_INFO("Channel locked to %d\n", priv->channel);
James Ketrenos43f66a62005-03-25 12:31:53 -06005564 else
5565 IPW_DEBUG_INFO("Channel unlocked.\n");
5566 if (priv->config & CFG_STATIC_ESSID)
Jeff Garzikbf794512005-07-31 13:07:26 -04005567 IPW_DEBUG_INFO("ESSID locked to '%s'\n",
Jeff Garzik0edd5b42005-09-07 00:48:31 -04005568 escape_essid(priv->essid, priv->essid_len));
James Ketrenos43f66a62005-03-25 12:31:53 -06005569 else
5570 IPW_DEBUG_INFO("ESSID unlocked.\n");
5571 if (priv->config & CFG_STATIC_BSSID)
James Ketrenosea2b26e2005-08-24 21:25:16 -05005572 IPW_DEBUG_INFO("BSSID locked to " MAC_FMT "\n",
5573 MAC_ARG(priv->bssid));
James Ketrenos43f66a62005-03-25 12:31:53 -06005574 else
5575 IPW_DEBUG_INFO("BSSID unlocked.\n");
5576 if (priv->capability & CAP_PRIVACY_ON)
5577 IPW_DEBUG_INFO("PRIVACY on\n");
5578 else
5579 IPW_DEBUG_INFO("PRIVACY off\n");
5580 IPW_DEBUG_INFO("RATE MASK: 0x%08X\n", priv->rates_mask);
5581}
5582#else
Jiri Benc8d45ff72005-08-25 20:09:39 -04005583#define ipw_debug_config(x) do {} while (0)
James Ketrenos43f66a62005-03-25 12:31:53 -06005584#endif
5585
James Ketrenosb095c382005-08-24 22:04:42 -05005586static inline void ipw_set_fixed_rate(struct ipw_priv *priv, int mode)
James Ketrenos43f66a62005-03-25 12:31:53 -06005587{
5588 /* TODO: Verify that this works... */
5589 struct ipw_fixed_rate fr = {
5590 .tx_rates = priv->rates_mask
5591 };
5592 u32 reg;
5593 u16 mask = 0;
5594
Jeff Garzikbf794512005-07-31 13:07:26 -04005595 /* Identify 'current FW band' and match it with the fixed
James Ketrenos43f66a62005-03-25 12:31:53 -06005596 * Tx rates */
Jeff Garzikbf794512005-07-31 13:07:26 -04005597
James Ketrenos43f66a62005-03-25 12:31:53 -06005598 switch (priv->ieee->freq_band) {
Jeff Garzik0edd5b42005-09-07 00:48:31 -04005599 case IEEE80211_52GHZ_BAND: /* A only */
James Ketrenos43f66a62005-03-25 12:31:53 -06005600 /* IEEE_A */
5601 if (priv->rates_mask & ~IEEE80211_OFDM_RATES_MASK) {
5602 /* Invalid fixed rate mask */
James Ketrenosea2b26e2005-08-24 21:25:16 -05005603 IPW_DEBUG_WX
5604 ("invalid fixed rate mask in ipw_set_fixed_rate\n");
James Ketrenos43f66a62005-03-25 12:31:53 -06005605 fr.tx_rates = 0;
5606 break;
5607 }
Jeff Garzikbf794512005-07-31 13:07:26 -04005608
James Ketrenos43f66a62005-03-25 12:31:53 -06005609 fr.tx_rates >>= IEEE80211_OFDM_SHIFT_MASK_A;
5610 break;
5611
Jeff Garzik0edd5b42005-09-07 00:48:31 -04005612 default: /* 2.4Ghz or Mixed */
James Ketrenos43f66a62005-03-25 12:31:53 -06005613 /* IEEE_B */
James Ketrenosb095c382005-08-24 22:04:42 -05005614 if (mode == IEEE_B) {
James Ketrenos43f66a62005-03-25 12:31:53 -06005615 if (fr.tx_rates & ~IEEE80211_CCK_RATES_MASK) {
5616 /* Invalid fixed rate mask */
James Ketrenosea2b26e2005-08-24 21:25:16 -05005617 IPW_DEBUG_WX
5618 ("invalid fixed rate mask in ipw_set_fixed_rate\n");
James Ketrenos43f66a62005-03-25 12:31:53 -06005619 fr.tx_rates = 0;
5620 }
5621 break;
Jeff Garzikbf794512005-07-31 13:07:26 -04005622 }
James Ketrenos43f66a62005-03-25 12:31:53 -06005623
5624 /* IEEE_G */
5625 if (fr.tx_rates & ~(IEEE80211_CCK_RATES_MASK |
5626 IEEE80211_OFDM_RATES_MASK)) {
5627 /* Invalid fixed rate mask */
James Ketrenosea2b26e2005-08-24 21:25:16 -05005628 IPW_DEBUG_WX
5629 ("invalid fixed rate mask in ipw_set_fixed_rate\n");
James Ketrenos43f66a62005-03-25 12:31:53 -06005630 fr.tx_rates = 0;
5631 break;
5632 }
5633
5634 if (IEEE80211_OFDM_RATE_6MB_MASK & fr.tx_rates) {
5635 mask |= (IEEE80211_OFDM_RATE_6MB_MASK >> 1);
5636 fr.tx_rates &= ~IEEE80211_OFDM_RATE_6MB_MASK;
5637 }
Jeff Garzikbf794512005-07-31 13:07:26 -04005638
James Ketrenos43f66a62005-03-25 12:31:53 -06005639 if (IEEE80211_OFDM_RATE_9MB_MASK & fr.tx_rates) {
5640 mask |= (IEEE80211_OFDM_RATE_9MB_MASK >> 1);
5641 fr.tx_rates &= ~IEEE80211_OFDM_RATE_9MB_MASK;
5642 }
Jeff Garzikbf794512005-07-31 13:07:26 -04005643
James Ketrenos43f66a62005-03-25 12:31:53 -06005644 if (IEEE80211_OFDM_RATE_12MB_MASK & fr.tx_rates) {
5645 mask |= (IEEE80211_OFDM_RATE_12MB_MASK >> 1);
5646 fr.tx_rates &= ~IEEE80211_OFDM_RATE_12MB_MASK;
5647 }
Jeff Garzikbf794512005-07-31 13:07:26 -04005648
James Ketrenos43f66a62005-03-25 12:31:53 -06005649 fr.tx_rates |= mask;
5650 break;
5651 }
5652
5653 reg = ipw_read32(priv, IPW_MEM_FIXED_OVERRIDE);
Jeff Garzik0edd5b42005-09-07 00:48:31 -04005654 ipw_write_reg32(priv, reg, *(u32 *) & fr);
James Ketrenos43f66a62005-03-25 12:31:53 -06005655}
5656
James Ketrenosea2b26e2005-08-24 21:25:16 -05005657static void ipw_abort_scan(struct ipw_priv *priv)
5658{
5659 int err;
5660
5661 if (priv->status & STATUS_SCAN_ABORTING) {
5662 IPW_DEBUG_HC("Ignoring concurrent scan abort request.\n");
5663 return;
5664 }
5665 priv->status |= STATUS_SCAN_ABORTING;
5666
5667 err = ipw_send_scan_abort(priv);
5668 if (err)
5669 IPW_DEBUG_HC("Request to abort scan failed.\n");
5670}
5671
5672static int ipw_request_scan(struct ipw_priv *priv)
5673{
5674 struct ipw_scan_request_ext scan;
5675 int channel_index = 0;
James Ketrenosb095c382005-08-24 22:04:42 -05005676 int i, err = 0, scan_type;
5677 const struct ieee80211_geo *geo;
5678#ifdef CONFIG_IPW2200_MONITOR
5679 u8 channel;
5680#endif
James Ketrenosea2b26e2005-08-24 21:25:16 -05005681
James Ketrenosb095c382005-08-24 22:04:42 -05005682 down(&priv->sem);
5683
5684 geo = ieee80211_get_geo(priv->ieee);
James Ketrenosea2b26e2005-08-24 21:25:16 -05005685
5686 if (priv->status & STATUS_SCANNING) {
James Ketrenosa613bff2005-08-24 21:43:11 -05005687 IPW_DEBUG_HC("Concurrent scan requested. Ignoring.\n");
James Ketrenosea2b26e2005-08-24 21:25:16 -05005688 priv->status |= STATUS_SCAN_PENDING;
James Ketrenosb095c382005-08-24 22:04:42 -05005689 goto done;
James Ketrenosea2b26e2005-08-24 21:25:16 -05005690 }
5691
5692 if (priv->status & STATUS_SCAN_ABORTING) {
5693 IPW_DEBUG_HC("Scan request while abort pending. Queuing.\n");
5694 priv->status |= STATUS_SCAN_PENDING;
James Ketrenosb095c382005-08-24 22:04:42 -05005695 goto done;
James Ketrenosea2b26e2005-08-24 21:25:16 -05005696 }
5697
5698 if (priv->status & STATUS_RF_KILL_MASK) {
5699 IPW_DEBUG_HC("Aborting scan due to RF Kill activation\n");
5700 priv->status |= STATUS_SCAN_PENDING;
James Ketrenosb095c382005-08-24 22:04:42 -05005701 goto done;
James Ketrenosea2b26e2005-08-24 21:25:16 -05005702 }
5703
5704 memset(&scan, 0, sizeof(scan));
5705
James Ketrenosb095c382005-08-24 22:04:42 -05005706 if (priv->config & CFG_SPEED_SCAN)
5707 scan.dwell_time[IPW_SCAN_ACTIVE_BROADCAST_SCAN] =
5708 cpu_to_le16(30);
5709 else
5710 scan.dwell_time[IPW_SCAN_ACTIVE_BROADCAST_SCAN] =
5711 cpu_to_le16(20);
5712
James Ketrenosa613bff2005-08-24 21:43:11 -05005713 scan.dwell_time[IPW_SCAN_ACTIVE_BROADCAST_AND_DIRECT_SCAN] =
5714 cpu_to_le16(20);
5715 scan.dwell_time[IPW_SCAN_PASSIVE_FULL_DWELL_SCAN] = cpu_to_le16(20);
James Ketrenosea2b26e2005-08-24 21:25:16 -05005716
James Ketrenosa613bff2005-08-24 21:43:11 -05005717 scan.full_scan_index = cpu_to_le32(ieee80211_get_scans(priv->ieee));
James Ketrenosea2b26e2005-08-24 21:25:16 -05005718
James Ketrenosb095c382005-08-24 22:04:42 -05005719#ifdef CONFIG_IPW2200_MONITOR
James Ketrenosea2b26e2005-08-24 21:25:16 -05005720 if (priv->ieee->iw_mode == IW_MODE_MONITOR) {
James Ketrenosb095c382005-08-24 22:04:42 -05005721 u8 band = 0;
James Ketrenosea2b26e2005-08-24 21:25:16 -05005722
James Ketrenosb095c382005-08-24 22:04:42 -05005723 switch (ieee80211_is_valid_channel(priv->ieee, priv->channel)) {
5724 case IEEE80211_52GHZ_BAND:
James Ketrenosea2b26e2005-08-24 21:25:16 -05005725 band = (u8) (IPW_A_MODE << 6) | 1;
James Ketrenosb095c382005-08-24 22:04:42 -05005726 channel = priv->channel;
5727 break;
James Ketrenosea2b26e2005-08-24 21:25:16 -05005728
James Ketrenosb095c382005-08-24 22:04:42 -05005729 case IEEE80211_24GHZ_BAND:
James Ketrenosea2b26e2005-08-24 21:25:16 -05005730 band = (u8) (IPW_B_MODE << 6) | 1;
James Ketrenosb095c382005-08-24 22:04:42 -05005731 channel = priv->channel;
5732 break;
James Ketrenosea2b26e2005-08-24 21:25:16 -05005733
James Ketrenosb095c382005-08-24 22:04:42 -05005734 default:
James Ketrenosea2b26e2005-08-24 21:25:16 -05005735 band = (u8) (IPW_B_MODE << 6) | 1;
5736 channel = 9;
James Ketrenosb095c382005-08-24 22:04:42 -05005737 break;
James Ketrenosea2b26e2005-08-24 21:25:16 -05005738 }
5739
James Ketrenosb095c382005-08-24 22:04:42 -05005740 scan.channels_list[0] = band;
5741 scan.channels_list[1] = channel;
5742 ipw_set_scan_type(&scan, 1, IPW_SCAN_PASSIVE_FULL_DWELL_SCAN);
James Ketrenosea2b26e2005-08-24 21:25:16 -05005743
James Ketrenosb095c382005-08-24 22:04:42 -05005744 /* NOTE: The card will sit on this channel for this time
5745 * period. Scan aborts are timing sensitive and frequently
5746 * result in firmware restarts. As such, it is best to
5747 * set a small dwell_time here and just keep re-issuing
5748 * scans. Otherwise fast channel hopping will not actually
5749 * hop channels.
5750 *
5751 * TODO: Move SPEED SCAN support to all modes and bands */
James Ketrenosa613bff2005-08-24 21:43:11 -05005752 scan.dwell_time[IPW_SCAN_PASSIVE_FULL_DWELL_SCAN] =
5753 cpu_to_le16(2000);
James Ketrenosea2b26e2005-08-24 21:25:16 -05005754 } else {
James Ketrenosb095c382005-08-24 22:04:42 -05005755#endif /* CONFIG_IPW2200_MONITOR */
5756 /* If we are roaming, then make this a directed scan for the
5757 * current network. Otherwise, ensure that every other scan
5758 * is a fast channel hop scan */
5759 if ((priv->status & STATUS_ROAMING)
5760 || (!(priv->status & STATUS_ASSOCIATED)
5761 && (priv->config & CFG_STATIC_ESSID)
5762 && (le32_to_cpu(scan.full_scan_index) % 2))) {
James Ketrenosea2b26e2005-08-24 21:25:16 -05005763 err = ipw_send_ssid(priv, priv->essid, priv->essid_len);
5764 if (err) {
James Ketrenosb095c382005-08-24 22:04:42 -05005765 IPW_DEBUG_HC("Attempt to send SSID command "
5766 "failed.\n");
5767 goto done;
James Ketrenosea2b26e2005-08-24 21:25:16 -05005768 }
5769
5770 scan_type = IPW_SCAN_ACTIVE_BROADCAST_AND_DIRECT_SCAN;
5771 } else {
5772 scan_type = IPW_SCAN_ACTIVE_BROADCAST_SCAN;
5773 }
5774
James Ketrenosb095c382005-08-24 22:04:42 -05005775 /* Add channels to the scan list */
James Ketrenosea2b26e2005-08-24 21:25:16 -05005776 if (priv->ieee->freq_band & IEEE80211_52GHZ_BAND) {
5777 int start = channel_index;
James Ketrenosb095c382005-08-24 22:04:42 -05005778 for (i = 0; i < geo->a_channels; i++) {
James Ketrenosea2b26e2005-08-24 21:25:16 -05005779 if ((priv->status & STATUS_ASSOCIATED) &&
James Ketrenosb095c382005-08-24 22:04:42 -05005780 geo->a[i].channel == priv->channel)
James Ketrenosea2b26e2005-08-24 21:25:16 -05005781 continue;
5782 channel_index++;
5783 scan.channels_list[channel_index] =
James Ketrenosb095c382005-08-24 22:04:42 -05005784 geo->a[i].channel;
James Ketrenosea2b26e2005-08-24 21:25:16 -05005785 ipw_set_scan_type(&scan, channel_index,
5786 scan_type);
5787 }
5788
5789 if (start != channel_index) {
5790 scan.channels_list[start] =
5791 (u8) (IPW_A_MODE << 6) | (channel_index -
5792 start);
5793 channel_index++;
5794 }
5795 }
5796
5797 if (priv->ieee->freq_band & IEEE80211_24GHZ_BAND) {
5798 int start = channel_index;
James Ketrenosb095c382005-08-24 22:04:42 -05005799 if (priv->config & CFG_SPEED_SCAN) {
5800 u8 channels[IEEE80211_24GHZ_CHANNELS] = {
5801 /* nop out the list */
5802 [0] = 0
5803 };
5804
5805 u8 channel;
5806 while (channel_index < IPW_SCAN_CHANNELS) {
5807 channel =
5808 priv->speed_scan[priv->
5809 speed_scan_pos];
5810 if (channel == 0) {
5811 priv->speed_scan_pos = 0;
5812 channel = priv->speed_scan[0];
5813 }
5814 if ((priv->status & STATUS_ASSOCIATED)
5815 && channel == priv->channel) {
5816 priv->speed_scan_pos++;
5817 continue;
5818 }
5819
5820 /* If this channel has already been
5821 * added in scan, break from loop
5822 * and this will be the first channel
5823 * in the next scan.
5824 */
5825 if (channels[channel - 1] != 0)
5826 break;
5827
5828 channels[channel - 1] = 1;
5829 priv->speed_scan_pos++;
5830 channel_index++;
5831 scan.channels_list[channel_index] =
5832 channel;
5833 ipw_set_scan_type(&scan, channel_index,
5834 scan_type);
5835 }
5836 } else {
5837 for (i = 0; i < geo->bg_channels; i++) {
5838 if ((priv->status & STATUS_ASSOCIATED)
5839 && geo->bg[i].channel ==
5840 priv->channel)
5841 continue;
5842 channel_index++;
5843 scan.channels_list[channel_index] =
5844 geo->bg[i].channel;
5845 ipw_set_scan_type(&scan, channel_index,
5846 scan_type);
5847 }
James Ketrenosea2b26e2005-08-24 21:25:16 -05005848 }
5849
5850 if (start != channel_index) {
5851 scan.channels_list[start] =
5852 (u8) (IPW_B_MODE << 6) | (channel_index -
5853 start);
5854 }
5855 }
James Ketrenosb095c382005-08-24 22:04:42 -05005856#ifdef CONFIG_IPW2200_MONITOR
James Ketrenosea2b26e2005-08-24 21:25:16 -05005857 }
5858#endif
5859
5860 err = ipw_send_scan_request_ext(priv, &scan);
5861 if (err) {
5862 IPW_DEBUG_HC("Sending scan command failed: %08X\n", err);
James Ketrenosb095c382005-08-24 22:04:42 -05005863 goto done;
James Ketrenosea2b26e2005-08-24 21:25:16 -05005864 }
5865
5866 priv->status |= STATUS_SCANNING;
5867 priv->status &= ~STATUS_SCAN_PENDING;
5868
James Ketrenosb095c382005-08-24 22:04:42 -05005869 done:
James Ketrenosc848d0a2005-08-24 21:56:24 -05005870 up(&priv->sem);
James Ketrenosb095c382005-08-24 22:04:42 -05005871 return err;
James Ketrenosc848d0a2005-08-24 21:56:24 -05005872}
5873
5874static void ipw_bg_abort_scan(void *data)
5875{
5876 struct ipw_priv *priv = data;
5877 down(&priv->sem);
5878 ipw_abort_scan(data);
5879 up(&priv->sem);
5880}
5881
James Ketrenosea2b26e2005-08-24 21:25:16 -05005882/* Support for wpa_supplicant. Will be replaced with WEXT once
5883 * they get WPA support. */
James Ketrenosea2b26e2005-08-24 21:25:16 -05005884
5885/* following definitions must match definitions in driver_ipw.c */
5886
5887#define IPW_IOCTL_WPA_SUPPLICANT SIOCIWFIRSTPRIV+30
5888
5889#define IPW_CMD_SET_WPA_PARAM 1
5890#define IPW_CMD_SET_WPA_IE 2
5891#define IPW_CMD_SET_ENCRYPTION 3
5892#define IPW_CMD_MLME 4
5893
5894#define IPW_PARAM_WPA_ENABLED 1
5895#define IPW_PARAM_TKIP_COUNTERMEASURES 2
5896#define IPW_PARAM_DROP_UNENCRYPTED 3
5897#define IPW_PARAM_PRIVACY_INVOKED 4
5898#define IPW_PARAM_AUTH_ALGS 5
5899#define IPW_PARAM_IEEE_802_1X 6
5900
5901#define IPW_MLME_STA_DEAUTH 1
5902#define IPW_MLME_STA_DISASSOC 2
5903
5904#define IPW_CRYPT_ERR_UNKNOWN_ALG 2
5905#define IPW_CRYPT_ERR_UNKNOWN_ADDR 3
5906#define IPW_CRYPT_ERR_CRYPT_INIT_FAILED 4
5907#define IPW_CRYPT_ERR_KEY_SET_FAILED 5
5908#define IPW_CRYPT_ERR_TX_KEY_SET_FAILED 6
5909#define IPW_CRYPT_ERR_CARD_CONF_FAILED 7
5910
5911#define IPW_CRYPT_ALG_NAME_LEN 16
5912
5913struct ipw_param {
5914 u32 cmd;
5915 u8 sta_addr[ETH_ALEN];
5916 union {
5917 struct {
5918 u8 name;
5919 u32 value;
5920 } wpa_param;
5921 struct {
5922 u32 len;
James Ketrenosb095c382005-08-24 22:04:42 -05005923 u8 reserved[32];
5924 u8 data[0];
James Ketrenosea2b26e2005-08-24 21:25:16 -05005925 } wpa_ie;
5926 struct {
5927 int command;
5928 int reason_code;
5929 } mlme;
5930 struct {
5931 u8 alg[IPW_CRYPT_ALG_NAME_LEN];
5932 u8 set_tx;
5933 u32 err;
5934 u8 idx;
5935 u8 seq[8]; /* sequence counter (set: RX, get: TX) */
5936 u16 key_len;
5937 u8 key[0];
5938 } crypt;
5939
5940 } u;
5941};
5942
5943/* end of driver_ipw.c code */
5944
5945static int ipw_wpa_enable(struct ipw_priv *priv, int value)
5946{
James Ketrenosb095c382005-08-24 22:04:42 -05005947 /* This is called when wpa_supplicant loads and closes the driver
5948 * interface. */
5949 return 0;
James Ketrenosea2b26e2005-08-24 21:25:16 -05005950}
5951
5952#define AUTH_ALG_OPEN_SYSTEM 0x1
5953#define AUTH_ALG_SHARED_KEY 0x2
5954
5955static int ipw_wpa_set_auth_algs(struct ipw_priv *priv, int value)
5956{
5957 struct ieee80211_device *ieee = priv->ieee;
5958 struct ieee80211_security sec = {
5959 .flags = SEC_AUTH_MODE,
5960 };
5961 int ret = 0;
5962
5963 if (value & AUTH_ALG_SHARED_KEY) {
5964 sec.auth_mode = WLAN_AUTH_SHARED_KEY;
5965 ieee->open_wep = 0;
5966 } else {
5967 sec.auth_mode = WLAN_AUTH_OPEN;
5968 ieee->open_wep = 1;
5969 }
5970
5971 if (ieee->set_security)
5972 ieee->set_security(ieee->dev, &sec);
5973 else
5974 ret = -EOPNOTSUPP;
5975
5976 return ret;
5977}
5978
5979static int ipw_wpa_set_param(struct net_device *dev, u8 name, u32 value)
5980{
5981 struct ipw_priv *priv = ieee80211_priv(dev);
James Ketrenosa613bff2005-08-24 21:43:11 -05005982 struct ieee80211_crypt_data *crypt;
5983 unsigned long flags;
James Ketrenosea2b26e2005-08-24 21:25:16 -05005984 int ret = 0;
5985
5986 switch (name) {
5987 case IPW_PARAM_WPA_ENABLED:
5988 ret = ipw_wpa_enable(priv, value);
5989 break;
5990
5991 case IPW_PARAM_TKIP_COUNTERMEASURES:
James Ketrenosa613bff2005-08-24 21:43:11 -05005992 crypt = priv->ieee->crypt[priv->ieee->tx_keyidx];
5993 if (!crypt || !crypt->ops->set_flags || !crypt->ops->get_flags) {
5994 IPW_WARNING("Can't set TKIP countermeasures: "
5995 "crypt not set!\n");
5996 break;
5997 }
5998
5999 flags = crypt->ops->get_flags(crypt->priv);
6000
6001 if (value)
6002 flags |= IEEE80211_CRYPTO_TKIP_COUNTERMEASURES;
6003 else
6004 flags &= ~IEEE80211_CRYPTO_TKIP_COUNTERMEASURES;
6005
6006 crypt->ops->set_flags(flags, crypt->priv);
6007
James Ketrenosea2b26e2005-08-24 21:25:16 -05006008 break;
6009
James Ketrenosb095c382005-08-24 22:04:42 -05006010 case IPW_PARAM_DROP_UNENCRYPTED:{
6011 /* HACK:
6012 *
6013 * wpa_supplicant calls set_wpa_enabled when the driver
6014 * is loaded and unloaded, regardless of if WPA is being
6015 * used. No other calls are made which can be used to
6016 * determine if encryption will be used or not prior to
6017 * association being expected. If encryption is not being
6018 * used, drop_unencrypted is set to false, else true -- we
6019 * can use this to determine if the CAP_PRIVACY_ON bit should
6020 * be set.
6021 */
6022 struct ieee80211_security sec = {
6023 .flags = SEC_ENABLED,
6024 .enabled = value,
6025 };
6026 priv->ieee->drop_unencrypted = value;
6027 /* We only change SEC_LEVEL for open mode. Others
6028 * are set by ipw_wpa_set_encryption.
6029 */
6030 if (!value) {
6031 sec.flags |= SEC_LEVEL;
6032 sec.level = SEC_LEVEL_0;
6033 } else {
6034 sec.flags |= SEC_LEVEL;
6035 sec.level = SEC_LEVEL_1;
6036 }
6037 if (priv->ieee->set_security)
6038 priv->ieee->set_security(priv->ieee->dev, &sec);
6039 break;
6040 }
James Ketrenosea2b26e2005-08-24 21:25:16 -05006041
6042 case IPW_PARAM_PRIVACY_INVOKED:
6043 priv->ieee->privacy_invoked = value;
6044 break;
6045
6046 case IPW_PARAM_AUTH_ALGS:
6047 ret = ipw_wpa_set_auth_algs(priv, value);
6048 break;
6049
6050 case IPW_PARAM_IEEE_802_1X:
6051 priv->ieee->ieee802_1x = value;
6052 break;
6053
6054 default:
6055 IPW_ERROR("%s: Unknown WPA param: %d\n", dev->name, name);
6056 ret = -EOPNOTSUPP;
6057 }
6058
6059 return ret;
6060}
6061
6062static int ipw_wpa_mlme(struct net_device *dev, int command, int reason)
6063{
6064 struct ipw_priv *priv = ieee80211_priv(dev);
6065 int ret = 0;
6066
6067 switch (command) {
6068 case IPW_MLME_STA_DEAUTH:
6069 // silently ignore
6070 break;
6071
6072 case IPW_MLME_STA_DISASSOC:
6073 ipw_disassociate(priv);
6074 break;
6075
6076 default:
6077 IPW_ERROR("%s: Unknown MLME request: %d\n", dev->name, command);
6078 ret = -EOPNOTSUPP;
6079 }
6080
6081 return ret;
6082}
6083
6084static int ipw_set_rsn_capa(struct ipw_priv *priv,
6085 char *capabilities, int length)
6086{
6087 struct host_cmd cmd = {
6088 .cmd = IPW_CMD_RSN_CAPABILITIES,
6089 .len = length,
6090 };
6091
6092 IPW_DEBUG_HC("HOST_CMD_RSN_CAPABILITIES\n");
6093
6094 memcpy(&cmd.param, capabilities, length);
6095 if (ipw_send_cmd(priv, &cmd)) {
6096 IPW_ERROR("failed to send HOST_CMD_RSN_CAPABILITIES command\n");
6097 return -1;
6098 }
6099 return 0;
6100}
6101
6102void ipw_wpa_assoc_frame(struct ipw_priv *priv, char *wpa_ie, int wpa_ie_len)
6103{
6104 /* make sure WPA is enabled */
6105 ipw_wpa_enable(priv, 1);
6106
James Ketrenosc848d0a2005-08-24 21:56:24 -05006107 ipw_disassociate(priv);
James Ketrenosea2b26e2005-08-24 21:25:16 -05006108}
6109
6110static int ipw_wpa_set_wpa_ie(struct net_device *dev,
6111 struct ipw_param *param, int plen)
6112{
6113 struct ipw_priv *priv = ieee80211_priv(dev);
6114 struct ieee80211_device *ieee = priv->ieee;
6115 u8 *buf;
6116
James Ketrenosea2b26e2005-08-24 21:25:16 -05006117 if (param->u.wpa_ie.len > MAX_WPA_IE_LEN ||
6118 (param->u.wpa_ie.len && param->u.wpa_ie.data == NULL))
6119 return -EINVAL;
6120
6121 if (param->u.wpa_ie.len) {
6122 buf = kmalloc(param->u.wpa_ie.len, GFP_KERNEL);
6123 if (buf == NULL)
6124 return -ENOMEM;
6125
6126 memcpy(buf, param->u.wpa_ie.data, param->u.wpa_ie.len);
6127 kfree(ieee->wpa_ie);
6128 ieee->wpa_ie = buf;
6129 ieee->wpa_ie_len = param->u.wpa_ie.len;
6130 } else {
6131 kfree(ieee->wpa_ie);
6132 ieee->wpa_ie = NULL;
6133 ieee->wpa_ie_len = 0;
6134 }
6135
6136 ipw_wpa_assoc_frame(priv, ieee->wpa_ie, ieee->wpa_ie_len);
6137 return 0;
6138}
6139
6140/* implementation borrowed from hostap driver */
6141
6142static int ipw_wpa_set_encryption(struct net_device *dev,
6143 struct ipw_param *param, int param_len)
6144{
6145 int ret = 0;
6146 struct ipw_priv *priv = ieee80211_priv(dev);
6147 struct ieee80211_device *ieee = priv->ieee;
6148 struct ieee80211_crypto_ops *ops;
6149 struct ieee80211_crypt_data **crypt;
6150
6151 struct ieee80211_security sec = {
6152 .flags = 0,
6153 };
6154
6155 param->u.crypt.err = 0;
6156 param->u.crypt.alg[IPW_CRYPT_ALG_NAME_LEN - 1] = '\0';
6157
6158 if (param_len !=
6159 (int)((char *)param->u.crypt.key - (char *)param) +
6160 param->u.crypt.key_len) {
6161 IPW_DEBUG_INFO("Len mismatch %d, %d\n", param_len,
6162 param->u.crypt.key_len);
6163 return -EINVAL;
6164 }
6165 if (param->sta_addr[0] == 0xff && param->sta_addr[1] == 0xff &&
6166 param->sta_addr[2] == 0xff && param->sta_addr[3] == 0xff &&
6167 param->sta_addr[4] == 0xff && param->sta_addr[5] == 0xff) {
6168 if (param->u.crypt.idx >= WEP_KEYS)
6169 return -EINVAL;
6170 crypt = &ieee->crypt[param->u.crypt.idx];
6171 } else {
6172 return -EINVAL;
6173 }
6174
6175 if (strcmp(param->u.crypt.alg, "none") == 0) {
6176 if (crypt) {
6177 sec.enabled = 0;
James Ketrenosb095c382005-08-24 22:04:42 -05006178 sec.encrypt = 0;
James Ketrenosea2b26e2005-08-24 21:25:16 -05006179 sec.level = SEC_LEVEL_0;
6180 sec.flags |= SEC_ENABLED | SEC_LEVEL;
6181 ieee80211_crypt_delayed_deinit(ieee, crypt);
6182 }
6183 goto done;
6184 }
6185 sec.enabled = 1;
James Ketrenosb095c382005-08-24 22:04:42 -05006186 sec.encrypt = 1;
James Ketrenosea2b26e2005-08-24 21:25:16 -05006187 sec.flags |= SEC_ENABLED;
6188
James Ketrenosb095c382005-08-24 22:04:42 -05006189 /* IPW HW cannot build TKIP MIC, host decryption still needed. */
6190 if (!(ieee->host_encrypt || ieee->host_decrypt) &&
6191 strcmp(param->u.crypt.alg, "TKIP"))
6192 goto skip_host_crypt;
6193
James Ketrenosea2b26e2005-08-24 21:25:16 -05006194 ops = ieee80211_get_crypto_ops(param->u.crypt.alg);
6195 if (ops == NULL && strcmp(param->u.crypt.alg, "WEP") == 0) {
6196 request_module("ieee80211_crypt_wep");
6197 ops = ieee80211_get_crypto_ops(param->u.crypt.alg);
6198 } else if (ops == NULL && strcmp(param->u.crypt.alg, "TKIP") == 0) {
6199 request_module("ieee80211_crypt_tkip");
6200 ops = ieee80211_get_crypto_ops(param->u.crypt.alg);
6201 } else if (ops == NULL && strcmp(param->u.crypt.alg, "CCMP") == 0) {
6202 request_module("ieee80211_crypt_ccmp");
6203 ops = ieee80211_get_crypto_ops(param->u.crypt.alg);
6204 }
6205 if (ops == NULL) {
6206 IPW_DEBUG_INFO("%s: unknown crypto alg '%s'\n",
6207 dev->name, param->u.crypt.alg);
6208 param->u.crypt.err = IPW_CRYPT_ERR_UNKNOWN_ALG;
6209 ret = -EINVAL;
6210 goto done;
6211 }
6212
6213 if (*crypt == NULL || (*crypt)->ops != ops) {
6214 struct ieee80211_crypt_data *new_crypt;
6215
6216 ieee80211_crypt_delayed_deinit(ieee, crypt);
6217
6218 new_crypt = (struct ieee80211_crypt_data *)
6219 kmalloc(sizeof(*new_crypt), GFP_KERNEL);
6220 if (new_crypt == NULL) {
6221 ret = -ENOMEM;
6222 goto done;
6223 }
6224 memset(new_crypt, 0, sizeof(struct ieee80211_crypt_data));
6225 new_crypt->ops = ops;
6226 if (new_crypt->ops && try_module_get(new_crypt->ops->owner))
6227 new_crypt->priv =
6228 new_crypt->ops->init(param->u.crypt.idx);
6229
6230 if (new_crypt->priv == NULL) {
6231 kfree(new_crypt);
6232 param->u.crypt.err = IPW_CRYPT_ERR_CRYPT_INIT_FAILED;
6233 ret = -EINVAL;
6234 goto done;
6235 }
6236
6237 *crypt = new_crypt;
6238 }
6239
6240 if (param->u.crypt.key_len > 0 && (*crypt)->ops->set_key &&
6241 (*crypt)->ops->set_key(param->u.crypt.key,
6242 param->u.crypt.key_len, param->u.crypt.seq,
6243 (*crypt)->priv) < 0) {
6244 IPW_DEBUG_INFO("%s: key setting failed\n", dev->name);
6245 param->u.crypt.err = IPW_CRYPT_ERR_KEY_SET_FAILED;
6246 ret = -EINVAL;
6247 goto done;
6248 }
6249
James Ketrenosb095c382005-08-24 22:04:42 -05006250 skip_host_crypt:
James Ketrenosea2b26e2005-08-24 21:25:16 -05006251 if (param->u.crypt.set_tx) {
6252 ieee->tx_keyidx = param->u.crypt.idx;
6253 sec.active_key = param->u.crypt.idx;
6254 sec.flags |= SEC_ACTIVE_KEY;
James Ketrenosb095c382005-08-24 22:04:42 -05006255 } else
6256 sec.flags &= ~SEC_ACTIVE_KEY;
James Ketrenosea2b26e2005-08-24 21:25:16 -05006257
James Ketrenosb095c382005-08-24 22:04:42 -05006258 if (param->u.crypt.alg != NULL) {
6259 memcpy(sec.keys[param->u.crypt.idx],
6260 param->u.crypt.key, param->u.crypt.key_len);
6261 sec.key_sizes[param->u.crypt.idx] = param->u.crypt.key_len;
6262 sec.flags |= (1 << param->u.crypt.idx);
6263
6264 if (strcmp(param->u.crypt.alg, "WEP") == 0) {
James Ketrenosea2b26e2005-08-24 21:25:16 -05006265 sec.flags |= SEC_LEVEL;
6266 sec.level = SEC_LEVEL_1;
James Ketrenosb095c382005-08-24 22:04:42 -05006267 } else if (strcmp(param->u.crypt.alg, "TKIP") == 0) {
James Ketrenosea2b26e2005-08-24 21:25:16 -05006268 sec.flags |= SEC_LEVEL;
6269 sec.level = SEC_LEVEL_2;
James Ketrenosb095c382005-08-24 22:04:42 -05006270 } else if (strcmp(param->u.crypt.alg, "CCMP") == 0) {
James Ketrenosea2b26e2005-08-24 21:25:16 -05006271 sec.flags |= SEC_LEVEL;
6272 sec.level = SEC_LEVEL_3;
6273 }
6274 }
6275 done:
6276 if (ieee->set_security)
6277 ieee->set_security(ieee->dev, &sec);
6278
6279 /* Do not reset port if card is in Managed mode since resetting will
6280 * generate new IEEE 802.11 authentication which may end up in looping
6281 * with IEEE 802.1X. If your hardware requires a reset after WEP
6282 * configuration (for example... Prism2), implement the reset_port in
6283 * the callbacks structures used to initialize the 802.11 stack. */
6284 if (ieee->reset_on_keychange &&
6285 ieee->iw_mode != IW_MODE_INFRA &&
6286 ieee->reset_port && ieee->reset_port(dev)) {
6287 IPW_DEBUG_INFO("%s: reset_port failed\n", dev->name);
6288 param->u.crypt.err = IPW_CRYPT_ERR_CARD_CONF_FAILED;
6289 return -EINVAL;
6290 }
6291
6292 return ret;
6293}
6294
6295static int ipw_wpa_supplicant(struct net_device *dev, struct iw_point *p)
6296{
6297 struct ipw_param *param;
6298 int ret = 0;
6299
6300 IPW_DEBUG_INFO("wpa_supplicant: len=%d\n", p->length);
6301
6302 if (p->length < sizeof(struct ipw_param) || !p->pointer)
6303 return -EINVAL;
6304
6305 param = (struct ipw_param *)kmalloc(p->length, GFP_KERNEL);
6306 if (param == NULL)
6307 return -ENOMEM;
6308
6309 if (copy_from_user(param, p->pointer, p->length)) {
6310 kfree(param);
6311 return -EFAULT;
6312 }
6313
6314 switch (param->cmd) {
6315
6316 case IPW_CMD_SET_WPA_PARAM:
6317 ret = ipw_wpa_set_param(dev, param->u.wpa_param.name,
6318 param->u.wpa_param.value);
6319 break;
6320
6321 case IPW_CMD_SET_WPA_IE:
6322 ret = ipw_wpa_set_wpa_ie(dev, param, p->length);
6323 break;
6324
6325 case IPW_CMD_SET_ENCRYPTION:
6326 ret = ipw_wpa_set_encryption(dev, param, p->length);
6327 break;
6328
6329 case IPW_CMD_MLME:
6330 ret = ipw_wpa_mlme(dev, param->u.mlme.command,
6331 param->u.mlme.reason_code);
6332 break;
6333
6334 default:
6335 IPW_ERROR("%s: Unknown WPA supplicant request: %d\n",
6336 dev->name, param->cmd);
6337 ret = -EOPNOTSUPP;
6338 }
6339
6340 if (ret == 0 && copy_to_user(p->pointer, param, p->length))
6341 ret = -EFAULT;
6342
6343 kfree(param);
6344 return ret;
6345}
James Ketrenosea2b26e2005-08-24 21:25:16 -05006346
James Ketrenosb095c382005-08-24 22:04:42 -05006347#ifdef CONFIG_IPW_QOS
6348
6349/* QoS */
6350/*
6351* get the modulation type of the current network or
6352* the card current mode
6353*/
6354u8 ipw_qos_current_mode(struct ipw_priv * priv)
6355{
6356 u8 mode = 0;
6357
6358 if (priv->status & STATUS_ASSOCIATED) {
6359 unsigned long flags;
6360
6361 spin_lock_irqsave(&priv->ieee->lock, flags);
6362 mode = priv->assoc_network->mode;
6363 spin_unlock_irqrestore(&priv->ieee->lock, flags);
6364 } else {
6365 mode = priv->ieee->mode;
6366 }
6367 IPW_DEBUG_QOS("QoS network/card mode %d \n", mode);
6368 return mode;
6369}
6370
6371/*
6372* Handle management frame beacon and probe response
6373*/
6374static int ipw_qos_handle_probe_reponse(struct ipw_priv *priv,
6375 int active_network,
6376 struct ieee80211_network *network)
6377{
6378 u32 size = sizeof(struct ieee80211_qos_parameters);
6379
6380 if ((network->capability & WLAN_CAPABILITY_IBSS))
6381 network->qos_data.active = network->qos_data.supported;
6382
6383 if (network->flags & NETWORK_HAS_QOS_MASK) {
6384 if (active_network
6385 && (network->flags & NETWORK_HAS_QOS_PARAMETERS))
6386 network->qos_data.active = network->qos_data.supported;
6387
6388 if ((network->qos_data.active == 1) && (active_network == 1) &&
6389 (network->flags & NETWORK_HAS_QOS_PARAMETERS) &&
6390 (network->qos_data.old_param_count !=
6391 network->qos_data.param_count)) {
6392 network->qos_data.old_param_count =
6393 network->qos_data.param_count;
6394 schedule_work(&priv->qos_activate);
6395 IPW_DEBUG_QOS
6396 ("QoS parameters change call qos_activate\n");
6397 }
6398 } else {
6399 if ((priv->ieee->mode == IEEE_B) || (network->mode == IEEE_B)) {
6400 memcpy(&(network->qos_data.parameters),
6401 &def_parameters_CCK, size);
6402 } else {
6403 memcpy(&(network->qos_data.parameters),
6404 &def_parameters_OFDM, size);
6405 }
6406 if ((network->qos_data.active == 1) && (active_network == 1)) {
6407 IPW_DEBUG_QOS("QoS was disabled call qos_activate \n");
6408 schedule_work(&priv->qos_activate);
6409 }
6410
6411 network->qos_data.active = 0;
6412 network->qos_data.supported = 0;
6413 }
6414 if ((priv->status & STATUS_ASSOCIATED)
6415 && (priv->ieee->iw_mode == IW_MODE_ADHOC)
6416 && (active_network == 0)) {
6417
6418 if (memcmp(network->bssid, priv->bssid, ETH_ALEN)) {
6419 if ((network->capability & WLAN_CAPABILITY_IBSS)
6420 && !(network->flags & NETWORK_EMPTY_ESSID)) {
6421 if ((network->ssid_len ==
6422 priv->assoc_network->ssid_len)
6423 && !memcmp(network->ssid,
6424 priv->assoc_network->ssid,
6425 network->ssid_len)) {
6426 queue_work(priv->workqueue,
6427 &priv->merge_networks);
6428 }
6429
6430 }
6431 }
6432 }
6433
6434 return 0;
6435}
6436
6437/*
6438* This function set up the firmware to support QoS. It sends
6439* IPW_CMD_QOS_PARAMETERS and IPW_CMD_WME_INFO
6440*/
6441static int ipw_qos_activate(struct ipw_priv *priv,
6442 struct ieee80211_qos_data *qos_network_data)
6443{
6444 int err;
6445 struct ieee80211_qos_parameters qos_parameters[QOS_QOS_SETS];
6446 struct ieee80211_qos_parameters *active_one = NULL;
6447 u32 size = sizeof(struct ieee80211_qos_parameters);
6448 u32 burst_duration;
6449 int i;
6450 u8 type;
6451
6452 type = ipw_qos_current_mode(priv);
6453
6454 active_one = &(qos_parameters[QOS_PARAM_SET_DEF_CCK]);
6455 memcpy(active_one, priv->qos_data.def_qos_parm_CCK, size);
6456 active_one = &(qos_parameters[QOS_PARAM_SET_DEF_OFDM]);
6457 memcpy(active_one, priv->qos_data.def_qos_parm_OFDM, size);
6458
6459 if (qos_network_data == NULL) {
6460 if (type == IEEE_B) {
6461 IPW_DEBUG_QOS("QoS activate network mode %d\n", type);
6462 active_one = &def_parameters_CCK;
6463 } else
6464 active_one = &def_parameters_OFDM;
6465
6466 memcpy(&(qos_parameters[QOS_PARAM_SET_ACTIVE]), active_one,
6467 size);
6468 burst_duration = ipw_qos_get_burst_duration(priv);
6469 for (i = 0; i < QOS_QUEUE_NUM; i++)
6470 qos_parameters[QOS_PARAM_SET_ACTIVE].
6471 tx_op_limit[i] = (u16) burst_duration;
6472 } else if ((priv->ieee->iw_mode == IW_MODE_ADHOC)) {
6473 if (type == IEEE_B) {
6474 IPW_DEBUG_QOS("QoS activate IBSS nework mode %d\n",
6475 type);
6476 if (priv->qos_data.qos_enable == 0)
6477 active_one = &def_parameters_CCK;
6478 else
6479 active_one = priv->qos_data.def_qos_parm_CCK;
6480 } else {
6481 if (priv->qos_data.qos_enable == 0)
6482 active_one = &def_parameters_OFDM;
6483 else
6484 active_one = priv->qos_data.def_qos_parm_OFDM;
6485 }
6486 memcpy(&(qos_parameters[QOS_PARAM_SET_ACTIVE]), active_one,
6487 size);
6488 } else {
6489 unsigned long flags;
6490 int active;
6491
6492 spin_lock_irqsave(&priv->ieee->lock, flags);
6493 active_one = &(qos_network_data->parameters);
6494 qos_network_data->old_param_count =
6495 qos_network_data->param_count;
6496 memcpy(&(qos_parameters[QOS_PARAM_SET_ACTIVE]), active_one,
6497 size);
6498 active = qos_network_data->supported;
6499 spin_unlock_irqrestore(&priv->ieee->lock, flags);
6500
6501 if (active == 0) {
6502 burst_duration = ipw_qos_get_burst_duration(priv);
6503 for (i = 0; i < QOS_QUEUE_NUM; i++)
6504 qos_parameters[QOS_PARAM_SET_ACTIVE].
6505 tx_op_limit[i] = (u16) burst_duration;
6506 }
6507 }
6508
6509 IPW_DEBUG_QOS("QoS sending IPW_CMD_QOS_PARAMETERS\n");
6510 err =
6511 ipw_send_qos_params_command(priv,
6512 (struct ieee80211_qos_parameters *)
6513 &(qos_parameters[0]));
6514 if (err)
6515 IPW_DEBUG_QOS("QoS IPW_CMD_QOS_PARAMETERS failed\n");
6516
6517 return err;
6518}
6519
6520/*
6521* send IPW_CMD_WME_INFO to the firmware
6522*/
6523static int ipw_qos_set_info_element(struct ipw_priv *priv)
6524{
6525 int ret = 0;
6526 struct ieee80211_qos_information_element qos_info;
6527
6528 if (priv == NULL)
6529 return -1;
6530
6531 qos_info.elementID = QOS_ELEMENT_ID;
6532 qos_info.length = sizeof(struct ieee80211_qos_information_element) - 2;
6533
6534 qos_info.version = QOS_VERSION_1;
6535 qos_info.ac_info = 0;
6536
6537 memcpy(qos_info.qui, qos_oui, QOS_OUI_LEN);
6538 qos_info.qui_type = QOS_OUI_TYPE;
6539 qos_info.qui_subtype = QOS_OUI_INFO_SUB_TYPE;
6540
6541 ret = ipw_send_qos_info_command(priv, &qos_info);
6542 if (ret != 0) {
6543 IPW_DEBUG_QOS("QoS error calling ipw_send_qos_info_command\n");
6544 }
6545 return ret;
6546}
6547
6548/*
6549* Set the QoS parameter with the association request structure
6550*/
6551static int ipw_qos_association(struct ipw_priv *priv,
6552 struct ieee80211_network *network)
6553{
6554 int err = 0;
6555 struct ieee80211_qos_data *qos_data = NULL;
6556 struct ieee80211_qos_data ibss_data = {
6557 .supported = 1,
6558 .active = 1,
6559 };
6560
6561 switch (priv->ieee->iw_mode) {
6562 case IW_MODE_ADHOC:
6563 if (!(network->capability & WLAN_CAPABILITY_IBSS))
6564 BUG();
6565
6566 qos_data = &ibss_data;
6567 break;
6568
6569 case IW_MODE_INFRA:
6570 qos_data = &network->qos_data;
6571 break;
6572
6573 default:
6574 BUG();
6575 break;
6576 }
6577
6578 err = ipw_qos_activate(priv, qos_data);
6579 if (err) {
6580 priv->assoc_request.policy_support &= ~HC_QOS_SUPPORT_ASSOC;
6581 return err;
6582 }
6583
6584 if (priv->qos_data.qos_enable && qos_data->supported) {
6585 IPW_DEBUG_QOS("QoS will be enabled for this association\n");
6586 priv->assoc_request.policy_support |= HC_QOS_SUPPORT_ASSOC;
6587 return ipw_qos_set_info_element(priv);
6588 }
6589
6590 return 0;
6591}
6592
6593/*
6594* handling the beaconing responces. if we get different QoS setting
6595* of the network from the the associated setting adjust the QoS
6596* setting
6597*/
6598static int ipw_qos_association_resp(struct ipw_priv *priv,
6599 struct ieee80211_network *network)
6600{
6601 int ret = 0;
6602 unsigned long flags;
6603 u32 size = sizeof(struct ieee80211_qos_parameters);
6604 int set_qos_param = 0;
6605
6606 if ((priv == NULL) || (network == NULL)
6607 || (priv->assoc_network == NULL))
6608
6609 return ret;
6610
6611 if (!(priv->status & STATUS_ASSOCIATED))
6612 return ret;
6613
6614 if ((priv->ieee->iw_mode != IW_MODE_INFRA)) {
6615 return ret;
6616 }
6617
6618 spin_lock_irqsave(&priv->ieee->lock, flags);
6619 if (network->flags & NETWORK_HAS_QOS_PARAMETERS) {
6620 memcpy(&(priv->assoc_network->qos_data), &(network->qos_data),
6621 sizeof(struct ieee80211_qos_data));
6622 priv->assoc_network->qos_data.active = 1;
6623 if ((network->qos_data.old_param_count !=
6624 network->qos_data.param_count)) {
6625 set_qos_param = 1;
6626 network->qos_data.old_param_count =
6627 network->qos_data.param_count;
6628 }
6629
6630 } else {
6631 if ((network->mode == IEEE_B) || (priv->ieee->mode == IEEE_B)) {
6632 memcpy(&(priv->assoc_network->qos_data.parameters),
6633 &def_parameters_CCK, size);
6634 } else {
6635 memcpy(&(priv->assoc_network->qos_data.parameters),
6636 &def_parameters_OFDM, size);
6637 }
6638 priv->assoc_network->qos_data.active = 0;
6639 priv->assoc_network->qos_data.supported = 0;
6640 set_qos_param = 1;
6641 }
6642
6643 spin_unlock_irqrestore(&priv->ieee->lock, flags);
6644
6645 if (set_qos_param == 1)
6646 schedule_work(&priv->qos_activate);
6647
6648 return ret;
6649}
6650
6651static u32 ipw_qos_get_burst_duration(struct ipw_priv *priv)
6652{
6653 u32 ret = 0;
6654
6655 if ((priv == NULL))
6656 return 0;
6657
6658 if (!(priv->ieee->modulation & IEEE80211_OFDM_MODULATION)) {
6659 ret = priv->qos_data.burst_duration_CCK;
6660 } else {
6661 ret = priv->qos_data.burst_duration_OFDM;
6662 }
6663 return ret;
6664}
6665
6666/*
6667* Initialize the setting of QoS global
6668*/
6669static void ipw_qos_init(struct ipw_priv *priv, int enable,
6670 int burst_enable, u32 burst_duration_CCK,
6671 u32 burst_duration_OFDM)
6672{
6673 priv->qos_data.qos_enable = enable;
6674
6675 if (priv->qos_data.qos_enable) {
6676 priv->qos_data.def_qos_parm_CCK = &def_qos_parameters_CCK;
6677 priv->qos_data.def_qos_parm_OFDM = &def_qos_parameters_OFDM;
6678 IPW_DEBUG_QOS("QoS is enabled\n");
6679 } else {
6680 priv->qos_data.def_qos_parm_CCK = &def_parameters_CCK;
6681 priv->qos_data.def_qos_parm_OFDM = &def_parameters_OFDM;
6682 IPW_DEBUG_QOS("QoS is not enabled\n");
6683 }
6684
6685 priv->qos_data.burst_enable = burst_enable;
6686
6687 if (burst_enable) {
6688 priv->qos_data.burst_duration_CCK = burst_duration_CCK;
6689 priv->qos_data.burst_duration_OFDM = burst_duration_OFDM;
6690 } else {
6691 priv->qos_data.burst_duration_CCK = 0;
6692 priv->qos_data.burst_duration_OFDM = 0;
6693 }
6694}
6695
6696/*
6697* map the packet priority to the right TX Queue
6698*/
6699static int ipw_get_tx_queue_number(struct ipw_priv *priv, u16 priority)
6700{
6701 if (priority > 7 || !priv->qos_data.qos_enable)
6702 priority = 0;
6703
6704 return from_priority_to_tx_queue[priority] - 1;
6705}
6706
6707/*
6708* add QoS parameter to the TX command
6709*/
6710static int ipw_qos_set_tx_queue_command(struct ipw_priv *priv,
6711 u16 priority,
6712 struct tfd_data *tfd, u8 unicast)
6713{
6714 int ret = 0;
6715 int tx_queue_id = 0;
6716 struct ieee80211_qos_data *qos_data = NULL;
6717 int active, supported;
6718 unsigned long flags;
6719
6720 if (!(priv->status & STATUS_ASSOCIATED))
6721 return 0;
6722
6723 qos_data = &priv->assoc_network->qos_data;
6724
6725 spin_lock_irqsave(&priv->ieee->lock, flags);
6726
6727 if (priv->ieee->iw_mode == IW_MODE_ADHOC) {
6728 if (unicast == 0)
6729 qos_data->active = 0;
6730 else
6731 qos_data->active = qos_data->supported;
6732 }
6733
6734 active = qos_data->active;
6735 supported = qos_data->supported;
6736
6737 spin_unlock_irqrestore(&priv->ieee->lock, flags);
6738
6739 IPW_DEBUG_QOS
6740 ("QoS %d network is QoS active %d supported %d unicast %d\n",
6741 priv->qos_data.qos_enable, active, supported, unicast);
6742 if (active && priv->qos_data.qos_enable) {
6743 ret = from_priority_to_tx_queue[priority];
6744 tx_queue_id = ret - 1;
6745 IPW_DEBUG_QOS("QoS packet priority is %d \n", priority);
6746 if (priority <= 7) {
6747 tfd->tx_flags_ext |= DCT_FLAG_EXT_QOS_ENABLED;
6748 tfd->tfd.tfd_26.mchdr.qos_ctrl = priority;
6749 tfd->tfd.tfd_26.mchdr.frame_ctl |=
6750 IEEE80211_STYPE_QOS_DATA;
6751
6752 if (priv->qos_data.qos_no_ack_mask &
6753 (1UL << tx_queue_id)) {
6754 tfd->tx_flags &= ~DCT_FLAG_ACK_REQD;
6755 tfd->tfd.tfd_26.mchdr.qos_ctrl |=
6756 CTRL_QOS_NO_ACK;
6757 }
6758 }
6759 }
6760
6761 return ret;
6762}
6763
6764/*
6765* background support to run QoS activate functionality
6766*/
6767static void ipw_bg_qos_activate(void *data)
6768{
6769 struct ipw_priv *priv = data;
6770
6771 if (priv == NULL)
6772 return;
6773
6774 down(&priv->sem);
6775
6776 if (priv->status & STATUS_ASSOCIATED)
6777 ipw_qos_activate(priv, &(priv->assoc_network->qos_data));
6778
6779 up(&priv->sem);
6780}
6781
6782/*
6783* Handler for probe responce and beacon frame
6784*/
6785static int ipw_handle_management_frame(struct net_device *dev,
6786 struct ieee80211_network *network,
6787 u16 type)
6788{
6789 struct ipw_priv *priv = ieee80211_priv(dev);
6790 int active_network;
6791
6792 if (priv->status & STATUS_ASSOCIATED && network == priv->assoc_network)
6793 active_network = 1;
6794 else
6795 active_network = 0;
6796
6797 switch (type) {
6798 case IEEE80211_STYPE_PROBE_RESP:
6799 case IEEE80211_STYPE_BEACON:
6800 ipw_qos_handle_probe_reponse(priv, active_network, network);
6801 break;
6802 case IEEE80211_STYPE_ASSOC_RESP:
6803 ipw_qos_association_resp(priv, network);
6804 break;
6805 default:
6806 break;
6807 }
6808
6809 return 0;
6810}
6811
6812static int ipw_send_qos_params_command(struct ipw_priv *priv, struct ieee80211_qos_parameters
6813 *qos_param)
6814{
6815 struct host_cmd cmd = {
6816 .cmd = IPW_CMD_QOS_PARAMETERS,
6817 .len = (sizeof(struct ieee80211_qos_parameters) * 3)
6818 };
6819
6820 if (!priv || !qos_param) {
6821 IPW_ERROR("Invalid args\n");
6822 return -1;
6823 }
6824
6825 memcpy(&cmd.param, qos_param,
6826 (sizeof(struct ieee80211_qos_parameters) * 3));
6827 if (ipw_send_cmd(priv, &cmd)) {
6828 IPW_ERROR("failed to send IPW_CMD_QOS_PARAMETERS command\n");
6829 return -1;
6830 }
6831
6832 return 0;
6833}
6834
6835static int ipw_send_qos_info_command(struct ipw_priv *priv, struct ieee80211_qos_information_element
6836 *qos_param)
6837{
6838 struct host_cmd cmd = {
6839 .cmd = IPW_CMD_WME_INFO,
6840 .len = sizeof(*qos_param)
6841 };
6842
6843 if (!priv || !qos_param) {
6844 IPW_ERROR("Invalid args\n");
6845 return -1;
6846 }
6847
6848 memcpy(&cmd.param, qos_param, sizeof(*qos_param));
6849 if (ipw_send_cmd(priv, &cmd)) {
6850 IPW_ERROR("failed to send CMD_QOS_INFO command\n");
6851 return -1;
6852 }
6853
6854 return 0;
6855}
6856
6857#endif /* CONFIG_IPW_QOS */
6858
James Ketrenos43f66a62005-03-25 12:31:53 -06006859static int ipw_associate_network(struct ipw_priv *priv,
6860 struct ieee80211_network *network,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04006861 struct ipw_supported_rates *rates, int roaming)
James Ketrenos43f66a62005-03-25 12:31:53 -06006862{
6863 int err;
6864
6865 if (priv->config & CFG_FIXED_RATE)
James Ketrenosb095c382005-08-24 22:04:42 -05006866 ipw_set_fixed_rate(priv, network->mode);
James Ketrenos43f66a62005-03-25 12:31:53 -06006867
6868 if (!(priv->config & CFG_STATIC_ESSID)) {
Jeff Garzikbf794512005-07-31 13:07:26 -04006869 priv->essid_len = min(network->ssid_len,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04006870 (u8) IW_ESSID_MAX_SIZE);
James Ketrenos43f66a62005-03-25 12:31:53 -06006871 memcpy(priv->essid, network->ssid, priv->essid_len);
6872 }
6873
6874 network->last_associate = jiffies;
6875
6876 memset(&priv->assoc_request, 0, sizeof(priv->assoc_request));
6877 priv->assoc_request.channel = network->channel;
6878 if ((priv->capability & CAP_PRIVACY_ON) &&
6879 (priv->capability & CAP_SHARED_KEY)) {
6880 priv->assoc_request.auth_type = AUTH_SHARED_KEY;
James Ketrenosb095c382005-08-24 22:04:42 -05006881 priv->assoc_request.auth_key = priv->ieee->sec.active_key;
6882
6883 if ((priv->capability & CAP_PRIVACY_ON) &&
6884 (priv->ieee->sec.level == SEC_LEVEL_1) &&
6885 !(priv->ieee->host_encrypt || priv->ieee->host_decrypt))
6886 ipw_send_wep_keys(priv, DCW_WEP_KEY_SEC_TYPE_WEP);
James Ketrenos43f66a62005-03-25 12:31:53 -06006887 } else {
6888 priv->assoc_request.auth_type = AUTH_OPEN;
6889 priv->assoc_request.auth_key = 0;
6890 }
6891
James Ketrenosa613bff2005-08-24 21:43:11 -05006892 if (priv->ieee->wpa_ie_len) {
James Ketrenosea2b26e2005-08-24 21:25:16 -05006893 priv->assoc_request.policy_support = 0x02; /* RSN active */
6894 ipw_set_rsn_capa(priv, priv->ieee->wpa_ie,
6895 priv->ieee->wpa_ie_len);
6896 }
James Ketrenosea2b26e2005-08-24 21:25:16 -05006897
Jeff Garzikbf794512005-07-31 13:07:26 -04006898 /*
6899 * It is valid for our ieee device to support multiple modes, but
6900 * when it comes to associating to a given network we have to choose
James Ketrenos43f66a62005-03-25 12:31:53 -06006901 * just one mode.
6902 */
6903 if (network->mode & priv->ieee->mode & IEEE_A)
6904 priv->assoc_request.ieee_mode = IPW_A_MODE;
6905 else if (network->mode & priv->ieee->mode & IEEE_G)
6906 priv->assoc_request.ieee_mode = IPW_G_MODE;
6907 else if (network->mode & priv->ieee->mode & IEEE_B)
6908 priv->assoc_request.ieee_mode = IPW_B_MODE;
6909
James Ketrenosea2b26e2005-08-24 21:25:16 -05006910 priv->assoc_request.capability = network->capability;
6911 if ((network->capability & WLAN_CAPABILITY_SHORT_PREAMBLE)
6912 && !(priv->config & CFG_PREAMBLE_LONG)) {
6913 priv->assoc_request.preamble_length = DCT_FLAG_SHORT_PREAMBLE;
6914 } else {
6915 priv->assoc_request.preamble_length = DCT_FLAG_LONG_PREAMBLE;
6916
6917 /* Clear the short preamble if we won't be supporting it */
6918 priv->assoc_request.capability &=
6919 ~WLAN_CAPABILITY_SHORT_PREAMBLE;
6920 }
6921
James Ketrenos43f66a62005-03-25 12:31:53 -06006922 IPW_DEBUG_ASSOC("%sssocation attempt: '%s', channel %d, "
James Ketrenosea2b26e2005-08-24 21:25:16 -05006923 "802.11%c [%d], %s[:%s], enc=%s%s%s%c%c\n",
James Ketrenos43f66a62005-03-25 12:31:53 -06006924 roaming ? "Rea" : "A",
Jeff Garzikbf794512005-07-31 13:07:26 -04006925 escape_essid(priv->essid, priv->essid_len),
6926 network->channel,
6927 ipw_modes[priv->assoc_request.ieee_mode],
6928 rates->num_rates,
James Ketrenosea2b26e2005-08-24 21:25:16 -05006929 (priv->assoc_request.preamble_length ==
6930 DCT_FLAG_LONG_PREAMBLE) ? "long" : "short",
6931 network->capability &
6932 WLAN_CAPABILITY_SHORT_PREAMBLE ? "short" : "long",
James Ketrenos43f66a62005-03-25 12:31:53 -06006933 priv->capability & CAP_PRIVACY_ON ? "on " : "off",
Jeff Garzikbf794512005-07-31 13:07:26 -04006934 priv->capability & CAP_PRIVACY_ON ?
6935 (priv->capability & CAP_SHARED_KEY ? "(shared)" :
James Ketrenos43f66a62005-03-25 12:31:53 -06006936 "(open)") : "",
6937 priv->capability & CAP_PRIVACY_ON ? " key=" : "",
Jeff Garzikbf794512005-07-31 13:07:26 -04006938 priv->capability & CAP_PRIVACY_ON ?
James Ketrenosb095c382005-08-24 22:04:42 -05006939 '1' + priv->ieee->sec.active_key : '.',
Jeff Garzik0edd5b42005-09-07 00:48:31 -04006940 priv->capability & CAP_PRIVACY_ON ? '.' : ' ');
James Ketrenos43f66a62005-03-25 12:31:53 -06006941
6942 priv->assoc_request.beacon_interval = network->beacon_interval;
6943 if ((priv->ieee->iw_mode == IW_MODE_ADHOC) &&
Jeff Garzik0edd5b42005-09-07 00:48:31 -04006944 (network->time_stamp[0] == 0) && (network->time_stamp[1] == 0)) {
James Ketrenos43f66a62005-03-25 12:31:53 -06006945 priv->assoc_request.assoc_type = HC_IBSS_START;
6946 priv->assoc_request.assoc_tsf_msw = 0;
6947 priv->assoc_request.assoc_tsf_lsw = 0;
6948 } else {
6949 if (unlikely(roaming))
6950 priv->assoc_request.assoc_type = HC_REASSOCIATE;
6951 else
6952 priv->assoc_request.assoc_type = HC_ASSOCIATE;
6953 priv->assoc_request.assoc_tsf_msw = network->time_stamp[1];
6954 priv->assoc_request.assoc_tsf_lsw = network->time_stamp[0];
6955 }
6956
6957 memcpy(&priv->assoc_request.bssid, network->bssid, ETH_ALEN);
6958
6959 if (priv->ieee->iw_mode == IW_MODE_ADHOC) {
6960 memset(&priv->assoc_request.dest, 0xFF, ETH_ALEN);
6961 priv->assoc_request.atim_window = network->atim_window;
6962 } else {
Jeff Garzik0edd5b42005-09-07 00:48:31 -04006963 memcpy(&priv->assoc_request.dest, network->bssid, ETH_ALEN);
James Ketrenos43f66a62005-03-25 12:31:53 -06006964 priv->assoc_request.atim_window = 0;
6965 }
6966
James Ketrenos43f66a62005-03-25 12:31:53 -06006967 priv->assoc_request.listen_interval = network->listen_interval;
Jeff Garzikbf794512005-07-31 13:07:26 -04006968
James Ketrenos43f66a62005-03-25 12:31:53 -06006969 err = ipw_send_ssid(priv, priv->essid, priv->essid_len);
6970 if (err) {
6971 IPW_DEBUG_HC("Attempt to send SSID command failed.\n");
6972 return err;
6973 }
6974
6975 rates->ieee_mode = priv->assoc_request.ieee_mode;
6976 rates->purpose = IPW_RATE_CONNECT;
6977 ipw_send_supported_rates(priv, rates);
Jeff Garzikbf794512005-07-31 13:07:26 -04006978
James Ketrenos43f66a62005-03-25 12:31:53 -06006979 if (priv->assoc_request.ieee_mode == IPW_G_MODE)
6980 priv->sys_config.dot11g_auto_detection = 1;
6981 else
6982 priv->sys_config.dot11g_auto_detection = 0;
James Ketrenosc848d0a2005-08-24 21:56:24 -05006983
6984 if (priv->ieee->iw_mode == IW_MODE_ADHOC)
6985 priv->sys_config.answer_broadcast_ssid_probe = 1;
6986 else
6987 priv->sys_config.answer_broadcast_ssid_probe = 0;
6988
James Ketrenos43f66a62005-03-25 12:31:53 -06006989 err = ipw_send_system_config(priv, &priv->sys_config);
6990 if (err) {
6991 IPW_DEBUG_HC("Attempt to send sys config command failed.\n");
6992 return err;
6993 }
Jeff Garzikbf794512005-07-31 13:07:26 -04006994
James Ketrenos43f66a62005-03-25 12:31:53 -06006995 IPW_DEBUG_ASSOC("Association sensitivity: %d\n", network->stats.rssi);
James Ketrenosea2b26e2005-08-24 21:25:16 -05006996 err = ipw_set_sensitivity(priv, network->stats.rssi + IPW_RSSI_TO_DBM);
James Ketrenos43f66a62005-03-25 12:31:53 -06006997 if (err) {
6998 IPW_DEBUG_HC("Attempt to send associate command failed.\n");
6999 return err;
7000 }
7001
7002 /*
7003 * If preemption is enabled, it is possible for the association
7004 * to complete before we return from ipw_send_associate. Therefore
7005 * we have to be sure and update our priviate data first.
7006 */
7007 priv->channel = network->channel;
7008 memcpy(priv->bssid, network->bssid, ETH_ALEN);
Jeff Garzikbf794512005-07-31 13:07:26 -04007009 priv->status |= STATUS_ASSOCIATING;
James Ketrenos43f66a62005-03-25 12:31:53 -06007010 priv->status &= ~STATUS_SECURITY_UPDATED;
7011
7012 priv->assoc_network = network;
7013
James Ketrenosb095c382005-08-24 22:04:42 -05007014#ifdef CONFIG_IPW_QOS
7015 ipw_qos_association(priv, network);
7016#endif
7017
James Ketrenos43f66a62005-03-25 12:31:53 -06007018 err = ipw_send_associate(priv, &priv->assoc_request);
7019 if (err) {
7020 IPW_DEBUG_HC("Attempt to send associate command failed.\n");
7021 return err;
7022 }
Jeff Garzikbf794512005-07-31 13:07:26 -04007023
7024 IPW_DEBUG(IPW_DL_STATE, "associating: '%s' " MAC_FMT " \n",
James Ketrenos43f66a62005-03-25 12:31:53 -06007025 escape_essid(priv->essid, priv->essid_len),
7026 MAC_ARG(priv->bssid));
7027
7028 return 0;
7029}
7030
7031static void ipw_roam(void *data)
7032{
7033 struct ipw_priv *priv = data;
7034 struct ieee80211_network *network = NULL;
7035 struct ipw_network_match match = {
7036 .network = priv->assoc_network
7037 };
7038
7039 /* The roaming process is as follows:
Jeff Garzikbf794512005-07-31 13:07:26 -04007040 *
7041 * 1. Missed beacon threshold triggers the roaming process by
James Ketrenos43f66a62005-03-25 12:31:53 -06007042 * setting the status ROAM bit and requesting a scan.
7043 * 2. When the scan completes, it schedules the ROAM work
7044 * 3. The ROAM work looks at all of the known networks for one that
7045 * is a better network than the currently associated. If none
7046 * found, the ROAM process is over (ROAM bit cleared)
7047 * 4. If a better network is found, a disassociation request is
7048 * sent.
7049 * 5. When the disassociation completes, the roam work is again
7050 * scheduled. The second time through, the driver is no longer
7051 * associated, and the newly selected network is sent an
Jeff Garzikbf794512005-07-31 13:07:26 -04007052 * association request.
James Ketrenos43f66a62005-03-25 12:31:53 -06007053 * 6. At this point ,the roaming process is complete and the ROAM
7054 * status bit is cleared.
7055 */
7056
7057 /* If we are no longer associated, and the roaming bit is no longer
7058 * set, then we are not actively roaming, so just return */
7059 if (!(priv->status & (STATUS_ASSOCIATED | STATUS_ROAMING)))
7060 return;
Jeff Garzikbf794512005-07-31 13:07:26 -04007061
James Ketrenos43f66a62005-03-25 12:31:53 -06007062 if (priv->status & STATUS_ASSOCIATED) {
Jeff Garzikbf794512005-07-31 13:07:26 -04007063 /* First pass through ROAM process -- look for a better
James Ketrenos43f66a62005-03-25 12:31:53 -06007064 * network */
James Ketrenosa613bff2005-08-24 21:43:11 -05007065 unsigned long flags;
James Ketrenos43f66a62005-03-25 12:31:53 -06007066 u8 rssi = priv->assoc_network->stats.rssi;
7067 priv->assoc_network->stats.rssi = -128;
James Ketrenosa613bff2005-08-24 21:43:11 -05007068 spin_lock_irqsave(&priv->ieee->lock, flags);
James Ketrenos43f66a62005-03-25 12:31:53 -06007069 list_for_each_entry(network, &priv->ieee->network_list, list) {
7070 if (network != priv->assoc_network)
7071 ipw_best_network(priv, &match, network, 1);
7072 }
James Ketrenosa613bff2005-08-24 21:43:11 -05007073 spin_unlock_irqrestore(&priv->ieee->lock, flags);
James Ketrenos43f66a62005-03-25 12:31:53 -06007074 priv->assoc_network->stats.rssi = rssi;
Jeff Garzikbf794512005-07-31 13:07:26 -04007075
James Ketrenos43f66a62005-03-25 12:31:53 -06007076 if (match.network == priv->assoc_network) {
7077 IPW_DEBUG_ASSOC("No better APs in this network to "
7078 "roam to.\n");
7079 priv->status &= ~STATUS_ROAMING;
7080 ipw_debug_config(priv);
7081 return;
7082 }
Jeff Garzikbf794512005-07-31 13:07:26 -04007083
James Ketrenos43f66a62005-03-25 12:31:53 -06007084 ipw_send_disassociate(priv, 1);
7085 priv->assoc_network = match.network;
7086
7087 return;
Jeff Garzikbf794512005-07-31 13:07:26 -04007088 }
James Ketrenos43f66a62005-03-25 12:31:53 -06007089
7090 /* Second pass through ROAM process -- request association */
7091 ipw_compatible_rates(priv, priv->assoc_network, &match.rates);
7092 ipw_associate_network(priv, priv->assoc_network, &match.rates, 1);
7093 priv->status &= ~STATUS_ROAMING;
7094}
7095
James Ketrenosc848d0a2005-08-24 21:56:24 -05007096static void ipw_bg_roam(void *data)
7097{
7098 struct ipw_priv *priv = data;
7099 down(&priv->sem);
7100 ipw_roam(data);
7101 up(&priv->sem);
7102}
7103
7104static int ipw_associate(void *data)
James Ketrenos43f66a62005-03-25 12:31:53 -06007105{
7106 struct ipw_priv *priv = data;
7107
7108 struct ieee80211_network *network = NULL;
7109 struct ipw_network_match match = {
7110 .network = NULL
7111 };
7112 struct ipw_supported_rates *rates;
7113 struct list_head *element;
James Ketrenosa613bff2005-08-24 21:43:11 -05007114 unsigned long flags;
James Ketrenos43f66a62005-03-25 12:31:53 -06007115
James Ketrenosb095c382005-08-24 22:04:42 -05007116 if (priv->ieee->iw_mode == IW_MODE_MONITOR) {
7117 IPW_DEBUG_ASSOC("Not attempting association (monitor mode)\n");
7118 return 0;
7119 }
7120
James Ketrenosc848d0a2005-08-24 21:56:24 -05007121 if (priv->status & (STATUS_ASSOCIATED | STATUS_ASSOCIATING)) {
7122 IPW_DEBUG_ASSOC
7123 ("Not attempting association (already in progress)\n");
7124 return 0;
7125 }
7126
7127 if (!ipw_is_init(priv) || (priv->status & STATUS_SCANNING)) {
7128 IPW_DEBUG_ASSOC
7129 ("Not attempting association (scanning or not initialized)\n");
7130 return 0;
7131 }
7132
James Ketrenos43f66a62005-03-25 12:31:53 -06007133 if (!(priv->config & CFG_ASSOCIATE) &&
7134 !(priv->config & (CFG_STATIC_ESSID |
Jeff Garzik0edd5b42005-09-07 00:48:31 -04007135 CFG_STATIC_CHANNEL | CFG_STATIC_BSSID))) {
James Ketrenos43f66a62005-03-25 12:31:53 -06007136 IPW_DEBUG_ASSOC("Not attempting association (associate=0)\n");
James Ketrenosc848d0a2005-08-24 21:56:24 -05007137 return 0;
James Ketrenos43f66a62005-03-25 12:31:53 -06007138 }
7139
James Ketrenosa613bff2005-08-24 21:43:11 -05007140 /* Protect our use of the network_list */
7141 spin_lock_irqsave(&priv->ieee->lock, flags);
Jeff Garzikbf794512005-07-31 13:07:26 -04007142 list_for_each_entry(network, &priv->ieee->network_list, list)
Jeff Garzik0edd5b42005-09-07 00:48:31 -04007143 ipw_best_network(priv, &match, network, 0);
James Ketrenos43f66a62005-03-25 12:31:53 -06007144
7145 network = match.network;
7146 rates = &match.rates;
7147
7148 if (network == NULL &&
7149 priv->ieee->iw_mode == IW_MODE_ADHOC &&
7150 priv->config & CFG_ADHOC_CREATE &&
7151 priv->config & CFG_STATIC_ESSID &&
James Ketrenosa613bff2005-08-24 21:43:11 -05007152 priv->config & CFG_STATIC_CHANNEL &&
James Ketrenos43f66a62005-03-25 12:31:53 -06007153 !list_empty(&priv->ieee->network_free_list)) {
7154 element = priv->ieee->network_free_list.next;
Jeff Garzik0edd5b42005-09-07 00:48:31 -04007155 network = list_entry(element, struct ieee80211_network, list);
James Ketrenos43f66a62005-03-25 12:31:53 -06007156 ipw_adhoc_create(priv, network);
7157 rates = &priv->rates;
7158 list_del(element);
7159 list_add_tail(&network->list, &priv->ieee->network_list);
7160 }
James Ketrenosa613bff2005-08-24 21:43:11 -05007161 spin_unlock_irqrestore(&priv->ieee->lock, flags);
Jeff Garzikbf794512005-07-31 13:07:26 -04007162
James Ketrenos43f66a62005-03-25 12:31:53 -06007163 /* If we reached the end of the list, then we don't have any valid
7164 * matching APs */
7165 if (!network) {
7166 ipw_debug_config(priv);
7167
James Ketrenosb095c382005-08-24 22:04:42 -05007168 if (!(priv->status & STATUS_SCANNING)) {
7169 if (!(priv->config & CFG_SPEED_SCAN))
7170 queue_delayed_work(priv->workqueue,
7171 &priv->request_scan,
7172 SCAN_INTERVAL);
7173 else
7174 queue_work(priv->workqueue,
7175 &priv->request_scan);
7176 }
Jeff Garzikbf794512005-07-31 13:07:26 -04007177
James Ketrenosc848d0a2005-08-24 21:56:24 -05007178 return 0;
James Ketrenos43f66a62005-03-25 12:31:53 -06007179 }
7180
7181 ipw_associate_network(priv, network, rates, 0);
James Ketrenosc848d0a2005-08-24 21:56:24 -05007182
7183 return 1;
7184}
7185
7186static void ipw_bg_associate(void *data)
7187{
7188 struct ipw_priv *priv = data;
7189 down(&priv->sem);
7190 ipw_associate(data);
7191 up(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06007192}
Jeff Garzikbf794512005-07-31 13:07:26 -04007193
James Ketrenosb095c382005-08-24 22:04:42 -05007194static void ipw_rebuild_decrypted_skb(struct ipw_priv *priv,
7195 struct sk_buff *skb)
7196{
7197 struct ieee80211_hdr *hdr;
7198 u16 fc;
7199
7200 hdr = (struct ieee80211_hdr *)skb->data;
7201 fc = le16_to_cpu(hdr->frame_ctl);
7202 if (!(fc & IEEE80211_FCTL_PROTECTED))
7203 return;
7204
7205 fc &= ~IEEE80211_FCTL_PROTECTED;
7206 hdr->frame_ctl = cpu_to_le16(fc);
7207 switch (priv->ieee->sec.level) {
7208 case SEC_LEVEL_3:
7209 /* Remove CCMP HDR */
7210 memmove(skb->data + IEEE80211_3ADDR_LEN,
7211 skb->data + IEEE80211_3ADDR_LEN + 8,
7212 skb->len - IEEE80211_3ADDR_LEN - 8);
7213 skb_trim(skb, skb->len - 8); /* MIC */
7214 break;
7215 case SEC_LEVEL_2:
7216 break;
7217 case SEC_LEVEL_1:
7218 /* Remove IV */
7219 memmove(skb->data + IEEE80211_3ADDR_LEN,
7220 skb->data + IEEE80211_3ADDR_LEN + 4,
7221 skb->len - IEEE80211_3ADDR_LEN - 4);
7222 skb_trim(skb, skb->len - 4); /* ICV */
7223 break;
7224 case SEC_LEVEL_0:
7225 break;
7226 default:
7227 printk(KERN_ERR "Unknow security level %d\n",
7228 priv->ieee->sec.level);
7229 break;
7230 }
7231}
7232
7233static void ipw_handle_data_packet(struct ipw_priv *priv,
7234 struct ipw_rx_mem_buffer *rxb,
7235 struct ieee80211_rx_stats *stats)
James Ketrenos43f66a62005-03-25 12:31:53 -06007236{
7237 struct ipw_rx_packet *pkt = (struct ipw_rx_packet *)rxb->skb->data;
7238
7239 /* We received data from the HW, so stop the watchdog */
7240 priv->net_dev->trans_start = jiffies;
7241
Jeff Garzikbf794512005-07-31 13:07:26 -04007242 /* We only process data packets if the
James Ketrenos43f66a62005-03-25 12:31:53 -06007243 * interface is open */
James Ketrenosa613bff2005-08-24 21:43:11 -05007244 if (unlikely((le16_to_cpu(pkt->u.frame.length) + IPW_RX_FRAME_SIZE) >
James Ketrenos43f66a62005-03-25 12:31:53 -06007245 skb_tailroom(rxb->skb))) {
7246 priv->ieee->stats.rx_errors++;
7247 priv->wstats.discard.misc++;
7248 IPW_DEBUG_DROP("Corruption detected! Oh no!\n");
7249 return;
7250 } else if (unlikely(!netif_running(priv->net_dev))) {
7251 priv->ieee->stats.rx_dropped++;
7252 priv->wstats.discard.misc++;
7253 IPW_DEBUG_DROP("Dropping packet while interface is not up.\n");
7254 return;
7255 }
7256
7257 /* Advance skb->data to the start of the actual payload */
Jiri Bencaaa4d302005-06-07 14:58:41 +02007258 skb_reserve(rxb->skb, offsetof(struct ipw_rx_packet, u.frame.data));
James Ketrenos43f66a62005-03-25 12:31:53 -06007259
7260 /* Set the size of the skb to the size of the frame */
James Ketrenosa613bff2005-08-24 21:43:11 -05007261 skb_put(rxb->skb, le16_to_cpu(pkt->u.frame.length));
James Ketrenos43f66a62005-03-25 12:31:53 -06007262
7263 IPW_DEBUG_RX("Rx packet of %d bytes.\n", rxb->skb->len);
7264
James Ketrenosb095c382005-08-24 22:04:42 -05007265 /* HW decrypt will not clear the WEP bit, MIC, PN, etc. */
7266 if (!priv->ieee->host_decrypt)
7267 ipw_rebuild_decrypted_skb(priv, rxb->skb);
7268
Jeff Garzikbf794512005-07-31 13:07:26 -04007269 if (!ieee80211_rx(priv->ieee, rxb->skb, stats))
James Ketrenos43f66a62005-03-25 12:31:53 -06007270 priv->ieee->stats.rx_errors++;
James Ketrenosa613bff2005-08-24 21:43:11 -05007271 else { /* ieee80211_rx succeeded, so it now owns the SKB */
James Ketrenos43f66a62005-03-25 12:31:53 -06007272 rxb->skb = NULL;
James Ketrenosb095c382005-08-24 22:04:42 -05007273 __ipw_led_activity_on(priv);
James Ketrenosa613bff2005-08-24 21:43:11 -05007274 }
James Ketrenos43f66a62005-03-25 12:31:53 -06007275}
7276
James Ketrenosea2b26e2005-08-24 21:25:16 -05007277static inline int is_network_packet(struct ipw_priv *priv,
7278 struct ieee80211_hdr_4addr *header)
7279{
7280 /* Filter incoming packets to determine if they are targetted toward
7281 * this network, discarding packets coming from ourselves */
7282 switch (priv->ieee->iw_mode) {
James Ketrenosa613bff2005-08-24 21:43:11 -05007283 case IW_MODE_ADHOC: /* Header: Dest. | Source | BSSID */
James Ketrenosc848d0a2005-08-24 21:56:24 -05007284 /* packets from our adapter are dropped (echo) */
7285 if (!memcmp(header->addr2, priv->net_dev->dev_addr, ETH_ALEN))
7286 return 0;
7287
James Ketrenosa613bff2005-08-24 21:43:11 -05007288 /* {broad,multi}cast packets to our IBSS go through */
James Ketrenosea2b26e2005-08-24 21:25:16 -05007289 if (is_broadcast_ether_addr(header->addr1) ||
7290 is_multicast_ether_addr(header->addr1))
7291 return !memcmp(header->addr3, priv->bssid, ETH_ALEN);
James Ketrenosa613bff2005-08-24 21:43:11 -05007292
7293 /* packets to our adapter go through */
7294 return !memcmp(header->addr1, priv->net_dev->dev_addr,
7295 ETH_ALEN);
James Ketrenosa613bff2005-08-24 21:43:11 -05007296
7297 case IW_MODE_INFRA: /* Header: Dest. | AP{BSSID} | Source */
James Ketrenosc848d0a2005-08-24 21:56:24 -05007298 /* packets from our adapter are dropped (echo) */
7299 if (!memcmp(header->addr3, priv->net_dev->dev_addr, ETH_ALEN))
7300 return 0;
7301
James Ketrenosa613bff2005-08-24 21:43:11 -05007302 /* {broad,multi}cast packets to our IBSS go through */
7303 if (is_broadcast_ether_addr(header->addr1) ||
7304 is_multicast_ether_addr(header->addr1))
7305 return !memcmp(header->addr2, priv->bssid, ETH_ALEN);
7306
7307 /* packets to our adapter go through */
7308 return !memcmp(header->addr1, priv->net_dev->dev_addr,
7309 ETH_ALEN);
James Ketrenosea2b26e2005-08-24 21:25:16 -05007310 }
James Ketrenosa613bff2005-08-24 21:43:11 -05007311
James Ketrenosea2b26e2005-08-24 21:25:16 -05007312 return 1;
7313}
7314
James Ketrenosb095c382005-08-24 22:04:42 -05007315static void ipw_handle_mgmt_packet(struct ipw_priv *priv,
7316 struct ipw_rx_mem_buffer *rxb,
7317 struct ieee80211_rx_stats *stats)
7318{
7319 struct sk_buff *skb = rxb->skb;
7320 struct ipw_rx_packet *pkt = (struct ipw_rx_packet *)skb->data;
7321 struct ieee80211_hdr_4addr *header = (struct ieee80211_hdr_4addr *)
7322 (skb->data + IPW_RX_FRAME_SIZE);
7323
7324 ieee80211_rx_mgt(priv->ieee, header, stats);
7325
7326 if (priv->ieee->iw_mode == IW_MODE_ADHOC &&
7327 ((WLAN_FC_GET_STYPE(le16_to_cpu(header->frame_ctl)) ==
7328 IEEE80211_STYPE_PROBE_RESP) ||
7329 (WLAN_FC_GET_STYPE(le16_to_cpu(header->frame_ctl)) ==
7330 IEEE80211_STYPE_BEACON))) {
7331 if (!memcmp(header->addr3, priv->bssid, ETH_ALEN))
7332 ipw_add_station(priv, header->addr2);
7333 }
7334
7335 if (priv->config & CFG_NET_STATS) {
7336 IPW_DEBUG_HC("sending stat packet\n");
7337
7338 /* Set the size of the skb to the size of the full
7339 * ipw header and 802.11 frame */
7340 skb_put(skb, le16_to_cpu(pkt->u.frame.length) +
7341 IPW_RX_FRAME_SIZE);
7342
7343 /* Advance past the ipw packet header to the 802.11 frame */
7344 skb_pull(skb, IPW_RX_FRAME_SIZE);
7345
7346 /* Push the ieee80211_rx_stats before the 802.11 frame */
7347 memcpy(skb_push(skb, sizeof(*stats)), stats, sizeof(*stats));
7348
7349 skb->dev = priv->ieee->dev;
7350
7351 /* Point raw at the ieee80211_stats */
7352 skb->mac.raw = skb->data;
7353
7354 skb->pkt_type = PACKET_OTHERHOST;
7355 skb->protocol = __constant_htons(ETH_P_80211_STATS);
7356 memset(skb->cb, 0, sizeof(rxb->skb->cb));
7357 netif_rx(skb);
7358 rxb->skb = NULL;
7359 }
7360}
7361
James Ketrenos43f66a62005-03-25 12:31:53 -06007362/*
7363 * Main entry function for recieving a packet with 80211 headers. This
7364 * should be called when ever the FW has notified us that there is a new
7365 * skb in the recieve queue.
7366 */
7367static void ipw_rx(struct ipw_priv *priv)
7368{
7369 struct ipw_rx_mem_buffer *rxb;
7370 struct ipw_rx_packet *pkt;
James Ketrenos0dacca12005-09-21 12:23:41 -05007371 struct ieee80211_hdr_4addr *header;
James Ketrenos43f66a62005-03-25 12:31:53 -06007372 u32 r, w, i;
7373 u8 network_packet;
7374
James Ketrenosb095c382005-08-24 22:04:42 -05007375 r = ipw_read32(priv, IPW_RX_READ_INDEX);
7376 w = ipw_read32(priv, IPW_RX_WRITE_INDEX);
James Ketrenos43f66a62005-03-25 12:31:53 -06007377 i = (priv->rxq->processed + 1) % RX_QUEUE_SIZE;
7378
7379 while (i != r) {
7380 rxb = priv->rxq->queue[i];
7381#ifdef CONFIG_IPW_DEBUG
7382 if (unlikely(rxb == NULL)) {
7383 printk(KERN_CRIT "Queue not allocated!\n");
7384 break;
7385 }
7386#endif
7387 priv->rxq->queue[i] = NULL;
7388
7389 pci_dma_sync_single_for_cpu(priv->pci_dev, rxb->dma_addr,
James Ketrenosb095c382005-08-24 22:04:42 -05007390 IPW_RX_BUF_SIZE,
James Ketrenos43f66a62005-03-25 12:31:53 -06007391 PCI_DMA_FROMDEVICE);
7392
7393 pkt = (struct ipw_rx_packet *)rxb->skb->data;
7394 IPW_DEBUG_RX("Packet: type=%02X seq=%02X bits=%02X\n",
7395 pkt->header.message_type,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04007396 pkt->header.rx_seq_num, pkt->header.control_bits);
James Ketrenos43f66a62005-03-25 12:31:53 -06007397
7398 switch (pkt->header.message_type) {
Jeff Garzik0edd5b42005-09-07 00:48:31 -04007399 case RX_FRAME_TYPE: /* 802.11 frame */ {
7400 struct ieee80211_rx_stats stats = {
James Ketrenosc848d0a2005-08-24 21:56:24 -05007401 .rssi =
7402 le16_to_cpu(pkt->u.frame.rssi_dbm) -
Jeff Garzik0edd5b42005-09-07 00:48:31 -04007403 IPW_RSSI_TO_DBM,
James Ketrenosc848d0a2005-08-24 21:56:24 -05007404 .signal =
7405 le16_to_cpu(pkt->u.frame.signal),
7406 .noise =
7407 le16_to_cpu(pkt->u.frame.noise),
Jeff Garzik0edd5b42005-09-07 00:48:31 -04007408 .rate = pkt->u.frame.rate,
7409 .mac_time = jiffies,
7410 .received_channel =
7411 pkt->u.frame.received_channel,
7412 .freq =
7413 (pkt->u.frame.
7414 control & (1 << 0)) ?
7415 IEEE80211_24GHZ_BAND :
7416 IEEE80211_52GHZ_BAND,
James Ketrenosa613bff2005-08-24 21:43:11 -05007417 .len = le16_to_cpu(pkt->u.frame.length),
Jeff Garzik0edd5b42005-09-07 00:48:31 -04007418 };
James Ketrenos43f66a62005-03-25 12:31:53 -06007419
Jeff Garzik0edd5b42005-09-07 00:48:31 -04007420 if (stats.rssi != 0)
7421 stats.mask |= IEEE80211_STATMASK_RSSI;
7422 if (stats.signal != 0)
7423 stats.mask |= IEEE80211_STATMASK_SIGNAL;
James Ketrenosc848d0a2005-08-24 21:56:24 -05007424 if (stats.noise != 0)
7425 stats.mask |= IEEE80211_STATMASK_NOISE;
Jeff Garzik0edd5b42005-09-07 00:48:31 -04007426 if (stats.rate != 0)
7427 stats.mask |= IEEE80211_STATMASK_RATE;
James Ketrenos43f66a62005-03-25 12:31:53 -06007428
Jeff Garzik0edd5b42005-09-07 00:48:31 -04007429 priv->rx_packets++;
James Ketrenos43f66a62005-03-25 12:31:53 -06007430
James Ketrenosb095c382005-08-24 22:04:42 -05007431#ifdef CONFIG_IPW2200_MONITOR
Jeff Garzik0edd5b42005-09-07 00:48:31 -04007432 if (priv->ieee->iw_mode == IW_MODE_MONITOR) {
7433 ipw_handle_data_packet(priv, rxb,
7434 &stats);
7435 break;
7436 }
James Ketrenos43f66a62005-03-25 12:31:53 -06007437#endif
Jeff Garzikbf794512005-07-31 13:07:26 -04007438
Jeff Garzik0edd5b42005-09-07 00:48:31 -04007439 header =
James Ketrenos0dacca12005-09-21 12:23:41 -05007440 (struct ieee80211_hdr_4addr *)(rxb->skb->
7441 data +
7442 IPW_RX_FRAME_SIZE);
James Ketrenos43f66a62005-03-25 12:31:53 -06007443 /* TODO: Check Ad-Hoc dest/source and make sure
7444 * that we are actually parsing these packets
Jeff Garzikbf794512005-07-31 13:07:26 -04007445 * correctly -- we should probably use the
James Ketrenos43f66a62005-03-25 12:31:53 -06007446 * frame control of the packet and disregard
7447 * the current iw_mode */
James Ketrenos43f66a62005-03-25 12:31:53 -06007448
James Ketrenosea2b26e2005-08-24 21:25:16 -05007449 network_packet =
7450 is_network_packet(priv, header);
Jeff Garzik0edd5b42005-09-07 00:48:31 -04007451 if (network_packet && priv->assoc_network) {
7452 priv->assoc_network->stats.rssi =
7453 stats.rssi;
7454 average_add(&priv->average_rssi,
7455 stats.rssi);
7456 priv->last_rx_rssi = stats.rssi;
7457 }
7458
7459 IPW_DEBUG_RX("Frame: len=%u\n",
James Ketrenosa613bff2005-08-24 21:43:11 -05007460 le16_to_cpu(pkt->u.frame.length));
Jeff Garzik0edd5b42005-09-07 00:48:31 -04007461
James Ketrenosa613bff2005-08-24 21:43:11 -05007462 if (le16_to_cpu(pkt->u.frame.length) <
7463 frame_hdr_len(header)) {
Jeff Garzik0edd5b42005-09-07 00:48:31 -04007464 IPW_DEBUG_DROP
7465 ("Received packet is too small. "
7466 "Dropping.\n");
7467 priv->ieee->stats.rx_errors++;
7468 priv->wstats.discard.misc++;
7469 break;
7470 }
7471
James Ketrenosa613bff2005-08-24 21:43:11 -05007472 switch (WLAN_FC_GET_TYPE
7473 (le16_to_cpu(header->frame_ctl))) {
James Ketrenosb095c382005-08-24 22:04:42 -05007474
Jeff Garzik0edd5b42005-09-07 00:48:31 -04007475 case IEEE80211_FTYPE_MGMT:
James Ketrenosb095c382005-08-24 22:04:42 -05007476 ipw_handle_mgmt_packet(priv, rxb,
7477 &stats);
Jeff Garzik0edd5b42005-09-07 00:48:31 -04007478 break;
7479
7480 case IEEE80211_FTYPE_CTL:
7481 break;
7482
7483 case IEEE80211_FTYPE_DATA:
James Ketrenosb095c382005-08-24 22:04:42 -05007484 if (unlikely(!network_packet)) {
Jeff Garzik0edd5b42005-09-07 00:48:31 -04007485 IPW_DEBUG_DROP("Dropping: "
7486 MAC_FMT ", "
7487 MAC_FMT ", "
7488 MAC_FMT "\n",
7489 MAC_ARG(header->
7490 addr1),
7491 MAC_ARG(header->
7492 addr2),
7493 MAC_ARG(header->
7494 addr3));
James Ketrenosb095c382005-08-24 22:04:42 -05007495 break;
7496 }
7497
7498 ipw_handle_data_packet(priv, rxb,
7499 &stats);
7500
Jeff Garzik0edd5b42005-09-07 00:48:31 -04007501 break;
7502 }
James Ketrenos43f66a62005-03-25 12:31:53 -06007503 break;
7504 }
Jeff Garzikbf794512005-07-31 13:07:26 -04007505
Jeff Garzik0edd5b42005-09-07 00:48:31 -04007506 case RX_HOST_NOTIFICATION_TYPE:{
7507 IPW_DEBUG_RX
7508 ("Notification: subtype=%02X flags=%02X size=%d\n",
James Ketrenos43f66a62005-03-25 12:31:53 -06007509 pkt->u.notification.subtype,
7510 pkt->u.notification.flags,
7511 pkt->u.notification.size);
Jeff Garzik0edd5b42005-09-07 00:48:31 -04007512 ipw_rx_notification(priv, &pkt->u.notification);
7513 break;
7514 }
James Ketrenos43f66a62005-03-25 12:31:53 -06007515
7516 default:
7517 IPW_DEBUG_RX("Bad Rx packet of type %d\n",
7518 pkt->header.message_type);
7519 break;
7520 }
Jeff Garzikbf794512005-07-31 13:07:26 -04007521
7522 /* For now we just don't re-use anything. We can tweak this
7523 * later to try and re-use notification packets and SKBs that
James Ketrenos43f66a62005-03-25 12:31:53 -06007524 * fail to Rx correctly */
7525 if (rxb->skb != NULL) {
7526 dev_kfree_skb_any(rxb->skb);
7527 rxb->skb = NULL;
7528 }
Jeff Garzikbf794512005-07-31 13:07:26 -04007529
James Ketrenos43f66a62005-03-25 12:31:53 -06007530 pci_unmap_single(priv->pci_dev, rxb->dma_addr,
James Ketrenosb095c382005-08-24 22:04:42 -05007531 IPW_RX_BUF_SIZE, PCI_DMA_FROMDEVICE);
James Ketrenos43f66a62005-03-25 12:31:53 -06007532 list_add_tail(&rxb->list, &priv->rxq->rx_used);
Jeff Garzikbf794512005-07-31 13:07:26 -04007533
James Ketrenos43f66a62005-03-25 12:31:53 -06007534 i = (i + 1) % RX_QUEUE_SIZE;
7535 }
7536
7537 /* Backtrack one entry */
7538 priv->rxq->processed = (i ? i : RX_QUEUE_SIZE) - 1;
7539
7540 ipw_rx_queue_restock(priv);
7541}
7542
James Ketrenos43f66a62005-03-25 12:31:53 -06007543/*
7544 * This file defines the Wireless Extension handlers. It does not
7545 * define any methods of hardware manipulation and relies on the
7546 * functions defined in ipw_main to provide the HW interaction.
Jeff Garzikbf794512005-07-31 13:07:26 -04007547 *
7548 * The exception to this is the use of the ipw_get_ordinal()
James Ketrenos43f66a62005-03-25 12:31:53 -06007549 * function used to poll the hardware vs. making unecessary calls.
7550 *
7551 */
7552
Jeff Garzikbf794512005-07-31 13:07:26 -04007553static int ipw_wx_get_name(struct net_device *dev,
7554 struct iw_request_info *info,
James Ketrenos43f66a62005-03-25 12:31:53 -06007555 union iwreq_data *wrqu, char *extra)
7556{
7557 struct ipw_priv *priv = ieee80211_priv(dev);
James Ketrenosc848d0a2005-08-24 21:56:24 -05007558 down(&priv->sem);
7559 if (priv->status & STATUS_RF_KILL_MASK)
James Ketrenosa613bff2005-08-24 21:43:11 -05007560 strcpy(wrqu->name, "radio off");
James Ketrenosc848d0a2005-08-24 21:56:24 -05007561 else if (!(priv->status & STATUS_ASSOCIATED))
James Ketrenos43f66a62005-03-25 12:31:53 -06007562 strcpy(wrqu->name, "unassociated");
Jeff Garzikbf794512005-07-31 13:07:26 -04007563 else
James Ketrenos43f66a62005-03-25 12:31:53 -06007564 snprintf(wrqu->name, IFNAMSIZ, "IEEE 802.11%c",
7565 ipw_modes[priv->assoc_request.ieee_mode]);
7566 IPW_DEBUG_WX("Name: %s\n", wrqu->name);
James Ketrenosc848d0a2005-08-24 21:56:24 -05007567 up(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06007568 return 0;
7569}
7570
7571static int ipw_set_channel(struct ipw_priv *priv, u8 channel)
7572{
James Ketrenosb095c382005-08-24 22:04:42 -05007573 int i;
7574
James Ketrenos43f66a62005-03-25 12:31:53 -06007575 if (channel == 0) {
7576 IPW_DEBUG_INFO("Setting channel to ANY (0)\n");
7577 priv->config &= ~CFG_STATIC_CHANNEL;
James Ketrenosc848d0a2005-08-24 21:56:24 -05007578 IPW_DEBUG_ASSOC("Attempting to associate with new "
7579 "parameters.\n");
7580 ipw_associate(priv);
James Ketrenos43f66a62005-03-25 12:31:53 -06007581 return 0;
7582 }
7583
7584 priv->config |= CFG_STATIC_CHANNEL;
7585
7586 if (priv->channel == channel) {
Jeff Garzik0edd5b42005-09-07 00:48:31 -04007587 IPW_DEBUG_INFO("Request to set channel to current value (%d)\n",
7588 channel);
James Ketrenos43f66a62005-03-25 12:31:53 -06007589 return 0;
7590 }
7591
7592 IPW_DEBUG_INFO("Setting channel to %i\n", (int)channel);
7593 priv->channel = channel;
7594
James Ketrenosb095c382005-08-24 22:04:42 -05007595#ifdef CONFIG_IPW2200_MONITOR
7596 if (priv->ieee->iw_mode == IW_MODE_MONITOR) {
7597 if (priv->status & STATUS_SCANNING) {
7598 IPW_DEBUG_SCAN("scan abort triggered due to "
7599 "channel change.\n");
7600 queue_work(priv->workqueue, &priv->abort_scan);
7601 }
7602
7603 for (i = 1000; i && (priv->status & STATUS_SCANNING); i--)
7604 udelay(10);
7605
7606 if (priv->status & STATUS_SCANNING)
7607 IPW_DEBUG_SCAN("Still scanning...\n");
7608 else
7609 IPW_DEBUG_SCAN("Took %dms to abort current scan\n",
7610 1000 - i);
7611
7612 return 0;
7613 }
7614#endif /* CONFIG_IPW2200_MONITOR */
7615
James Ketrenosc848d0a2005-08-24 21:56:24 -05007616 /* Network configuration changed -- force [re]association */
7617 IPW_DEBUG_ASSOC("[re]association triggered due to channel change.\n");
7618 if (!ipw_disassociate(priv))
James Ketrenos43f66a62005-03-25 12:31:53 -06007619 ipw_associate(priv);
James Ketrenos43f66a62005-03-25 12:31:53 -06007620
7621 return 0;
7622}
7623
Jeff Garzikbf794512005-07-31 13:07:26 -04007624static int ipw_wx_set_freq(struct net_device *dev,
7625 struct iw_request_info *info,
7626 union iwreq_data *wrqu, char *extra)
James Ketrenos43f66a62005-03-25 12:31:53 -06007627{
7628 struct ipw_priv *priv = ieee80211_priv(dev);
7629 struct iw_freq *fwrq = &wrqu->freq;
James Ketrenosc848d0a2005-08-24 21:56:24 -05007630 int ret = 0;
James Ketrenosb095c382005-08-24 22:04:42 -05007631 u8 channel;
7632
7633 if (fwrq->m == 0) {
7634 IPW_DEBUG_WX("SET Freq/Channel -> any\n");
7635 down(&priv->sem);
7636 ret = ipw_set_channel(priv, 0);
7637 up(&priv->sem);
7638 return ret;
7639 }
Jeff Garzikbf794512005-07-31 13:07:26 -04007640
James Ketrenos43f66a62005-03-25 12:31:53 -06007641 /* if setting by freq convert to channel */
7642 if (fwrq->e == 1) {
James Ketrenosb095c382005-08-24 22:04:42 -05007643 channel = ieee80211_freq_to_channel(priv->ieee, fwrq->m);
7644 if (channel == 0)
7645 return -EINVAL;
7646 } else
7647 channel = fwrq->m;
Jeff Garzikbf794512005-07-31 13:07:26 -04007648
James Ketrenosb095c382005-08-24 22:04:42 -05007649 if (!ieee80211_is_valid_channel(priv->ieee, channel))
7650 return -EINVAL;
James Ketrenos43f66a62005-03-25 12:31:53 -06007651
7652 IPW_DEBUG_WX("SET Freq/Channel -> %d \n", fwrq->m);
James Ketrenosc848d0a2005-08-24 21:56:24 -05007653 down(&priv->sem);
James Ketrenosb095c382005-08-24 22:04:42 -05007654 ret = ipw_set_channel(priv, channel);
James Ketrenosc848d0a2005-08-24 21:56:24 -05007655 up(&priv->sem);
7656 return ret;
James Ketrenos43f66a62005-03-25 12:31:53 -06007657}
7658
Jeff Garzikbf794512005-07-31 13:07:26 -04007659static int ipw_wx_get_freq(struct net_device *dev,
7660 struct iw_request_info *info,
James Ketrenos43f66a62005-03-25 12:31:53 -06007661 union iwreq_data *wrqu, char *extra)
7662{
7663 struct ipw_priv *priv = ieee80211_priv(dev);
7664
7665 wrqu->freq.e = 0;
7666
7667 /* If we are associated, trying to associate, or have a statically
7668 * configured CHANNEL then return that; otherwise return ANY */
James Ketrenosc848d0a2005-08-24 21:56:24 -05007669 down(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06007670 if (priv->config & CFG_STATIC_CHANNEL ||
7671 priv->status & (STATUS_ASSOCIATING | STATUS_ASSOCIATED))
7672 wrqu->freq.m = priv->channel;
Jeff Garzikbf794512005-07-31 13:07:26 -04007673 else
James Ketrenos43f66a62005-03-25 12:31:53 -06007674 wrqu->freq.m = 0;
7675
James Ketrenosc848d0a2005-08-24 21:56:24 -05007676 up(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06007677 IPW_DEBUG_WX("GET Freq/Channel -> %d \n", priv->channel);
7678 return 0;
7679}
7680
Jeff Garzikbf794512005-07-31 13:07:26 -04007681static int ipw_wx_set_mode(struct net_device *dev,
7682 struct iw_request_info *info,
James Ketrenos43f66a62005-03-25 12:31:53 -06007683 union iwreq_data *wrqu, char *extra)
7684{
7685 struct ipw_priv *priv = ieee80211_priv(dev);
7686 int err = 0;
7687
7688 IPW_DEBUG_WX("Set MODE: %d\n", wrqu->mode);
James Ketrenos43f66a62005-03-25 12:31:53 -06007689
7690 switch (wrqu->mode) {
James Ketrenosb095c382005-08-24 22:04:42 -05007691#ifdef CONFIG_IPW2200_MONITOR
James Ketrenos43f66a62005-03-25 12:31:53 -06007692 case IW_MODE_MONITOR:
7693#endif
7694 case IW_MODE_ADHOC:
7695 case IW_MODE_INFRA:
7696 break;
7697 case IW_MODE_AUTO:
7698 wrqu->mode = IW_MODE_INFRA;
7699 break;
7700 default:
7701 return -EINVAL;
7702 }
James Ketrenosb095c382005-08-24 22:04:42 -05007703 if (wrqu->mode == priv->ieee->iw_mode)
7704 return 0;
James Ketrenos43f66a62005-03-25 12:31:53 -06007705
James Ketrenosb095c382005-08-24 22:04:42 -05007706 down(&priv->sem);
7707#ifdef CONFIG_IPW2200_MONITOR
Jeff Garzikbf794512005-07-31 13:07:26 -04007708 if (priv->ieee->iw_mode == IW_MODE_MONITOR)
James Ketrenos43f66a62005-03-25 12:31:53 -06007709 priv->net_dev->type = ARPHRD_ETHER;
Jeff Garzikbf794512005-07-31 13:07:26 -04007710
7711 if (wrqu->mode == IW_MODE_MONITOR)
James Ketrenos43f66a62005-03-25 12:31:53 -06007712 priv->net_dev->type = ARPHRD_IEEE80211;
James Ketrenosb095c382005-08-24 22:04:42 -05007713#endif /* CONFIG_IPW2200_MONITOR */
Jeff Garzikbf794512005-07-31 13:07:26 -04007714
James Ketrenos43f66a62005-03-25 12:31:53 -06007715#ifdef CONFIG_PM
Jeff Garzikbf794512005-07-31 13:07:26 -04007716 /* Free the existing firmware and reset the fw_loaded
James Ketrenos43f66a62005-03-25 12:31:53 -06007717 * flag so ipw_load() will bring in the new firmawre */
James Ketrenosa613bff2005-08-24 21:43:11 -05007718 if (fw_loaded)
James Ketrenos43f66a62005-03-25 12:31:53 -06007719 fw_loaded = 0;
James Ketrenos43f66a62005-03-25 12:31:53 -06007720
7721 release_firmware(bootfw);
7722 release_firmware(ucode);
7723 release_firmware(firmware);
7724 bootfw = ucode = firmware = NULL;
7725#endif
7726
7727 priv->ieee->iw_mode = wrqu->mode;
Jeff Garzikbf794512005-07-31 13:07:26 -04007728
James Ketrenosc848d0a2005-08-24 21:56:24 -05007729 queue_work(priv->workqueue, &priv->adapter_restart);
7730 up(&priv->sem);
Jeff Garzik0edd5b42005-09-07 00:48:31 -04007731 return err;
James Ketrenos43f66a62005-03-25 12:31:53 -06007732}
7733
Jeff Garzikbf794512005-07-31 13:07:26 -04007734static int ipw_wx_get_mode(struct net_device *dev,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04007735 struct iw_request_info *info,
7736 union iwreq_data *wrqu, char *extra)
James Ketrenos43f66a62005-03-25 12:31:53 -06007737{
7738 struct ipw_priv *priv = ieee80211_priv(dev);
James Ketrenosc848d0a2005-08-24 21:56:24 -05007739 down(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06007740 wrqu->mode = priv->ieee->iw_mode;
7741 IPW_DEBUG_WX("Get MODE -> %d\n", wrqu->mode);
James Ketrenosc848d0a2005-08-24 21:56:24 -05007742 up(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06007743 return 0;
7744}
7745
James Ketrenos43f66a62005-03-25 12:31:53 -06007746#define DEFAULT_RTS_THRESHOLD 2304U
7747#define MIN_RTS_THRESHOLD 1U
7748#define MAX_RTS_THRESHOLD 2304U
7749#define DEFAULT_BEACON_INTERVAL 100U
7750#define DEFAULT_SHORT_RETRY_LIMIT 7U
7751#define DEFAULT_LONG_RETRY_LIMIT 4U
7752
7753/* Values are in microsecond */
7754static const s32 timeout_duration[] = {
7755 350000,
7756 250000,
7757 75000,
7758 37000,
7759 25000,
7760};
7761
7762static const s32 period_duration[] = {
7763 400000,
7764 700000,
7765 1000000,
7766 1000000,
7767 1000000
7768};
7769
Jeff Garzikbf794512005-07-31 13:07:26 -04007770static int ipw_wx_get_range(struct net_device *dev,
7771 struct iw_request_info *info,
James Ketrenos43f66a62005-03-25 12:31:53 -06007772 union iwreq_data *wrqu, char *extra)
7773{
7774 struct ipw_priv *priv = ieee80211_priv(dev);
7775 struct iw_range *range = (struct iw_range *)extra;
James Ketrenosb095c382005-08-24 22:04:42 -05007776 const struct ieee80211_geo *geo = ieee80211_get_geo(priv->ieee);
7777 int i = 0, j;
James Ketrenos43f66a62005-03-25 12:31:53 -06007778
7779 wrqu->data.length = sizeof(*range);
7780 memset(range, 0, sizeof(*range));
7781
7782 /* 54Mbs == ~27 Mb/s real (802.11g) */
Jeff Garzikbf794512005-07-31 13:07:26 -04007783 range->throughput = 27 * 1000 * 1000;
James Ketrenos43f66a62005-03-25 12:31:53 -06007784
7785 range->max_qual.qual = 100;
7786 /* TODO: Find real max RSSI and stick here */
7787 range->max_qual.level = 0;
James Ketrenosc848d0a2005-08-24 21:56:24 -05007788 range->max_qual.noise = priv->ieee->worst_rssi + 0x100;
Jeff Garzik0edd5b42005-09-07 00:48:31 -04007789 range->max_qual.updated = 7; /* Updated all three */
James Ketrenos43f66a62005-03-25 12:31:53 -06007790
7791 range->avg_qual.qual = 70;
7792 /* TODO: Find real 'good' to 'bad' threshol value for RSSI */
Jeff Garzik0edd5b42005-09-07 00:48:31 -04007793 range->avg_qual.level = 0; /* FIXME to real average level */
James Ketrenos43f66a62005-03-25 12:31:53 -06007794 range->avg_qual.noise = 0;
Jeff Garzik0edd5b42005-09-07 00:48:31 -04007795 range->avg_qual.updated = 7; /* Updated all three */
James Ketrenosc848d0a2005-08-24 21:56:24 -05007796 down(&priv->sem);
Jeff Garzik0edd5b42005-09-07 00:48:31 -04007797 range->num_bitrates = min(priv->rates.num_rates, (u8) IW_MAX_BITRATES);
James Ketrenos43f66a62005-03-25 12:31:53 -06007798
Jeff Garzikbf794512005-07-31 13:07:26 -04007799 for (i = 0; i < range->num_bitrates; i++)
7800 range->bitrate[i] = (priv->rates.supported_rates[i] & 0x7F) *
Jeff Garzik0edd5b42005-09-07 00:48:31 -04007801 500000;
Jeff Garzikbf794512005-07-31 13:07:26 -04007802
James Ketrenos43f66a62005-03-25 12:31:53 -06007803 range->max_rts = DEFAULT_RTS_THRESHOLD;
7804 range->min_frag = MIN_FRAG_THRESHOLD;
7805 range->max_frag = MAX_FRAG_THRESHOLD;
7806
7807 range->encoding_size[0] = 5;
Jeff Garzikbf794512005-07-31 13:07:26 -04007808 range->encoding_size[1] = 13;
James Ketrenos43f66a62005-03-25 12:31:53 -06007809 range->num_encoding_sizes = 2;
7810 range->max_encoding_tokens = WEP_KEYS;
7811
7812 /* Set the Wireless Extension versions */
7813 range->we_version_compiled = WIRELESS_EXT;
7814 range->we_version_source = 16;
7815
James Ketrenosb095c382005-08-24 22:04:42 -05007816 i = 0;
7817 if (priv->ieee->mode & (IEEE_B | IEEE_G)) {
7818 for (j = 0; j < geo->bg_channels && i < IW_MAX_FREQUENCIES;
7819 i++, j++) {
7820 range->freq[i].i = geo->bg[j].channel;
7821 range->freq[i].m = geo->bg[j].freq * 100000;
7822 range->freq[i].e = 1;
7823 }
James Ketrenos43f66a62005-03-25 12:31:53 -06007824 }
James Ketrenosb095c382005-08-24 22:04:42 -05007825
7826 if (priv->ieee->mode & IEEE_A) {
7827 for (j = 0; j < geo->a_channels && i < IW_MAX_FREQUENCIES;
7828 i++, j++) {
7829 range->freq[i].i = geo->a[j].channel;
7830 range->freq[i].m = geo->a[j].freq * 100000;
7831 range->freq[i].e = 1;
7832 }
7833 }
7834
7835 range->num_channels = i;
7836 range->num_frequency = i;
7837
James Ketrenosc848d0a2005-08-24 21:56:24 -05007838 up(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06007839 IPW_DEBUG_WX("GET Range\n");
7840 return 0;
7841}
7842
Jeff Garzikbf794512005-07-31 13:07:26 -04007843static int ipw_wx_set_wap(struct net_device *dev,
7844 struct iw_request_info *info,
James Ketrenos43f66a62005-03-25 12:31:53 -06007845 union iwreq_data *wrqu, char *extra)
7846{
7847 struct ipw_priv *priv = ieee80211_priv(dev);
7848
7849 static const unsigned char any[] = {
7850 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
7851 };
7852 static const unsigned char off[] = {
7853 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
7854 };
7855
Jeff Garzikbf794512005-07-31 13:07:26 -04007856 if (wrqu->ap_addr.sa_family != ARPHRD_ETHER)
James Ketrenos43f66a62005-03-25 12:31:53 -06007857 return -EINVAL;
James Ketrenosc848d0a2005-08-24 21:56:24 -05007858 down(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06007859 if (!memcmp(any, wrqu->ap_addr.sa_data, ETH_ALEN) ||
7860 !memcmp(off, wrqu->ap_addr.sa_data, ETH_ALEN)) {
7861 /* we disable mandatory BSSID association */
7862 IPW_DEBUG_WX("Setting AP BSSID to ANY\n");
7863 priv->config &= ~CFG_STATIC_BSSID;
James Ketrenosc848d0a2005-08-24 21:56:24 -05007864 IPW_DEBUG_ASSOC("Attempting to associate with new "
7865 "parameters.\n");
7866 ipw_associate(priv);
7867 up(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06007868 return 0;
7869 }
7870
7871 priv->config |= CFG_STATIC_BSSID;
7872 if (!memcmp(priv->bssid, wrqu->ap_addr.sa_data, ETH_ALEN)) {
7873 IPW_DEBUG_WX("BSSID set to current BSSID.\n");
James Ketrenosc848d0a2005-08-24 21:56:24 -05007874 up(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06007875 return 0;
7876 }
7877
7878 IPW_DEBUG_WX("Setting mandatory BSSID to " MAC_FMT "\n",
7879 MAC_ARG(wrqu->ap_addr.sa_data));
7880
7881 memcpy(priv->bssid, wrqu->ap_addr.sa_data, ETH_ALEN);
7882
James Ketrenosc848d0a2005-08-24 21:56:24 -05007883 /* Network configuration changed -- force [re]association */
7884 IPW_DEBUG_ASSOC("[re]association triggered due to BSSID change.\n");
7885 if (!ipw_disassociate(priv))
James Ketrenos43f66a62005-03-25 12:31:53 -06007886 ipw_associate(priv);
James Ketrenos43f66a62005-03-25 12:31:53 -06007887
James Ketrenosc848d0a2005-08-24 21:56:24 -05007888 up(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06007889 return 0;
7890}
7891
Jeff Garzikbf794512005-07-31 13:07:26 -04007892static int ipw_wx_get_wap(struct net_device *dev,
7893 struct iw_request_info *info,
James Ketrenos43f66a62005-03-25 12:31:53 -06007894 union iwreq_data *wrqu, char *extra)
7895{
7896 struct ipw_priv *priv = ieee80211_priv(dev);
7897 /* If we are associated, trying to associate, or have a statically
7898 * configured BSSID then return that; otherwise return ANY */
James Ketrenosc848d0a2005-08-24 21:56:24 -05007899 down(&priv->sem);
Jeff Garzikbf794512005-07-31 13:07:26 -04007900 if (priv->config & CFG_STATIC_BSSID ||
James Ketrenos43f66a62005-03-25 12:31:53 -06007901 priv->status & (STATUS_ASSOCIATED | STATUS_ASSOCIATING)) {
7902 wrqu->ap_addr.sa_family = ARPHRD_ETHER;
7903 memcpy(wrqu->ap_addr.sa_data, &priv->bssid, ETH_ALEN);
7904 } else
7905 memset(wrqu->ap_addr.sa_data, 0, ETH_ALEN);
7906
7907 IPW_DEBUG_WX("Getting WAP BSSID: " MAC_FMT "\n",
7908 MAC_ARG(wrqu->ap_addr.sa_data));
James Ketrenosc848d0a2005-08-24 21:56:24 -05007909 up(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06007910 return 0;
7911}
7912
Jeff Garzikbf794512005-07-31 13:07:26 -04007913static int ipw_wx_set_essid(struct net_device *dev,
7914 struct iw_request_info *info,
James Ketrenos43f66a62005-03-25 12:31:53 -06007915 union iwreq_data *wrqu, char *extra)
7916{
7917 struct ipw_priv *priv = ieee80211_priv(dev);
Jeff Garzik0edd5b42005-09-07 00:48:31 -04007918 char *essid = ""; /* ANY */
James Ketrenos43f66a62005-03-25 12:31:53 -06007919 int length = 0;
James Ketrenosc848d0a2005-08-24 21:56:24 -05007920 down(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06007921 if (wrqu->essid.flags && wrqu->essid.length) {
7922 length = wrqu->essid.length - 1;
7923 essid = extra;
7924 }
7925 if (length == 0) {
7926 IPW_DEBUG_WX("Setting ESSID to ANY\n");
7927 priv->config &= ~CFG_STATIC_ESSID;
James Ketrenosc848d0a2005-08-24 21:56:24 -05007928 if (!(priv->status & (STATUS_ASSOCIATED | STATUS_ASSOCIATING))) {
James Ketrenos43f66a62005-03-25 12:31:53 -06007929 IPW_DEBUG_ASSOC("Attempting to associate with new "
7930 "parameters.\n");
7931 ipw_associate(priv);
7932 }
James Ketrenosc848d0a2005-08-24 21:56:24 -05007933 up(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06007934 return 0;
7935 }
7936
7937 length = min(length, IW_ESSID_MAX_SIZE);
7938
7939 priv->config |= CFG_STATIC_ESSID;
7940
7941 if (priv->essid_len == length && !memcmp(priv->essid, extra, length)) {
7942 IPW_DEBUG_WX("ESSID set to current ESSID.\n");
James Ketrenosc848d0a2005-08-24 21:56:24 -05007943 up(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06007944 return 0;
7945 }
7946
7947 IPW_DEBUG_WX("Setting ESSID: '%s' (%d)\n", escape_essid(essid, length),
7948 length);
7949
7950 priv->essid_len = length;
7951 memcpy(priv->essid, essid, priv->essid_len);
Jeff Garzikbf794512005-07-31 13:07:26 -04007952
James Ketrenosc848d0a2005-08-24 21:56:24 -05007953 /* Network configuration changed -- force [re]association */
7954 IPW_DEBUG_ASSOC("[re]association triggered due to ESSID change.\n");
7955 if (!ipw_disassociate(priv))
James Ketrenos43f66a62005-03-25 12:31:53 -06007956 ipw_associate(priv);
James Ketrenos43f66a62005-03-25 12:31:53 -06007957
James Ketrenosc848d0a2005-08-24 21:56:24 -05007958 up(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06007959 return 0;
7960}
7961
Jeff Garzikbf794512005-07-31 13:07:26 -04007962static int ipw_wx_get_essid(struct net_device *dev,
7963 struct iw_request_info *info,
James Ketrenos43f66a62005-03-25 12:31:53 -06007964 union iwreq_data *wrqu, char *extra)
7965{
7966 struct ipw_priv *priv = ieee80211_priv(dev);
7967
7968 /* If we are associated, trying to associate, or have a statically
7969 * configured ESSID then return that; otherwise return ANY */
James Ketrenosc848d0a2005-08-24 21:56:24 -05007970 down(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06007971 if (priv->config & CFG_STATIC_ESSID ||
Jeff Garzikbf794512005-07-31 13:07:26 -04007972 priv->status & (STATUS_ASSOCIATED | STATUS_ASSOCIATING)) {
7973 IPW_DEBUG_WX("Getting essid: '%s'\n",
James Ketrenos43f66a62005-03-25 12:31:53 -06007974 escape_essid(priv->essid, priv->essid_len));
Jeff Garzikbf794512005-07-31 13:07:26 -04007975 memcpy(extra, priv->essid, priv->essid_len);
James Ketrenos43f66a62005-03-25 12:31:53 -06007976 wrqu->essid.length = priv->essid_len;
Jeff Garzik0edd5b42005-09-07 00:48:31 -04007977 wrqu->essid.flags = 1; /* active */
James Ketrenos43f66a62005-03-25 12:31:53 -06007978 } else {
7979 IPW_DEBUG_WX("Getting essid: ANY\n");
7980 wrqu->essid.length = 0;
Jeff Garzik0edd5b42005-09-07 00:48:31 -04007981 wrqu->essid.flags = 0; /* active */
James Ketrenos43f66a62005-03-25 12:31:53 -06007982 }
James Ketrenosc848d0a2005-08-24 21:56:24 -05007983 up(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06007984 return 0;
7985}
7986
Jeff Garzikbf794512005-07-31 13:07:26 -04007987static int ipw_wx_set_nick(struct net_device *dev,
7988 struct iw_request_info *info,
James Ketrenos43f66a62005-03-25 12:31:53 -06007989 union iwreq_data *wrqu, char *extra)
Jeff Garzikbf794512005-07-31 13:07:26 -04007990{
James Ketrenos43f66a62005-03-25 12:31:53 -06007991 struct ipw_priv *priv = ieee80211_priv(dev);
7992
7993 IPW_DEBUG_WX("Setting nick to '%s'\n", extra);
7994 if (wrqu->data.length > IW_ESSID_MAX_SIZE)
7995 return -E2BIG;
James Ketrenosc848d0a2005-08-24 21:56:24 -05007996 down(&priv->sem);
Jeff Garzik0edd5b42005-09-07 00:48:31 -04007997 wrqu->data.length = min((size_t) wrqu->data.length, sizeof(priv->nick));
James Ketrenos43f66a62005-03-25 12:31:53 -06007998 memset(priv->nick, 0, sizeof(priv->nick));
Jeff Garzik0edd5b42005-09-07 00:48:31 -04007999 memcpy(priv->nick, extra, wrqu->data.length);
James Ketrenos43f66a62005-03-25 12:31:53 -06008000 IPW_DEBUG_TRACE("<<\n");
James Ketrenosc848d0a2005-08-24 21:56:24 -05008001 up(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06008002 return 0;
8003
8004}
8005
Jeff Garzikbf794512005-07-31 13:07:26 -04008006static int ipw_wx_get_nick(struct net_device *dev,
8007 struct iw_request_info *info,
James Ketrenos43f66a62005-03-25 12:31:53 -06008008 union iwreq_data *wrqu, char *extra)
Jeff Garzikbf794512005-07-31 13:07:26 -04008009{
James Ketrenos43f66a62005-03-25 12:31:53 -06008010 struct ipw_priv *priv = ieee80211_priv(dev);
8011 IPW_DEBUG_WX("Getting nick\n");
James Ketrenosc848d0a2005-08-24 21:56:24 -05008012 down(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06008013 wrqu->data.length = strlen(priv->nick) + 1;
8014 memcpy(extra, priv->nick, wrqu->data.length);
Jeff Garzik0edd5b42005-09-07 00:48:31 -04008015 wrqu->data.flags = 1; /* active */
James Ketrenosc848d0a2005-08-24 21:56:24 -05008016 up(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06008017 return 0;
8018}
8019
James Ketrenos43f66a62005-03-25 12:31:53 -06008020static int ipw_wx_set_rate(struct net_device *dev,
8021 struct iw_request_info *info,
8022 union iwreq_data *wrqu, char *extra)
Jeff Garzikbf794512005-07-31 13:07:26 -04008023{
James Ketrenosea2b26e2005-08-24 21:25:16 -05008024 /* TODO: We should use semaphores or locks for access to priv */
8025 struct ipw_priv *priv = ieee80211_priv(dev);
8026 u32 target_rate = wrqu->bitrate.value;
8027 u32 fixed, mask;
8028
8029 /* value = -1, fixed = 0 means auto only, so we should use all rates offered by AP */
8030 /* value = X, fixed = 1 means only rate X */
8031 /* value = X, fixed = 0 means all rates lower equal X */
8032
8033 if (target_rate == -1) {
8034 fixed = 0;
8035 mask = IEEE80211_DEFAULT_RATES_MASK;
8036 /* Now we should reassociate */
8037 goto apply;
8038 }
8039
8040 mask = 0;
8041 fixed = wrqu->bitrate.fixed;
8042
8043 if (target_rate == 1000000 || !fixed)
8044 mask |= IEEE80211_CCK_RATE_1MB_MASK;
8045 if (target_rate == 1000000)
8046 goto apply;
8047
8048 if (target_rate == 2000000 || !fixed)
8049 mask |= IEEE80211_CCK_RATE_2MB_MASK;
8050 if (target_rate == 2000000)
8051 goto apply;
8052
8053 if (target_rate == 5500000 || !fixed)
8054 mask |= IEEE80211_CCK_RATE_5MB_MASK;
8055 if (target_rate == 5500000)
8056 goto apply;
8057
8058 if (target_rate == 6000000 || !fixed)
8059 mask |= IEEE80211_OFDM_RATE_6MB_MASK;
8060 if (target_rate == 6000000)
8061 goto apply;
8062
8063 if (target_rate == 9000000 || !fixed)
8064 mask |= IEEE80211_OFDM_RATE_9MB_MASK;
8065 if (target_rate == 9000000)
8066 goto apply;
8067
8068 if (target_rate == 11000000 || !fixed)
8069 mask |= IEEE80211_CCK_RATE_11MB_MASK;
8070 if (target_rate == 11000000)
8071 goto apply;
8072
8073 if (target_rate == 12000000 || !fixed)
8074 mask |= IEEE80211_OFDM_RATE_12MB_MASK;
8075 if (target_rate == 12000000)
8076 goto apply;
8077
8078 if (target_rate == 18000000 || !fixed)
8079 mask |= IEEE80211_OFDM_RATE_18MB_MASK;
8080 if (target_rate == 18000000)
8081 goto apply;
8082
8083 if (target_rate == 24000000 || !fixed)
8084 mask |= IEEE80211_OFDM_RATE_24MB_MASK;
8085 if (target_rate == 24000000)
8086 goto apply;
8087
8088 if (target_rate == 36000000 || !fixed)
8089 mask |= IEEE80211_OFDM_RATE_36MB_MASK;
8090 if (target_rate == 36000000)
8091 goto apply;
8092
8093 if (target_rate == 48000000 || !fixed)
8094 mask |= IEEE80211_OFDM_RATE_48MB_MASK;
8095 if (target_rate == 48000000)
8096 goto apply;
8097
8098 if (target_rate == 54000000 || !fixed)
8099 mask |= IEEE80211_OFDM_RATE_54MB_MASK;
8100 if (target_rate == 54000000)
8101 goto apply;
8102
8103 IPW_DEBUG_WX("invalid rate specified, returning error\n");
8104 return -EINVAL;
8105
8106 apply:
8107 IPW_DEBUG_WX("Setting rate mask to 0x%08X [%s]\n",
8108 mask, fixed ? "fixed" : "sub-rates");
James Ketrenosc848d0a2005-08-24 21:56:24 -05008109 down(&priv->sem);
James Ketrenosb095c382005-08-24 22:04:42 -05008110 if (mask == IEEE80211_DEFAULT_RATES_MASK) {
James Ketrenosea2b26e2005-08-24 21:25:16 -05008111 priv->config &= ~CFG_FIXED_RATE;
James Ketrenosb095c382005-08-24 22:04:42 -05008112 ipw_set_fixed_rate(priv, priv->ieee->mode);
8113 } else
James Ketrenosea2b26e2005-08-24 21:25:16 -05008114 priv->config |= CFG_FIXED_RATE;
8115
James Ketrenosc848d0a2005-08-24 21:56:24 -05008116 if (priv->rates_mask == mask) {
8117 IPW_DEBUG_WX("Mask set to current mask.\n");
8118 up(&priv->sem);
8119 return 0;
James Ketrenosea2b26e2005-08-24 21:25:16 -05008120 }
8121
James Ketrenosc848d0a2005-08-24 21:56:24 -05008122 priv->rates_mask = mask;
8123
8124 /* Network configuration changed -- force [re]association */
8125 IPW_DEBUG_ASSOC("[re]association triggered due to rates change.\n");
8126 if (!ipw_disassociate(priv))
8127 ipw_associate(priv);
8128
8129 up(&priv->sem);
James Ketrenosea2b26e2005-08-24 21:25:16 -05008130 return 0;
James Ketrenos43f66a62005-03-25 12:31:53 -06008131}
8132
Jeff Garzikbf794512005-07-31 13:07:26 -04008133static int ipw_wx_get_rate(struct net_device *dev,
8134 struct iw_request_info *info,
James Ketrenos43f66a62005-03-25 12:31:53 -06008135 union iwreq_data *wrqu, char *extra)
Jeff Garzikbf794512005-07-31 13:07:26 -04008136{
Jeff Garzik0edd5b42005-09-07 00:48:31 -04008137 struct ipw_priv *priv = ieee80211_priv(dev);
James Ketrenosc848d0a2005-08-24 21:56:24 -05008138 down(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06008139 wrqu->bitrate.value = priv->last_rate;
James Ketrenosc848d0a2005-08-24 21:56:24 -05008140 up(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06008141 IPW_DEBUG_WX("GET Rate -> %d \n", wrqu->bitrate.value);
8142 return 0;
8143}
8144
Jeff Garzikbf794512005-07-31 13:07:26 -04008145static int ipw_wx_set_rts(struct net_device *dev,
8146 struct iw_request_info *info,
James Ketrenos43f66a62005-03-25 12:31:53 -06008147 union iwreq_data *wrqu, char *extra)
Jeff Garzikbf794512005-07-31 13:07:26 -04008148{
James Ketrenos43f66a62005-03-25 12:31:53 -06008149 struct ipw_priv *priv = ieee80211_priv(dev);
James Ketrenosc848d0a2005-08-24 21:56:24 -05008150 down(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06008151 if (wrqu->rts.disabled)
8152 priv->rts_threshold = DEFAULT_RTS_THRESHOLD;
8153 else {
8154 if (wrqu->rts.value < MIN_RTS_THRESHOLD ||
James Ketrenosc848d0a2005-08-24 21:56:24 -05008155 wrqu->rts.value > MAX_RTS_THRESHOLD) {
8156 up(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06008157 return -EINVAL;
James Ketrenosc848d0a2005-08-24 21:56:24 -05008158 }
James Ketrenos43f66a62005-03-25 12:31:53 -06008159 priv->rts_threshold = wrqu->rts.value;
8160 }
8161
8162 ipw_send_rts_threshold(priv, priv->rts_threshold);
James Ketrenosc848d0a2005-08-24 21:56:24 -05008163 up(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06008164 IPW_DEBUG_WX("SET RTS Threshold -> %d \n", priv->rts_threshold);
8165 return 0;
8166}
8167
Jeff Garzikbf794512005-07-31 13:07:26 -04008168static int ipw_wx_get_rts(struct net_device *dev,
8169 struct iw_request_info *info,
James Ketrenos43f66a62005-03-25 12:31:53 -06008170 union iwreq_data *wrqu, char *extra)
Jeff Garzikbf794512005-07-31 13:07:26 -04008171{
James Ketrenos43f66a62005-03-25 12:31:53 -06008172 struct ipw_priv *priv = ieee80211_priv(dev);
James Ketrenosc848d0a2005-08-24 21:56:24 -05008173 down(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06008174 wrqu->rts.value = priv->rts_threshold;
8175 wrqu->rts.fixed = 0; /* no auto select */
Jeff Garzik0edd5b42005-09-07 00:48:31 -04008176 wrqu->rts.disabled = (wrqu->rts.value == DEFAULT_RTS_THRESHOLD);
James Ketrenosc848d0a2005-08-24 21:56:24 -05008177 up(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06008178 IPW_DEBUG_WX("GET RTS Threshold -> %d \n", wrqu->rts.value);
8179 return 0;
8180}
8181
Jeff Garzikbf794512005-07-31 13:07:26 -04008182static int ipw_wx_set_txpow(struct net_device *dev,
8183 struct iw_request_info *info,
James Ketrenos43f66a62005-03-25 12:31:53 -06008184 union iwreq_data *wrqu, char *extra)
Jeff Garzikbf794512005-07-31 13:07:26 -04008185{
James Ketrenos43f66a62005-03-25 12:31:53 -06008186 struct ipw_priv *priv = ieee80211_priv(dev);
8187 struct ipw_tx_power tx_power;
8188 int i;
James Ketrenosb095c382005-08-24 22:04:42 -05008189
James Ketrenosc848d0a2005-08-24 21:56:24 -05008190 down(&priv->sem);
8191 if (ipw_radio_kill_sw(priv, wrqu->power.disabled)) {
8192 up(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06008193 return -EINPROGRESS;
James Ketrenosc848d0a2005-08-24 21:56:24 -05008194 }
James Ketrenos43f66a62005-03-25 12:31:53 -06008195
James Ketrenosb095c382005-08-24 22:04:42 -05008196 if (!wrqu->power.fixed)
8197 wrqu->power.value = IPW_TX_POWER_DEFAULT;
8198
James Ketrenosc848d0a2005-08-24 21:56:24 -05008199 if (wrqu->power.flags != IW_TXPOW_DBM) {
8200 up(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06008201 return -EINVAL;
James Ketrenosc848d0a2005-08-24 21:56:24 -05008202 }
James Ketrenos43f66a62005-03-25 12:31:53 -06008203
James Ketrenosb095c382005-08-24 22:04:42 -05008204 if ((wrqu->power.value > IPW_TX_POWER_MAX) ||
8205 (wrqu->power.value < -IPW_TX_POWER_MAX) || !wrqu->power.fixed) {
James Ketrenosc848d0a2005-08-24 21:56:24 -05008206 up(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06008207 return -EINVAL;
James Ketrenosc848d0a2005-08-24 21:56:24 -05008208 }
James Ketrenos43f66a62005-03-25 12:31:53 -06008209
8210 priv->tx_power = wrqu->power.value;
8211
8212 memset(&tx_power, 0, sizeof(tx_power));
8213
8214 /* configure device for 'G' band */
8215 tx_power.ieee_mode = IPW_G_MODE;
8216 tx_power.num_channels = 11;
8217 for (i = 0; i < 11; i++) {
8218 tx_power.channels_tx_power[i].channel_number = i + 1;
8219 tx_power.channels_tx_power[i].tx_power = priv->tx_power;
8220 }
8221 if (ipw_send_tx_power(priv, &tx_power))
8222 goto error;
8223
8224 /* configure device to also handle 'B' band */
8225 tx_power.ieee_mode = IPW_B_MODE;
8226 if (ipw_send_tx_power(priv, &tx_power))
8227 goto error;
8228
James Ketrenosc848d0a2005-08-24 21:56:24 -05008229 up(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06008230 return 0;
8231
Jeff Garzik0edd5b42005-09-07 00:48:31 -04008232 error:
James Ketrenosc848d0a2005-08-24 21:56:24 -05008233 up(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06008234 return -EIO;
8235}
8236
Jeff Garzikbf794512005-07-31 13:07:26 -04008237static int ipw_wx_get_txpow(struct net_device *dev,
8238 struct iw_request_info *info,
James Ketrenos43f66a62005-03-25 12:31:53 -06008239 union iwreq_data *wrqu, char *extra)
Jeff Garzikbf794512005-07-31 13:07:26 -04008240{
James Ketrenos43f66a62005-03-25 12:31:53 -06008241 struct ipw_priv *priv = ieee80211_priv(dev);
James Ketrenosc848d0a2005-08-24 21:56:24 -05008242 down(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06008243 wrqu->power.value = priv->tx_power;
8244 wrqu->power.fixed = 1;
8245 wrqu->power.flags = IW_TXPOW_DBM;
8246 wrqu->power.disabled = (priv->status & STATUS_RF_KILL_MASK) ? 1 : 0;
James Ketrenosc848d0a2005-08-24 21:56:24 -05008247 up(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06008248
Jeff Garzikbf794512005-07-31 13:07:26 -04008249 IPW_DEBUG_WX("GET TX Power -> %s %d \n",
Jeff Garzik0edd5b42005-09-07 00:48:31 -04008250 wrqu->power.disabled ? "ON" : "OFF", wrqu->power.value);
James Ketrenos43f66a62005-03-25 12:31:53 -06008251
8252 return 0;
8253}
8254
Jeff Garzikbf794512005-07-31 13:07:26 -04008255static int ipw_wx_set_frag(struct net_device *dev,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04008256 struct iw_request_info *info,
8257 union iwreq_data *wrqu, char *extra)
James Ketrenos43f66a62005-03-25 12:31:53 -06008258{
8259 struct ipw_priv *priv = ieee80211_priv(dev);
James Ketrenosc848d0a2005-08-24 21:56:24 -05008260 down(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06008261 if (wrqu->frag.disabled)
8262 priv->ieee->fts = DEFAULT_FTS;
8263 else {
8264 if (wrqu->frag.value < MIN_FRAG_THRESHOLD ||
James Ketrenosb095c382005-08-24 22:04:42 -05008265 wrqu->frag.value > MAX_FRAG_THRESHOLD) {
8266 up(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06008267 return -EINVAL;
James Ketrenosb095c382005-08-24 22:04:42 -05008268 }
Jeff Garzikbf794512005-07-31 13:07:26 -04008269
James Ketrenos43f66a62005-03-25 12:31:53 -06008270 priv->ieee->fts = wrqu->frag.value & ~0x1;
8271 }
8272
8273 ipw_send_frag_threshold(priv, wrqu->frag.value);
James Ketrenosc848d0a2005-08-24 21:56:24 -05008274 up(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06008275 IPW_DEBUG_WX("SET Frag Threshold -> %d \n", wrqu->frag.value);
8276 return 0;
8277}
8278
Jeff Garzikbf794512005-07-31 13:07:26 -04008279static int ipw_wx_get_frag(struct net_device *dev,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04008280 struct iw_request_info *info,
8281 union iwreq_data *wrqu, char *extra)
James Ketrenos43f66a62005-03-25 12:31:53 -06008282{
8283 struct ipw_priv *priv = ieee80211_priv(dev);
James Ketrenosc848d0a2005-08-24 21:56:24 -05008284 down(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06008285 wrqu->frag.value = priv->ieee->fts;
8286 wrqu->frag.fixed = 0; /* no auto select */
Jeff Garzik0edd5b42005-09-07 00:48:31 -04008287 wrqu->frag.disabled = (wrqu->frag.value == DEFAULT_FTS);
James Ketrenosc848d0a2005-08-24 21:56:24 -05008288 up(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06008289 IPW_DEBUG_WX("GET Frag Threshold -> %d \n", wrqu->frag.value);
8290
8291 return 0;
8292}
8293
Jeff Garzikbf794512005-07-31 13:07:26 -04008294static int ipw_wx_set_retry(struct net_device *dev,
8295 struct iw_request_info *info,
James Ketrenos43f66a62005-03-25 12:31:53 -06008296 union iwreq_data *wrqu, char *extra)
Jeff Garzikbf794512005-07-31 13:07:26 -04008297{
James Ketrenos43f66a62005-03-25 12:31:53 -06008298 IPW_DEBUG_WX("0x%p, 0x%p, 0x%p\n", dev, info, wrqu);
Jeff Garzikbf794512005-07-31 13:07:26 -04008299 return -EOPNOTSUPP;
James Ketrenos43f66a62005-03-25 12:31:53 -06008300}
8301
Jeff Garzikbf794512005-07-31 13:07:26 -04008302static int ipw_wx_get_retry(struct net_device *dev,
8303 struct iw_request_info *info,
James Ketrenos43f66a62005-03-25 12:31:53 -06008304 union iwreq_data *wrqu, char *extra)
Jeff Garzikbf794512005-07-31 13:07:26 -04008305{
James Ketrenos43f66a62005-03-25 12:31:53 -06008306 IPW_DEBUG_WX("0x%p, 0x%p, 0x%p\n", dev, info, wrqu);
Jeff Garzikbf794512005-07-31 13:07:26 -04008307 return -EOPNOTSUPP;
James Ketrenos43f66a62005-03-25 12:31:53 -06008308}
8309
Jeff Garzikbf794512005-07-31 13:07:26 -04008310static int ipw_wx_set_scan(struct net_device *dev,
8311 struct iw_request_info *info,
James Ketrenos43f66a62005-03-25 12:31:53 -06008312 union iwreq_data *wrqu, char *extra)
8313{
8314 struct ipw_priv *priv = ieee80211_priv(dev);
8315 IPW_DEBUG_WX("Start scan\n");
James Ketrenosb095c382005-08-24 22:04:42 -05008316
8317 queue_work(priv->workqueue, &priv->request_scan);
8318
James Ketrenos43f66a62005-03-25 12:31:53 -06008319 return 0;
8320}
8321
Jeff Garzikbf794512005-07-31 13:07:26 -04008322static int ipw_wx_get_scan(struct net_device *dev,
8323 struct iw_request_info *info,
James Ketrenos43f66a62005-03-25 12:31:53 -06008324 union iwreq_data *wrqu, char *extra)
Jeff Garzikbf794512005-07-31 13:07:26 -04008325{
James Ketrenos43f66a62005-03-25 12:31:53 -06008326 struct ipw_priv *priv = ieee80211_priv(dev);
8327 return ieee80211_wx_get_scan(priv->ieee, info, wrqu, extra);
8328}
8329
Jeff Garzikbf794512005-07-31 13:07:26 -04008330static int ipw_wx_set_encode(struct net_device *dev,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04008331 struct iw_request_info *info,
8332 union iwreq_data *wrqu, char *key)
James Ketrenos43f66a62005-03-25 12:31:53 -06008333{
8334 struct ipw_priv *priv = ieee80211_priv(dev);
8335 return ieee80211_wx_set_encode(priv->ieee, info, wrqu, key);
8336}
8337
Jeff Garzikbf794512005-07-31 13:07:26 -04008338static int ipw_wx_get_encode(struct net_device *dev,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04008339 struct iw_request_info *info,
8340 union iwreq_data *wrqu, char *key)
James Ketrenos43f66a62005-03-25 12:31:53 -06008341{
8342 struct ipw_priv *priv = ieee80211_priv(dev);
8343 return ieee80211_wx_get_encode(priv->ieee, info, wrqu, key);
8344}
8345
Jeff Garzikbf794512005-07-31 13:07:26 -04008346static int ipw_wx_set_power(struct net_device *dev,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04008347 struct iw_request_info *info,
8348 union iwreq_data *wrqu, char *extra)
James Ketrenos43f66a62005-03-25 12:31:53 -06008349{
8350 struct ipw_priv *priv = ieee80211_priv(dev);
8351 int err;
James Ketrenosc848d0a2005-08-24 21:56:24 -05008352 down(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06008353 if (wrqu->power.disabled) {
8354 priv->power_mode = IPW_POWER_LEVEL(priv->power_mode);
8355 err = ipw_send_power_mode(priv, IPW_POWER_MODE_CAM);
8356 if (err) {
8357 IPW_DEBUG_WX("failed setting power mode.\n");
James Ketrenosc848d0a2005-08-24 21:56:24 -05008358 up(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06008359 return err;
8360 }
James Ketrenos43f66a62005-03-25 12:31:53 -06008361 IPW_DEBUG_WX("SET Power Management Mode -> off\n");
James Ketrenosc848d0a2005-08-24 21:56:24 -05008362 up(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06008363 return 0;
Jeff Garzikbf794512005-07-31 13:07:26 -04008364 }
James Ketrenos43f66a62005-03-25 12:31:53 -06008365
8366 switch (wrqu->power.flags & IW_POWER_MODE) {
Jeff Garzik0edd5b42005-09-07 00:48:31 -04008367 case IW_POWER_ON: /* If not specified */
8368 case IW_POWER_MODE: /* If set all mask */
8369 case IW_POWER_ALL_R: /* If explicitely state all */
James Ketrenos43f66a62005-03-25 12:31:53 -06008370 break;
Jeff Garzik0edd5b42005-09-07 00:48:31 -04008371 default: /* Otherwise we don't support it */
James Ketrenos43f66a62005-03-25 12:31:53 -06008372 IPW_DEBUG_WX("SET PM Mode: %X not supported.\n",
8373 wrqu->power.flags);
James Ketrenosc848d0a2005-08-24 21:56:24 -05008374 up(&priv->sem);
Jeff Garzikbf794512005-07-31 13:07:26 -04008375 return -EOPNOTSUPP;
James Ketrenos43f66a62005-03-25 12:31:53 -06008376 }
Jeff Garzikbf794512005-07-31 13:07:26 -04008377
James Ketrenos43f66a62005-03-25 12:31:53 -06008378 /* If the user hasn't specified a power management mode yet, default
8379 * to BATTERY */
Jeff Garzik0edd5b42005-09-07 00:48:31 -04008380 if (IPW_POWER_LEVEL(priv->power_mode) == IPW_POWER_AC)
James Ketrenos43f66a62005-03-25 12:31:53 -06008381 priv->power_mode = IPW_POWER_ENABLED | IPW_POWER_BATTERY;
Jeff Garzikbf794512005-07-31 13:07:26 -04008382 else
James Ketrenos43f66a62005-03-25 12:31:53 -06008383 priv->power_mode = IPW_POWER_ENABLED | priv->power_mode;
8384 err = ipw_send_power_mode(priv, IPW_POWER_LEVEL(priv->power_mode));
8385 if (err) {
8386 IPW_DEBUG_WX("failed setting power mode.\n");
James Ketrenosc848d0a2005-08-24 21:56:24 -05008387 up(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06008388 return err;
8389 }
8390
Jeff Garzik0edd5b42005-09-07 00:48:31 -04008391 IPW_DEBUG_WX("SET Power Management Mode -> 0x%02X\n", priv->power_mode);
James Ketrenosc848d0a2005-08-24 21:56:24 -05008392 up(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06008393 return 0;
8394}
8395
Jeff Garzikbf794512005-07-31 13:07:26 -04008396static int ipw_wx_get_power(struct net_device *dev,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04008397 struct iw_request_info *info,
8398 union iwreq_data *wrqu, char *extra)
James Ketrenos43f66a62005-03-25 12:31:53 -06008399{
8400 struct ipw_priv *priv = ieee80211_priv(dev);
James Ketrenosc848d0a2005-08-24 21:56:24 -05008401 down(&priv->sem);
James Ketrenosa613bff2005-08-24 21:43:11 -05008402 if (!(priv->power_mode & IPW_POWER_ENABLED))
James Ketrenos43f66a62005-03-25 12:31:53 -06008403 wrqu->power.disabled = 1;
James Ketrenosa613bff2005-08-24 21:43:11 -05008404 else
James Ketrenos43f66a62005-03-25 12:31:53 -06008405 wrqu->power.disabled = 0;
James Ketrenos43f66a62005-03-25 12:31:53 -06008406
James Ketrenosc848d0a2005-08-24 21:56:24 -05008407 up(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06008408 IPW_DEBUG_WX("GET Power Management Mode -> %02X\n", priv->power_mode);
Jeff Garzikbf794512005-07-31 13:07:26 -04008409
James Ketrenos43f66a62005-03-25 12:31:53 -06008410 return 0;
8411}
8412
Jeff Garzikbf794512005-07-31 13:07:26 -04008413static int ipw_wx_set_powermode(struct net_device *dev,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04008414 struct iw_request_info *info,
8415 union iwreq_data *wrqu, char *extra)
James Ketrenos43f66a62005-03-25 12:31:53 -06008416{
8417 struct ipw_priv *priv = ieee80211_priv(dev);
8418 int mode = *(int *)extra;
8419 int err;
James Ketrenosc848d0a2005-08-24 21:56:24 -05008420 down(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06008421 if ((mode < 1) || (mode > IPW_POWER_LIMIT)) {
8422 mode = IPW_POWER_AC;
8423 priv->power_mode = mode;
8424 } else {
8425 priv->power_mode = IPW_POWER_ENABLED | mode;
8426 }
Jeff Garzikbf794512005-07-31 13:07:26 -04008427
James Ketrenos43f66a62005-03-25 12:31:53 -06008428 if (priv->power_mode != mode) {
8429 err = ipw_send_power_mode(priv, mode);
Jeff Garzikbf794512005-07-31 13:07:26 -04008430
James Ketrenos43f66a62005-03-25 12:31:53 -06008431 if (err) {
8432 IPW_DEBUG_WX("failed setting power mode.\n");
James Ketrenosc848d0a2005-08-24 21:56:24 -05008433 up(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06008434 return err;
8435 }
8436 }
James Ketrenosc848d0a2005-08-24 21:56:24 -05008437 up(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06008438 return 0;
8439}
8440
8441#define MAX_WX_STRING 80
Jeff Garzikbf794512005-07-31 13:07:26 -04008442static int ipw_wx_get_powermode(struct net_device *dev,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04008443 struct iw_request_info *info,
8444 union iwreq_data *wrqu, char *extra)
James Ketrenos43f66a62005-03-25 12:31:53 -06008445{
8446 struct ipw_priv *priv = ieee80211_priv(dev);
8447 int level = IPW_POWER_LEVEL(priv->power_mode);
8448 char *p = extra;
8449
8450 p += snprintf(p, MAX_WX_STRING, "Power save level: %d ", level);
8451
8452 switch (level) {
8453 case IPW_POWER_AC:
8454 p += snprintf(p, MAX_WX_STRING - (p - extra), "(AC)");
8455 break;
8456 case IPW_POWER_BATTERY:
8457 p += snprintf(p, MAX_WX_STRING - (p - extra), "(BATTERY)");
8458 break;
8459 default:
8460 p += snprintf(p, MAX_WX_STRING - (p - extra),
Jeff Garzikbf794512005-07-31 13:07:26 -04008461 "(Timeout %dms, Period %dms)",
James Ketrenos43f66a62005-03-25 12:31:53 -06008462 timeout_duration[level - 1] / 1000,
8463 period_duration[level - 1] / 1000);
8464 }
8465
8466 if (!(priv->power_mode & IPW_POWER_ENABLED))
Jeff Garzik0edd5b42005-09-07 00:48:31 -04008467 p += snprintf(p, MAX_WX_STRING - (p - extra), " OFF");
James Ketrenos43f66a62005-03-25 12:31:53 -06008468
8469 wrqu->data.length = p - extra + 1;
8470
8471 return 0;
8472}
8473
8474static int ipw_wx_set_wireless_mode(struct net_device *dev,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04008475 struct iw_request_info *info,
8476 union iwreq_data *wrqu, char *extra)
James Ketrenos43f66a62005-03-25 12:31:53 -06008477{
Jeff Garzik0edd5b42005-09-07 00:48:31 -04008478 struct ipw_priv *priv = ieee80211_priv(dev);
James Ketrenos43f66a62005-03-25 12:31:53 -06008479 int mode = *(int *)extra;
8480 u8 band = 0, modulation = 0;
8481
8482 if (mode == 0 || mode & ~IEEE_MODE_MASK) {
Jeff Garzik0edd5b42005-09-07 00:48:31 -04008483 IPW_WARNING("Attempt to set invalid wireless mode: %d\n", mode);
James Ketrenos43f66a62005-03-25 12:31:53 -06008484 return -EINVAL;
8485 }
James Ketrenosc848d0a2005-08-24 21:56:24 -05008486 down(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06008487 if (priv->adapter == IPW_2915ABG) {
James Ketrenosa33a1982005-09-14 14:28:59 -05008488 priv->ieee->abg_true = 1;
James Ketrenos43f66a62005-03-25 12:31:53 -06008489 if (mode & IEEE_A) {
8490 band |= IEEE80211_52GHZ_BAND;
8491 modulation |= IEEE80211_OFDM_MODULATION;
8492 } else
James Ketrenosa33a1982005-09-14 14:28:59 -05008493 priv->ieee->abg_true = 0;
James Ketrenos43f66a62005-03-25 12:31:53 -06008494 } else {
8495 if (mode & IEEE_A) {
8496 IPW_WARNING("Attempt to set 2200BG into "
8497 "802.11a mode\n");
James Ketrenosc848d0a2005-08-24 21:56:24 -05008498 up(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06008499 return -EINVAL;
8500 }
8501
James Ketrenosa33a1982005-09-14 14:28:59 -05008502 priv->ieee->abg_true = 0;
James Ketrenos43f66a62005-03-25 12:31:53 -06008503 }
8504
8505 if (mode & IEEE_B) {
8506 band |= IEEE80211_24GHZ_BAND;
8507 modulation |= IEEE80211_CCK_MODULATION;
8508 } else
James Ketrenosa33a1982005-09-14 14:28:59 -05008509 priv->ieee->abg_true = 0;
Jeff Garzikbf794512005-07-31 13:07:26 -04008510
James Ketrenos43f66a62005-03-25 12:31:53 -06008511 if (mode & IEEE_G) {
8512 band |= IEEE80211_24GHZ_BAND;
8513 modulation |= IEEE80211_OFDM_MODULATION;
8514 } else
James Ketrenosa33a1982005-09-14 14:28:59 -05008515 priv->ieee->abg_true = 0;
James Ketrenos43f66a62005-03-25 12:31:53 -06008516
8517 priv->ieee->mode = mode;
8518 priv->ieee->freq_band = band;
8519 priv->ieee->modulation = modulation;
Jeff Garzik0edd5b42005-09-07 00:48:31 -04008520 init_supported_rates(priv, &priv->rates);
James Ketrenos43f66a62005-03-25 12:31:53 -06008521
James Ketrenosc848d0a2005-08-24 21:56:24 -05008522 /* Network configuration changed -- force [re]association */
8523 IPW_DEBUG_ASSOC("[re]association triggered due to mode change.\n");
8524 if (!ipw_disassociate(priv)) {
James Ketrenos43f66a62005-03-25 12:31:53 -06008525 ipw_send_supported_rates(priv, &priv->rates);
James Ketrenosc848d0a2005-08-24 21:56:24 -05008526 ipw_associate(priv);
8527 }
James Ketrenos43f66a62005-03-25 12:31:53 -06008528
James Ketrenosa613bff2005-08-24 21:43:11 -05008529 /* Update the band LEDs */
8530 ipw_led_band_on(priv);
8531
Jeff Garzikbf794512005-07-31 13:07:26 -04008532 IPW_DEBUG_WX("PRIV SET MODE: %c%c%c\n",
James Ketrenos43f66a62005-03-25 12:31:53 -06008533 mode & IEEE_A ? 'a' : '.',
Jeff Garzik0edd5b42005-09-07 00:48:31 -04008534 mode & IEEE_B ? 'b' : '.', mode & IEEE_G ? 'g' : '.');
James Ketrenosc848d0a2005-08-24 21:56:24 -05008535 up(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06008536 return 0;
8537}
8538
8539static int ipw_wx_get_wireless_mode(struct net_device *dev,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04008540 struct iw_request_info *info,
8541 union iwreq_data *wrqu, char *extra)
James Ketrenos43f66a62005-03-25 12:31:53 -06008542{
Jeff Garzik0edd5b42005-09-07 00:48:31 -04008543 struct ipw_priv *priv = ieee80211_priv(dev);
James Ketrenosc848d0a2005-08-24 21:56:24 -05008544 down(&priv->sem);
James Ketrenosea2b26e2005-08-24 21:25:16 -05008545 switch (priv->ieee->mode) {
8546 case IEEE_A:
James Ketrenos43f66a62005-03-25 12:31:53 -06008547 strncpy(extra, "802.11a (1)", MAX_WX_STRING);
8548 break;
James Ketrenosea2b26e2005-08-24 21:25:16 -05008549 case IEEE_B:
8550 strncpy(extra, "802.11b (2)", MAX_WX_STRING);
8551 break;
8552 case IEEE_A | IEEE_B:
8553 strncpy(extra, "802.11ab (3)", MAX_WX_STRING);
8554 break;
8555 case IEEE_G:
8556 strncpy(extra, "802.11g (4)", MAX_WX_STRING);
8557 break;
8558 case IEEE_A | IEEE_G:
8559 strncpy(extra, "802.11ag (5)", MAX_WX_STRING);
8560 break;
8561 case IEEE_B | IEEE_G:
8562 strncpy(extra, "802.11bg (6)", MAX_WX_STRING);
8563 break;
8564 case IEEE_A | IEEE_B | IEEE_G:
8565 strncpy(extra, "802.11abg (7)", MAX_WX_STRING);
8566 break;
8567 default:
8568 strncpy(extra, "unknown", MAX_WX_STRING);
James Ketrenos43f66a62005-03-25 12:31:53 -06008569 break;
Jeff Garzikbf794512005-07-31 13:07:26 -04008570 }
8571
James Ketrenos43f66a62005-03-25 12:31:53 -06008572 IPW_DEBUG_WX("PRIV GET MODE: %s\n", extra);
8573
Jeff Garzik0edd5b42005-09-07 00:48:31 -04008574 wrqu->data.length = strlen(extra) + 1;
James Ketrenosc848d0a2005-08-24 21:56:24 -05008575 up(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06008576
Jeff Garzik0edd5b42005-09-07 00:48:31 -04008577 return 0;
James Ketrenos43f66a62005-03-25 12:31:53 -06008578}
8579
James Ketrenosea2b26e2005-08-24 21:25:16 -05008580static int ipw_wx_set_preamble(struct net_device *dev,
8581 struct iw_request_info *info,
8582 union iwreq_data *wrqu, char *extra)
8583{
8584 struct ipw_priv *priv = ieee80211_priv(dev);
8585 int mode = *(int *)extra;
James Ketrenosc848d0a2005-08-24 21:56:24 -05008586 down(&priv->sem);
James Ketrenosea2b26e2005-08-24 21:25:16 -05008587 /* Switching from SHORT -> LONG requires a disassociation */
8588 if (mode == 1) {
8589 if (!(priv->config & CFG_PREAMBLE_LONG)) {
8590 priv->config |= CFG_PREAMBLE_LONG;
James Ketrenosc848d0a2005-08-24 21:56:24 -05008591
8592 /* Network configuration changed -- force [re]association */
8593 IPW_DEBUG_ASSOC
8594 ("[re]association triggered due to preamble change.\n");
8595 if (!ipw_disassociate(priv))
8596 ipw_associate(priv);
James Ketrenosea2b26e2005-08-24 21:25:16 -05008597 }
8598 goto done;
8599 }
8600
8601 if (mode == 0) {
8602 priv->config &= ~CFG_PREAMBLE_LONG;
8603 goto done;
8604 }
James Ketrenosc848d0a2005-08-24 21:56:24 -05008605 up(&priv->sem);
James Ketrenosea2b26e2005-08-24 21:25:16 -05008606 return -EINVAL;
8607
8608 done:
James Ketrenosc848d0a2005-08-24 21:56:24 -05008609 up(&priv->sem);
James Ketrenosea2b26e2005-08-24 21:25:16 -05008610 return 0;
8611}
8612
8613static int ipw_wx_get_preamble(struct net_device *dev,
8614 struct iw_request_info *info,
8615 union iwreq_data *wrqu, char *extra)
8616{
8617 struct ipw_priv *priv = ieee80211_priv(dev);
James Ketrenosc848d0a2005-08-24 21:56:24 -05008618 down(&priv->sem);
James Ketrenosea2b26e2005-08-24 21:25:16 -05008619 if (priv->config & CFG_PREAMBLE_LONG)
8620 snprintf(wrqu->name, IFNAMSIZ, "long (1)");
8621 else
8622 snprintf(wrqu->name, IFNAMSIZ, "auto (0)");
James Ketrenosc848d0a2005-08-24 21:56:24 -05008623 up(&priv->sem);
James Ketrenosea2b26e2005-08-24 21:25:16 -05008624 return 0;
8625}
8626
James Ketrenosb095c382005-08-24 22:04:42 -05008627#ifdef CONFIG_IPW2200_MONITOR
James Ketrenosea2b26e2005-08-24 21:25:16 -05008628static int ipw_wx_set_monitor(struct net_device *dev,
Jeff Garzikbf794512005-07-31 13:07:26 -04008629 struct iw_request_info *info,
James Ketrenos43f66a62005-03-25 12:31:53 -06008630 union iwreq_data *wrqu, char *extra)
Jeff Garzikbf794512005-07-31 13:07:26 -04008631{
James Ketrenos43f66a62005-03-25 12:31:53 -06008632 struct ipw_priv *priv = ieee80211_priv(dev);
8633 int *parms = (int *)extra;
8634 int enable = (parms[0] > 0);
James Ketrenosc848d0a2005-08-24 21:56:24 -05008635 down(&priv->sem);
James Ketrenosea2b26e2005-08-24 21:25:16 -05008636 IPW_DEBUG_WX("SET MONITOR: %d %d\n", enable, parms[1]);
James Ketrenos43f66a62005-03-25 12:31:53 -06008637 if (enable) {
8638 if (priv->ieee->iw_mode != IW_MODE_MONITOR) {
8639 priv->net_dev->type = ARPHRD_IEEE80211;
James Ketrenosa613bff2005-08-24 21:43:11 -05008640 queue_work(priv->workqueue, &priv->adapter_restart);
James Ketrenos43f66a62005-03-25 12:31:53 -06008641 }
Jeff Garzikbf794512005-07-31 13:07:26 -04008642
James Ketrenos43f66a62005-03-25 12:31:53 -06008643 ipw_set_channel(priv, parms[1]);
8644 } else {
James Ketrenosc848d0a2005-08-24 21:56:24 -05008645 if (priv->ieee->iw_mode != IW_MODE_MONITOR) {
8646 up(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06008647 return 0;
James Ketrenosc848d0a2005-08-24 21:56:24 -05008648 }
James Ketrenos43f66a62005-03-25 12:31:53 -06008649 priv->net_dev->type = ARPHRD_ETHER;
James Ketrenosa613bff2005-08-24 21:43:11 -05008650 queue_work(priv->workqueue, &priv->adapter_restart);
James Ketrenos43f66a62005-03-25 12:31:53 -06008651 }
James Ketrenosc848d0a2005-08-24 21:56:24 -05008652 up(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06008653 return 0;
8654}
8655
James Ketrenosb095c382005-08-24 22:04:42 -05008656#endif // CONFIG_IPW2200_MONITOR
8657
Jeff Garzikbf794512005-07-31 13:07:26 -04008658static int ipw_wx_reset(struct net_device *dev,
8659 struct iw_request_info *info,
James Ketrenos43f66a62005-03-25 12:31:53 -06008660 union iwreq_data *wrqu, char *extra)
Jeff Garzikbf794512005-07-31 13:07:26 -04008661{
James Ketrenos43f66a62005-03-25 12:31:53 -06008662 struct ipw_priv *priv = ieee80211_priv(dev);
8663 IPW_DEBUG_WX("RESET\n");
James Ketrenosa613bff2005-08-24 21:43:11 -05008664 queue_work(priv->workqueue, &priv->adapter_restart);
James Ketrenos43f66a62005-03-25 12:31:53 -06008665 return 0;
8666}
James Ketrenosb095c382005-08-24 22:04:42 -05008667
8668static void ipw_sw_reset(struct ipw_priv *priv, int init)
8669{
8670 int band, modulation;
8671
8672 /* Initialize module parameter values here */
8673 priv->config = 0;
8674
8675 /* We default to disabling the LED code as right now it causes
8676 * too many systems to lock up... */
8677 if (!led)
8678 priv->config |= CFG_NO_LED;
8679
8680 if (associate)
8681 priv->config |= CFG_ASSOCIATE;
8682 else
8683 IPW_DEBUG_INFO("Auto associate disabled.\n");
8684
8685 if (auto_create)
8686 priv->config |= CFG_ADHOC_CREATE;
8687 else
8688 IPW_DEBUG_INFO("Auto adhoc creation disabled.\n");
8689
8690 if (disable) {
8691 priv->status |= STATUS_RF_KILL_SW;
8692 IPW_DEBUG_INFO("Radio disabled.\n");
8693 }
8694
8695 if (channel != 0) {
8696 priv->config |= CFG_STATIC_CHANNEL;
8697 priv->channel = channel;
8698 IPW_DEBUG_INFO("Bind to static channel %d\n", channel);
8699 /* TODO: Validate that provided channel is in range */
8700 }
8701#ifdef CONFIG_IPW_QOS
8702 ipw_qos_init(priv, qos_enable, qos_burst_enable,
8703 burst_duration_CCK, burst_duration_OFDM);
8704#endif /* CONFIG_IPW_QOS */
8705
8706 switch (mode) {
8707 case 1:
8708 priv->ieee->iw_mode = IW_MODE_ADHOC;
8709 priv->net_dev->type = ARPHRD_ETHER;
8710
8711 break;
8712#ifdef CONFIG_IPW2200_MONITOR
8713 case 2:
8714 priv->ieee->iw_mode = IW_MODE_MONITOR;
8715 priv->net_dev->type = ARPHRD_IEEE80211;
8716 break;
8717#endif
8718 default:
8719 case 0:
8720 priv->net_dev->type = ARPHRD_ETHER;
8721 priv->ieee->iw_mode = IW_MODE_INFRA;
8722 break;
8723 }
8724
8725 if (hwcrypto) {
8726 priv->ieee->host_encrypt = 0;
8727 priv->ieee->host_decrypt = 0;
8728 }
8729 IPW_DEBUG_INFO("Hardware crypto [%s]\n", hwcrypto ? "on" : "off");
8730
8731 if ((priv->pci_dev->device == 0x4223) ||
8732 (priv->pci_dev->device == 0x4224)) {
8733 if (init)
8734 printk(KERN_INFO DRV_NAME
8735 ": Detected Intel PRO/Wireless 2915ABG Network "
8736 "Connection\n");
8737 priv->ieee->abg_true = 1;
8738 band = IEEE80211_52GHZ_BAND | IEEE80211_24GHZ_BAND;
8739 modulation = IEEE80211_OFDM_MODULATION |
8740 IEEE80211_CCK_MODULATION;
8741 priv->adapter = IPW_2915ABG;
8742 priv->ieee->mode = IEEE_A | IEEE_G | IEEE_B;
8743 } else {
8744 if (init)
8745 printk(KERN_INFO DRV_NAME
8746 ": Detected Intel PRO/Wireless 2200BG Network "
8747 "Connection\n");
8748
8749 priv->ieee->abg_true = 0;
8750 band = IEEE80211_24GHZ_BAND;
8751 modulation = IEEE80211_OFDM_MODULATION |
8752 IEEE80211_CCK_MODULATION;
8753 priv->adapter = IPW_2200BG;
8754 priv->ieee->mode = IEEE_G | IEEE_B;
8755 }
8756
8757 priv->ieee->freq_band = band;
8758 priv->ieee->modulation = modulation;
8759
8760 priv->rates_mask = IEEE80211_DEFAULT_RATES_MASK;
8761
8762 priv->missed_beacon_threshold = IPW_MB_DISASSOCIATE_THRESHOLD_DEFAULT;
8763 priv->roaming_threshold = IPW_MB_ROAMING_THRESHOLD_DEFAULT;
8764
8765 priv->rts_threshold = DEFAULT_RTS_THRESHOLD;
8766
8767 /* If power management is turned on, default to AC mode */
8768 priv->power_mode = IPW_POWER_AC;
8769 priv->tx_power = IPW_TX_POWER_DEFAULT;
8770}
8771
8772static int ipw_wx_sw_reset(struct net_device *dev,
8773 struct iw_request_info *info,
8774 union iwreq_data *wrqu, char *extra)
8775{
8776 struct ipw_priv *priv = ieee80211_priv(dev);
8777 union iwreq_data wrqu_sec = {
8778 .encoding = {
8779 .flags = IW_ENCODE_DISABLED,
8780 },
8781 };
8782
8783 IPW_DEBUG_WX("SW_RESET\n");
8784
8785 down(&priv->sem);
8786
8787 ipw_sw_reset(priv, 0);
8788
8789 /* The SW reset bit might have been toggled on by the 'disable'
8790 * module parameter, so take appropriate action */
8791 ipw_radio_kill_sw(priv, priv->status & STATUS_RF_KILL_SW);
8792
8793 up(&priv->sem);
8794 ieee80211_wx_set_encode(priv->ieee, info, &wrqu_sec, NULL);
8795 down(&priv->sem);
8796
8797 if (!(priv->status & STATUS_RF_KILL_MASK)) {
8798 /* Configuration likely changed -- force [re]association */
8799 IPW_DEBUG_ASSOC("[re]association triggered due to sw "
8800 "reset.\n");
8801 if (!ipw_disassociate(priv))
8802 ipw_associate(priv);
8803 }
8804
8805 up(&priv->sem);
8806
8807 return 0;
8808}
James Ketrenos43f66a62005-03-25 12:31:53 -06008809
8810/* Rebase the WE IOCTLs to zero for the handler array */
8811#define IW_IOCTL(x) [(x)-SIOCSIWCOMMIT]
Jeff Garzik0edd5b42005-09-07 00:48:31 -04008812static iw_handler ipw_wx_handlers[] = {
James Ketrenosea2b26e2005-08-24 21:25:16 -05008813 IW_IOCTL(SIOCGIWNAME) = ipw_wx_get_name,
8814 IW_IOCTL(SIOCSIWFREQ) = ipw_wx_set_freq,
8815 IW_IOCTL(SIOCGIWFREQ) = ipw_wx_get_freq,
8816 IW_IOCTL(SIOCSIWMODE) = ipw_wx_set_mode,
8817 IW_IOCTL(SIOCGIWMODE) = ipw_wx_get_mode,
8818 IW_IOCTL(SIOCGIWRANGE) = ipw_wx_get_range,
8819 IW_IOCTL(SIOCSIWAP) = ipw_wx_set_wap,
8820 IW_IOCTL(SIOCGIWAP) = ipw_wx_get_wap,
8821 IW_IOCTL(SIOCSIWSCAN) = ipw_wx_set_scan,
8822 IW_IOCTL(SIOCGIWSCAN) = ipw_wx_get_scan,
8823 IW_IOCTL(SIOCSIWESSID) = ipw_wx_set_essid,
8824 IW_IOCTL(SIOCGIWESSID) = ipw_wx_get_essid,
8825 IW_IOCTL(SIOCSIWNICKN) = ipw_wx_set_nick,
8826 IW_IOCTL(SIOCGIWNICKN) = ipw_wx_get_nick,
8827 IW_IOCTL(SIOCSIWRATE) = ipw_wx_set_rate,
8828 IW_IOCTL(SIOCGIWRATE) = ipw_wx_get_rate,
8829 IW_IOCTL(SIOCSIWRTS) = ipw_wx_set_rts,
8830 IW_IOCTL(SIOCGIWRTS) = ipw_wx_get_rts,
8831 IW_IOCTL(SIOCSIWFRAG) = ipw_wx_set_frag,
8832 IW_IOCTL(SIOCGIWFRAG) = ipw_wx_get_frag,
8833 IW_IOCTL(SIOCSIWTXPOW) = ipw_wx_set_txpow,
8834 IW_IOCTL(SIOCGIWTXPOW) = ipw_wx_get_txpow,
8835 IW_IOCTL(SIOCSIWRETRY) = ipw_wx_set_retry,
8836 IW_IOCTL(SIOCGIWRETRY) = ipw_wx_get_retry,
8837 IW_IOCTL(SIOCSIWENCODE) = ipw_wx_set_encode,
8838 IW_IOCTL(SIOCGIWENCODE) = ipw_wx_get_encode,
8839 IW_IOCTL(SIOCSIWPOWER) = ipw_wx_set_power,
8840 IW_IOCTL(SIOCGIWPOWER) = ipw_wx_get_power,
James Ketrenosa613bff2005-08-24 21:43:11 -05008841 IW_IOCTL(SIOCSIWSPY) = iw_handler_set_spy,
8842 IW_IOCTL(SIOCGIWSPY) = iw_handler_get_spy,
8843 IW_IOCTL(SIOCSIWTHRSPY) = iw_handler_set_thrspy,
8844 IW_IOCTL(SIOCGIWTHRSPY) = iw_handler_get_thrspy,
James Ketrenos43f66a62005-03-25 12:31:53 -06008845};
8846
James Ketrenosb095c382005-08-24 22:04:42 -05008847enum {
8848 IPW_PRIV_SET_POWER = SIOCIWFIRSTPRIV,
8849 IPW_PRIV_GET_POWER,
8850 IPW_PRIV_SET_MODE,
8851 IPW_PRIV_GET_MODE,
8852 IPW_PRIV_SET_PREAMBLE,
8853 IPW_PRIV_GET_PREAMBLE,
8854 IPW_PRIV_RESET,
8855 IPW_PRIV_SW_RESET,
8856#ifdef CONFIG_IPW2200_MONITOR
8857 IPW_PRIV_SET_MONITOR,
8858#endif
8859};
James Ketrenos43f66a62005-03-25 12:31:53 -06008860
Jeff Garzikbf794512005-07-31 13:07:26 -04008861static struct iw_priv_args ipw_priv_args[] = {
James Ketrenos43f66a62005-03-25 12:31:53 -06008862 {
Jeff Garzik0edd5b42005-09-07 00:48:31 -04008863 .cmd = IPW_PRIV_SET_POWER,
8864 .set_args = IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
8865 .name = "set_power"},
James Ketrenos43f66a62005-03-25 12:31:53 -06008866 {
Jeff Garzik0edd5b42005-09-07 00:48:31 -04008867 .cmd = IPW_PRIV_GET_POWER,
8868 .get_args = IW_PRIV_TYPE_CHAR | IW_PRIV_SIZE_FIXED | MAX_WX_STRING,
8869 .name = "get_power"},
James Ketrenos43f66a62005-03-25 12:31:53 -06008870 {
Jeff Garzik0edd5b42005-09-07 00:48:31 -04008871 .cmd = IPW_PRIV_SET_MODE,
8872 .set_args = IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
8873 .name = "set_mode"},
James Ketrenos43f66a62005-03-25 12:31:53 -06008874 {
Jeff Garzik0edd5b42005-09-07 00:48:31 -04008875 .cmd = IPW_PRIV_GET_MODE,
8876 .get_args = IW_PRIV_TYPE_CHAR | IW_PRIV_SIZE_FIXED | MAX_WX_STRING,
8877 .name = "get_mode"},
James Ketrenos43f66a62005-03-25 12:31:53 -06008878 {
James Ketrenosea2b26e2005-08-24 21:25:16 -05008879 .cmd = IPW_PRIV_SET_PREAMBLE,
8880 .set_args = IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
8881 .name = "set_preamble"},
8882 {
8883 .cmd = IPW_PRIV_GET_PREAMBLE,
8884 .get_args = IW_PRIV_TYPE_CHAR | IW_PRIV_SIZE_FIXED | IFNAMSIZ,
8885 .name = "get_preamble"},
James Ketrenos43f66a62005-03-25 12:31:53 -06008886 {
Jeff Garzik0edd5b42005-09-07 00:48:31 -04008887 IPW_PRIV_RESET,
8888 IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 0, 0, "reset"},
James Ketrenosb095c382005-08-24 22:04:42 -05008889 {
8890 IPW_PRIV_SW_RESET,
8891 IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 0, 0, "sw_reset"},
8892#ifdef CONFIG_IPW2200_MONITOR
8893 {
8894 IPW_PRIV_SET_MONITOR,
8895 IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 2, 0, "monitor"},
8896#endif /* CONFIG_IPW2200_MONITOR */
James Ketrenos43f66a62005-03-25 12:31:53 -06008897};
8898
8899static iw_handler ipw_priv_handler[] = {
8900 ipw_wx_set_powermode,
8901 ipw_wx_get_powermode,
8902 ipw_wx_set_wireless_mode,
8903 ipw_wx_get_wireless_mode,
James Ketrenosea2b26e2005-08-24 21:25:16 -05008904 ipw_wx_set_preamble,
8905 ipw_wx_get_preamble,
Jeff Garzikbf794512005-07-31 13:07:26 -04008906 ipw_wx_reset,
James Ketrenosb095c382005-08-24 22:04:42 -05008907 ipw_wx_sw_reset,
8908#ifdef CONFIG_IPW2200_MONITOR
8909 ipw_wx_set_monitor,
James Ketrenos43f66a62005-03-25 12:31:53 -06008910#endif
8911};
8912
Jeff Garzik0edd5b42005-09-07 00:48:31 -04008913static struct iw_handler_def ipw_wx_handler_def = {
James Ketrenosea2b26e2005-08-24 21:25:16 -05008914 .standard = ipw_wx_handlers,
8915 .num_standard = ARRAY_SIZE(ipw_wx_handlers),
8916 .num_private = ARRAY_SIZE(ipw_priv_handler),
8917 .num_private_args = ARRAY_SIZE(ipw_priv_args),
8918 .private = ipw_priv_handler,
8919 .private_args = ipw_priv_args,
James Ketrenos43f66a62005-03-25 12:31:53 -06008920};
8921
James Ketrenosa613bff2005-08-24 21:43:11 -05008922static struct iw_public_data ipw_wx_data;
8923
James Ketrenos43f66a62005-03-25 12:31:53 -06008924/*
8925 * Get wireless statistics.
8926 * Called by /proc/net/wireless
8927 * Also called by SIOCGIWSTATS
8928 */
Jeff Garzik0edd5b42005-09-07 00:48:31 -04008929static struct iw_statistics *ipw_get_wireless_stats(struct net_device *dev)
James Ketrenos43f66a62005-03-25 12:31:53 -06008930{
8931 struct ipw_priv *priv = ieee80211_priv(dev);
8932 struct iw_statistics *wstats;
Jeff Garzikbf794512005-07-31 13:07:26 -04008933
James Ketrenos43f66a62005-03-25 12:31:53 -06008934 wstats = &priv->wstats;
8935
James Ketrenosea2b26e2005-08-24 21:25:16 -05008936 /* if hw is disabled, then ipw_get_ordinal() can't be called.
Jeff Garzikbf794512005-07-31 13:07:26 -04008937 * ipw2100_wx_wireless_stats seems to be called before fw is
James Ketrenos43f66a62005-03-25 12:31:53 -06008938 * initialized. STATUS_ASSOCIATED will only be set if the hw is up
8939 * and associated; if not associcated, the values are all meaningless
8940 * anyway, so set them all to NULL and INVALID */
8941 if (!(priv->status & STATUS_ASSOCIATED)) {
8942 wstats->miss.beacon = 0;
8943 wstats->discard.retries = 0;
8944 wstats->qual.qual = 0;
8945 wstats->qual.level = 0;
8946 wstats->qual.noise = 0;
8947 wstats->qual.updated = 7;
8948 wstats->qual.updated |= IW_QUAL_NOISE_INVALID |
Jeff Garzik0edd5b42005-09-07 00:48:31 -04008949 IW_QUAL_QUAL_INVALID | IW_QUAL_LEVEL_INVALID;
James Ketrenos43f66a62005-03-25 12:31:53 -06008950 return wstats;
Jeff Garzikbf794512005-07-31 13:07:26 -04008951 }
James Ketrenos43f66a62005-03-25 12:31:53 -06008952
8953 wstats->qual.qual = priv->quality;
8954 wstats->qual.level = average_value(&priv->average_rssi);
8955 wstats->qual.noise = average_value(&priv->average_noise);
8956 wstats->qual.updated = IW_QUAL_QUAL_UPDATED | IW_QUAL_LEVEL_UPDATED |
Jeff Garzik0edd5b42005-09-07 00:48:31 -04008957 IW_QUAL_NOISE_UPDATED;
James Ketrenos43f66a62005-03-25 12:31:53 -06008958
8959 wstats->miss.beacon = average_value(&priv->average_missed_beacons);
8960 wstats->discard.retries = priv->last_tx_failures;
8961 wstats->discard.code = priv->ieee->ieee_stats.rx_discards_undecryptable;
Jeff Garzikbf794512005-07-31 13:07:26 -04008962
James Ketrenos43f66a62005-03-25 12:31:53 -06008963/* if (ipw_get_ordinal(priv, IPW_ORD_STAT_TX_RETRY, &tx_retry, &len))
8964 goto fail_get_ordinal;
8965 wstats->discard.retries += tx_retry; */
Jeff Garzikbf794512005-07-31 13:07:26 -04008966
James Ketrenos43f66a62005-03-25 12:31:53 -06008967 return wstats;
8968}
8969
James Ketrenos43f66a62005-03-25 12:31:53 -06008970/* net device stuff */
8971
8972static inline void init_sys_config(struct ipw_sys_config *sys_config)
8973{
Jeff Garzik0edd5b42005-09-07 00:48:31 -04008974 memset(sys_config, 0, sizeof(struct ipw_sys_config));
8975 sys_config->bt_coexistence = 1; /* We may need to look into prvStaBtConfig */
James Ketrenos43f66a62005-03-25 12:31:53 -06008976 sys_config->answer_broadcast_ssid_probe = 0;
8977 sys_config->accept_all_data_frames = 0;
8978 sys_config->accept_non_directed_frames = 1;
8979 sys_config->exclude_unicast_unencrypted = 0;
8980 sys_config->disable_unicast_decryption = 1;
8981 sys_config->exclude_multicast_unencrypted = 0;
8982 sys_config->disable_multicast_decryption = 1;
8983 sys_config->antenna_diversity = CFG_SYS_ANTENNA_BOTH;
Jeff Garzik0edd5b42005-09-07 00:48:31 -04008984 sys_config->pass_crc_to_host = 0; /* TODO: See if 1 gives us FCS */
James Ketrenos43f66a62005-03-25 12:31:53 -06008985 sys_config->dot11g_auto_detection = 0;
Jeff Garzikbf794512005-07-31 13:07:26 -04008986 sys_config->enable_cts_to_self = 0;
James Ketrenos43f66a62005-03-25 12:31:53 -06008987 sys_config->bt_coexist_collision_thr = 0;
James Ketrenosc848d0a2005-08-24 21:56:24 -05008988 sys_config->pass_noise_stats_to_host = 1; //1 -- fix for 256
James Ketrenos43f66a62005-03-25 12:31:53 -06008989}
8990
8991static int ipw_net_open(struct net_device *dev)
8992{
8993 struct ipw_priv *priv = ieee80211_priv(dev);
8994 IPW_DEBUG_INFO("dev->open\n");
8995 /* we should be verifying the device is ready to be opened */
James Ketrenosc848d0a2005-08-24 21:56:24 -05008996 down(&priv->sem);
Jeff Garzikbf794512005-07-31 13:07:26 -04008997 if (!(priv->status & STATUS_RF_KILL_MASK) &&
8998 (priv->status & STATUS_ASSOCIATED))
James Ketrenos43f66a62005-03-25 12:31:53 -06008999 netif_start_queue(dev);
James Ketrenosc848d0a2005-08-24 21:56:24 -05009000 up(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06009001 return 0;
9002}
9003
9004static int ipw_net_stop(struct net_device *dev)
9005{
9006 IPW_DEBUG_INFO("dev->close\n");
9007 netif_stop_queue(dev);
9008 return 0;
9009}
9010
9011/*
9012todo:
9013
9014modify to send one tfd per fragment instead of using chunking. otherwise
9015we need to heavily modify the ieee80211_skb_to_txb.
9016*/
9017
James Ketrenosb095c382005-08-24 22:04:42 -05009018static inline void ipw_tx_skb(struct ipw_priv *priv, struct ieee80211_txb *txb,
9019 int pri)
James Ketrenos43f66a62005-03-25 12:31:53 -06009020{
James Ketrenos0dacca12005-09-21 12:23:41 -05009021 struct ieee80211_hdr_3addr *hdr = (struct ieee80211_hdr_3addr *)
Jeff Garzik0edd5b42005-09-07 00:48:31 -04009022 txb->fragments[0]->data;
James Ketrenos43f66a62005-03-25 12:31:53 -06009023 int i = 0;
9024 struct tfd_frame *tfd;
James Ketrenosb095c382005-08-24 22:04:42 -05009025#ifdef CONFIG_IPW_QOS
9026 int tx_id = ipw_get_tx_queue_number(priv, pri);
9027 struct clx2_tx_queue *txq = &priv->txq[tx_id];
9028#else
James Ketrenos43f66a62005-03-25 12:31:53 -06009029 struct clx2_tx_queue *txq = &priv->txq[0];
James Ketrenosb095c382005-08-24 22:04:42 -05009030#endif
James Ketrenos43f66a62005-03-25 12:31:53 -06009031 struct clx2_queue *q = &txq->q;
9032 u8 id, hdr_len, unicast;
9033 u16 remaining_bytes;
James Ketrenosc848d0a2005-08-24 21:56:24 -05009034 int fc;
James Ketrenos43f66a62005-03-25 12:31:53 -06009035
9036 switch (priv->ieee->iw_mode) {
9037 case IW_MODE_ADHOC:
9038 hdr_len = IEEE80211_3ADDR_LEN;
9039 unicast = !is_broadcast_ether_addr(hdr->addr1) &&
Jeff Garzik0edd5b42005-09-07 00:48:31 -04009040 !is_multicast_ether_addr(hdr->addr1);
James Ketrenos43f66a62005-03-25 12:31:53 -06009041 id = ipw_find_station(priv, hdr->addr1);
9042 if (id == IPW_INVALID_STATION) {
9043 id = ipw_add_station(priv, hdr->addr1);
9044 if (id == IPW_INVALID_STATION) {
9045 IPW_WARNING("Attempt to send data to "
Jeff Garzikbf794512005-07-31 13:07:26 -04009046 "invalid cell: " MAC_FMT "\n",
James Ketrenos43f66a62005-03-25 12:31:53 -06009047 MAC_ARG(hdr->addr1));
9048 goto drop;
9049 }
9050 }
9051 break;
9052
9053 case IW_MODE_INFRA:
9054 default:
9055 unicast = !is_broadcast_ether_addr(hdr->addr3) &&
Jeff Garzik0edd5b42005-09-07 00:48:31 -04009056 !is_multicast_ether_addr(hdr->addr3);
James Ketrenos43f66a62005-03-25 12:31:53 -06009057 hdr_len = IEEE80211_3ADDR_LEN;
9058 id = 0;
9059 break;
9060 }
9061
9062 tfd = &txq->bd[q->first_empty];
9063 txq->txb[q->first_empty] = txb;
9064 memset(tfd, 0, sizeof(*tfd));
9065 tfd->u.data.station_number = id;
9066
9067 tfd->control_flags.message_type = TX_FRAME_TYPE;
9068 tfd->control_flags.control_bits = TFD_NEED_IRQ_MASK;
9069
9070 tfd->u.data.cmd_id = DINO_CMD_TX;
James Ketrenosa613bff2005-08-24 21:43:11 -05009071 tfd->u.data.len = cpu_to_le16(txb->payload_size);
James Ketrenos43f66a62005-03-25 12:31:53 -06009072 remaining_bytes = txb->payload_size;
Jeff Garzikbf794512005-07-31 13:07:26 -04009073
James Ketrenos43f66a62005-03-25 12:31:53 -06009074 if (priv->assoc_request.ieee_mode == IPW_B_MODE)
James Ketrenosb095c382005-08-24 22:04:42 -05009075 tfd->u.data.tx_flags_ext |= DCT_FLAG_EXT_MODE_CCK;
James Ketrenos43f66a62005-03-25 12:31:53 -06009076 else
James Ketrenosb095c382005-08-24 22:04:42 -05009077 tfd->u.data.tx_flags_ext |= DCT_FLAG_EXT_MODE_OFDM;
James Ketrenos43f66a62005-03-25 12:31:53 -06009078
James Ketrenosea2b26e2005-08-24 21:25:16 -05009079 if (priv->assoc_request.preamble_length == DCT_FLAG_SHORT_PREAMBLE)
9080 tfd->u.data.tx_flags |= DCT_FLAG_SHORT_PREAMBLE;
James Ketrenos43f66a62005-03-25 12:31:53 -06009081
James Ketrenosc848d0a2005-08-24 21:56:24 -05009082 fc = le16_to_cpu(hdr->frame_ctl);
9083 hdr->frame_ctl = cpu_to_le16(fc & ~IEEE80211_FCTL_MOREFRAGS);
9084
James Ketrenos43f66a62005-03-25 12:31:53 -06009085 memcpy(&tfd->u.data.tfd.tfd_24.mchdr, hdr, hdr_len);
9086
James Ketrenosb095c382005-08-24 22:04:42 -05009087 if (likely(unicast))
9088 tfd->u.data.tx_flags |= DCT_FLAG_ACK_REQD;
9089
9090 if (txb->encrypted && !priv->ieee->host_encrypt) {
9091 switch (priv->ieee->sec.level) {
9092 case SEC_LEVEL_3:
9093 tfd->u.data.tfd.tfd_24.mchdr.frame_ctl |=
9094 IEEE80211_FCTL_PROTECTED;
9095 /* XXX: ACK flag must be set for CCMP even if it
9096 * is a multicast/broadcast packet, because CCMP
9097 * group communication encrypted by GTK is
9098 * actually done by the AP. */
9099 if (!unicast)
9100 tfd->u.data.tx_flags |= DCT_FLAG_ACK_REQD;
9101
9102 tfd->u.data.tx_flags &= ~DCT_FLAG_NO_WEP;
9103 tfd->u.data.tx_flags_ext |= DCT_FLAG_EXT_SECURITY_CCM;
9104 tfd->u.data.key_index = 0;
9105 tfd->u.data.key_index |= DCT_WEP_INDEX_USE_IMMEDIATE;
9106 break;
9107 case SEC_LEVEL_2:
9108 tfd->u.data.tfd.tfd_24.mchdr.frame_ctl |=
9109 IEEE80211_FCTL_PROTECTED;
9110 tfd->u.data.tx_flags &= ~DCT_FLAG_NO_WEP;
9111 tfd->u.data.tx_flags_ext |= DCT_FLAG_EXT_SECURITY_TKIP;
9112 tfd->u.data.key_index = DCT_WEP_INDEX_USE_IMMEDIATE;
9113 break;
9114 case SEC_LEVEL_1:
9115 tfd->u.data.tfd.tfd_24.mchdr.frame_ctl |=
9116 IEEE80211_FCTL_PROTECTED;
9117 tfd->u.data.key_index = priv->ieee->tx_keyidx;
9118 if (priv->ieee->sec.key_sizes[priv->ieee->tx_keyidx] <=
9119 40)
9120 tfd->u.data.key_index |= DCT_WEP_KEY_64Bit;
9121 else
9122 tfd->u.data.key_index |= DCT_WEP_KEY_128Bit;
9123 break;
9124 case SEC_LEVEL_0:
9125 break;
9126 default:
9127 printk(KERN_ERR "Unknow security level %d\n",
9128 priv->ieee->sec.level);
9129 break;
9130 }
9131 } else
9132 /* No hardware encryption */
9133 tfd->u.data.tx_flags |= DCT_FLAG_NO_WEP;
9134
9135#ifdef CONFIG_IPW_QOS
9136 ipw_qos_set_tx_queue_command(priv, pri, &(tfd->u.data), unicast);
9137#endif /* CONFIG_IPW_QOS */
9138
James Ketrenos43f66a62005-03-25 12:31:53 -06009139 /* payload */
James Ketrenosa613bff2005-08-24 21:43:11 -05009140 tfd->u.data.num_chunks = cpu_to_le32(min((u8) (NUM_TFD_CHUNKS - 2),
9141 txb->nr_frags));
9142 IPW_DEBUG_FRAG("%i fragments being sent as %i chunks.\n",
9143 txb->nr_frags, le32_to_cpu(tfd->u.data.num_chunks));
9144 for (i = 0; i < le32_to_cpu(tfd->u.data.num_chunks); i++) {
9145 IPW_DEBUG_FRAG("Adding fragment %i of %i (%d bytes).\n",
9146 i, le32_to_cpu(tfd->u.data.num_chunks),
9147 txb->fragments[i]->len - hdr_len);
Jeff Garzikbf794512005-07-31 13:07:26 -04009148 IPW_DEBUG_TX("Dumping TX packet frag %i of %i (%d bytes):\n",
James Ketrenos43f66a62005-03-25 12:31:53 -06009149 i, tfd->u.data.num_chunks,
9150 txb->fragments[i]->len - hdr_len);
Jeff Garzikbf794512005-07-31 13:07:26 -04009151 printk_buf(IPW_DL_TX, txb->fragments[i]->data + hdr_len,
James Ketrenos43f66a62005-03-25 12:31:53 -06009152 txb->fragments[i]->len - hdr_len);
9153
Jeff Garzik0edd5b42005-09-07 00:48:31 -04009154 tfd->u.data.chunk_ptr[i] =
James Ketrenosa613bff2005-08-24 21:43:11 -05009155 cpu_to_le32(pci_map_single
9156 (priv->pci_dev,
9157 txb->fragments[i]->data + hdr_len,
9158 txb->fragments[i]->len - hdr_len,
9159 PCI_DMA_TODEVICE));
9160 tfd->u.data.chunk_len[i] =
9161 cpu_to_le16(txb->fragments[i]->len - hdr_len);
James Ketrenos43f66a62005-03-25 12:31:53 -06009162 }
9163
9164 if (i != txb->nr_frags) {
9165 struct sk_buff *skb;
9166 u16 remaining_bytes = 0;
9167 int j;
9168
9169 for (j = i; j < txb->nr_frags; j++)
9170 remaining_bytes += txb->fragments[j]->len - hdr_len;
9171
9172 printk(KERN_INFO "Trying to reallocate for %d bytes\n",
9173 remaining_bytes);
9174 skb = alloc_skb(remaining_bytes, GFP_ATOMIC);
9175 if (skb != NULL) {
James Ketrenosa613bff2005-08-24 21:43:11 -05009176 tfd->u.data.chunk_len[i] = cpu_to_le16(remaining_bytes);
James Ketrenos43f66a62005-03-25 12:31:53 -06009177 for (j = i; j < txb->nr_frags; j++) {
9178 int size = txb->fragments[j]->len - hdr_len;
9179 printk(KERN_INFO "Adding frag %d %d...\n",
Jeff Garzik0edd5b42005-09-07 00:48:31 -04009180 j, size);
James Ketrenos43f66a62005-03-25 12:31:53 -06009181 memcpy(skb_put(skb, size),
Jeff Garzik0edd5b42005-09-07 00:48:31 -04009182 txb->fragments[j]->data + hdr_len, size);
James Ketrenos43f66a62005-03-25 12:31:53 -06009183 }
9184 dev_kfree_skb_any(txb->fragments[i]);
9185 txb->fragments[i] = skb;
Jeff Garzik0edd5b42005-09-07 00:48:31 -04009186 tfd->u.data.chunk_ptr[i] =
James Ketrenosa613bff2005-08-24 21:43:11 -05009187 cpu_to_le32(pci_map_single
9188 (priv->pci_dev, skb->data,
9189 tfd->u.data.chunk_len[i],
9190 PCI_DMA_TODEVICE));
9191
9192 tfd->u.data.num_chunks =
9193 cpu_to_le32(le32_to_cpu(tfd->u.data.num_chunks) +
9194 1);
Jeff Garzikbf794512005-07-31 13:07:26 -04009195 }
James Ketrenos43f66a62005-03-25 12:31:53 -06009196 }
9197
9198 /* kick DMA */
9199 q->first_empty = ipw_queue_inc_wrap(q->first_empty, q->n_bd);
9200 ipw_write32(priv, q->reg_w, q->first_empty);
9201
Jeff Garzikbf794512005-07-31 13:07:26 -04009202 if (ipw_queue_space(q) < q->high_mark)
James Ketrenos43f66a62005-03-25 12:31:53 -06009203 netif_stop_queue(priv->net_dev);
9204
9205 return;
9206
Jeff Garzik0edd5b42005-09-07 00:48:31 -04009207 drop:
James Ketrenos43f66a62005-03-25 12:31:53 -06009208 IPW_DEBUG_DROP("Silently dropping Tx packet.\n");
9209 ieee80211_txb_free(txb);
9210}
9211
9212static int ipw_net_hard_start_xmit(struct ieee80211_txb *txb,
James Ketrenosc8d42d12005-09-21 12:23:43 -05009213 struct net_device *dev, int pri)
James Ketrenos43f66a62005-03-25 12:31:53 -06009214{
9215 struct ipw_priv *priv = ieee80211_priv(dev);
9216 unsigned long flags;
9217
9218 IPW_DEBUG_TX("dev->xmit(%d bytes)\n", txb->payload_size);
James Ketrenos43f66a62005-03-25 12:31:53 -06009219 spin_lock_irqsave(&priv->lock, flags);
9220
9221 if (!(priv->status & STATUS_ASSOCIATED)) {
9222 IPW_DEBUG_INFO("Tx attempt while not associated.\n");
9223 priv->ieee->stats.tx_carrier_errors++;
9224 netif_stop_queue(dev);
9225 goto fail_unlock;
9226 }
9227
James Ketrenosb095c382005-08-24 22:04:42 -05009228 ipw_tx_skb(priv, txb, pri);
9229 __ipw_led_activity_on(priv);
James Ketrenosc848d0a2005-08-24 21:56:24 -05009230 spin_unlock_irqrestore(&priv->lock, flags);
James Ketrenos43f66a62005-03-25 12:31:53 -06009231
James Ketrenos43f66a62005-03-25 12:31:53 -06009232 return 0;
9233
Jeff Garzik0edd5b42005-09-07 00:48:31 -04009234 fail_unlock:
James Ketrenos43f66a62005-03-25 12:31:53 -06009235 spin_unlock_irqrestore(&priv->lock, flags);
9236 return 1;
9237}
9238
9239static struct net_device_stats *ipw_net_get_stats(struct net_device *dev)
9240{
9241 struct ipw_priv *priv = ieee80211_priv(dev);
Jeff Garzikbf794512005-07-31 13:07:26 -04009242
James Ketrenos43f66a62005-03-25 12:31:53 -06009243 priv->ieee->stats.tx_packets = priv->tx_packets;
9244 priv->ieee->stats.rx_packets = priv->rx_packets;
9245 return &priv->ieee->stats;
9246}
9247
9248static void ipw_net_set_multicast_list(struct net_device *dev)
9249{
9250
9251}
9252
9253static int ipw_net_set_mac_address(struct net_device *dev, void *p)
9254{
9255 struct ipw_priv *priv = ieee80211_priv(dev);
9256 struct sockaddr *addr = p;
9257 if (!is_valid_ether_addr(addr->sa_data))
9258 return -EADDRNOTAVAIL;
James Ketrenosc848d0a2005-08-24 21:56:24 -05009259 down(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06009260 priv->config |= CFG_CUSTOM_MAC;
9261 memcpy(priv->mac_addr, addr->sa_data, ETH_ALEN);
9262 printk(KERN_INFO "%s: Setting MAC to " MAC_FMT "\n",
9263 priv->net_dev->name, MAC_ARG(priv->mac_addr));
James Ketrenosa613bff2005-08-24 21:43:11 -05009264 queue_work(priv->workqueue, &priv->adapter_restart);
James Ketrenosc848d0a2005-08-24 21:56:24 -05009265 up(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06009266 return 0;
9267}
9268
Jeff Garzikbf794512005-07-31 13:07:26 -04009269static void ipw_ethtool_get_drvinfo(struct net_device *dev,
James Ketrenos43f66a62005-03-25 12:31:53 -06009270 struct ethtool_drvinfo *info)
9271{
9272 struct ipw_priv *p = ieee80211_priv(dev);
9273 char vers[64];
9274 char date[32];
9275 u32 len;
9276
9277 strcpy(info->driver, DRV_NAME);
9278 strcpy(info->version, DRV_VERSION);
9279
9280 len = sizeof(vers);
9281 ipw_get_ordinal(p, IPW_ORD_STAT_FW_VERSION, vers, &len);
9282 len = sizeof(date);
9283 ipw_get_ordinal(p, IPW_ORD_STAT_FW_DATE, date, &len);
9284
Jeff Garzik0edd5b42005-09-07 00:48:31 -04009285 snprintf(info->fw_version, sizeof(info->fw_version), "%s (%s)",
James Ketrenos43f66a62005-03-25 12:31:53 -06009286 vers, date);
9287 strcpy(info->bus_info, pci_name(p->pci_dev));
James Ketrenosb095c382005-08-24 22:04:42 -05009288 info->eedump_len = IPW_EEPROM_IMAGE_SIZE;
James Ketrenos43f66a62005-03-25 12:31:53 -06009289}
9290
9291static u32 ipw_ethtool_get_link(struct net_device *dev)
9292{
9293 struct ipw_priv *priv = ieee80211_priv(dev);
9294 return (priv->status & STATUS_ASSOCIATED) != 0;
9295}
9296
9297static int ipw_ethtool_get_eeprom_len(struct net_device *dev)
9298{
James Ketrenosb095c382005-08-24 22:04:42 -05009299 return IPW_EEPROM_IMAGE_SIZE;
James Ketrenos43f66a62005-03-25 12:31:53 -06009300}
9301
9302static int ipw_ethtool_get_eeprom(struct net_device *dev,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04009303 struct ethtool_eeprom *eeprom, u8 * bytes)
James Ketrenos43f66a62005-03-25 12:31:53 -06009304{
9305 struct ipw_priv *p = ieee80211_priv(dev);
9306
James Ketrenosb095c382005-08-24 22:04:42 -05009307 if (eeprom->offset + eeprom->len > IPW_EEPROM_IMAGE_SIZE)
James Ketrenos43f66a62005-03-25 12:31:53 -06009308 return -EINVAL;
James Ketrenosc848d0a2005-08-24 21:56:24 -05009309 down(&p->sem);
Jeff Garzik0edd5b42005-09-07 00:48:31 -04009310 memcpy(bytes, &((u8 *) p->eeprom)[eeprom->offset], eeprom->len);
James Ketrenosc848d0a2005-08-24 21:56:24 -05009311 up(&p->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06009312 return 0;
9313}
9314
9315static int ipw_ethtool_set_eeprom(struct net_device *dev,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04009316 struct ethtool_eeprom *eeprom, u8 * bytes)
James Ketrenos43f66a62005-03-25 12:31:53 -06009317{
9318 struct ipw_priv *p = ieee80211_priv(dev);
9319 int i;
9320
James Ketrenosb095c382005-08-24 22:04:42 -05009321 if (eeprom->offset + eeprom->len > IPW_EEPROM_IMAGE_SIZE)
James Ketrenos43f66a62005-03-25 12:31:53 -06009322 return -EINVAL;
James Ketrenosc848d0a2005-08-24 21:56:24 -05009323 down(&p->sem);
Jeff Garzik0edd5b42005-09-07 00:48:31 -04009324 memcpy(&((u8 *) p->eeprom)[eeprom->offset], bytes, eeprom->len);
Jeff Garzikbf794512005-07-31 13:07:26 -04009325 for (i = IPW_EEPROM_DATA;
James Ketrenosb095c382005-08-24 22:04:42 -05009326 i < IPW_EEPROM_DATA + IPW_EEPROM_IMAGE_SIZE; i++)
James Ketrenos43f66a62005-03-25 12:31:53 -06009327 ipw_write8(p, i, p->eeprom[i]);
James Ketrenosc848d0a2005-08-24 21:56:24 -05009328 up(&p->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06009329 return 0;
9330}
9331
9332static struct ethtool_ops ipw_ethtool_ops = {
James Ketrenosea2b26e2005-08-24 21:25:16 -05009333 .get_link = ipw_ethtool_get_link,
9334 .get_drvinfo = ipw_ethtool_get_drvinfo,
9335 .get_eeprom_len = ipw_ethtool_get_eeprom_len,
9336 .get_eeprom = ipw_ethtool_get_eeprom,
9337 .set_eeprom = ipw_ethtool_set_eeprom,
James Ketrenos43f66a62005-03-25 12:31:53 -06009338};
9339
9340static irqreturn_t ipw_isr(int irq, void *data, struct pt_regs *regs)
9341{
9342 struct ipw_priv *priv = data;
9343 u32 inta, inta_mask;
Jeff Garzikbf794512005-07-31 13:07:26 -04009344
James Ketrenos43f66a62005-03-25 12:31:53 -06009345 if (!priv)
9346 return IRQ_NONE;
9347
9348 spin_lock(&priv->lock);
9349
9350 if (!(priv->status & STATUS_INT_ENABLED)) {
9351 /* Shared IRQ */
9352 goto none;
9353 }
9354
James Ketrenosb095c382005-08-24 22:04:42 -05009355 inta = ipw_read32(priv, IPW_INTA_RW);
9356 inta_mask = ipw_read32(priv, IPW_INTA_MASK_R);
Jeff Garzikbf794512005-07-31 13:07:26 -04009357
James Ketrenos43f66a62005-03-25 12:31:53 -06009358 if (inta == 0xFFFFFFFF) {
9359 /* Hardware disappeared */
9360 IPW_WARNING("IRQ INTA == 0xFFFFFFFF\n");
9361 goto none;
9362 }
9363
James Ketrenosb095c382005-08-24 22:04:42 -05009364 if (!(inta & (IPW_INTA_MASK_ALL & inta_mask))) {
James Ketrenos43f66a62005-03-25 12:31:53 -06009365 /* Shared interrupt */
9366 goto none;
9367 }
9368
9369 /* tell the device to stop sending interrupts */
9370 ipw_disable_interrupts(priv);
Jeff Garzikbf794512005-07-31 13:07:26 -04009371
James Ketrenos43f66a62005-03-25 12:31:53 -06009372 /* ack current interrupts */
James Ketrenosb095c382005-08-24 22:04:42 -05009373 inta &= (IPW_INTA_MASK_ALL & inta_mask);
9374 ipw_write32(priv, IPW_INTA_RW, inta);
Jeff Garzikbf794512005-07-31 13:07:26 -04009375
James Ketrenos43f66a62005-03-25 12:31:53 -06009376 /* Cache INTA value for our tasklet */
9377 priv->isr_inta = inta;
9378
9379 tasklet_schedule(&priv->irq_tasklet);
9380
Jeff Garzik0edd5b42005-09-07 00:48:31 -04009381 spin_unlock(&priv->lock);
James Ketrenos43f66a62005-03-25 12:31:53 -06009382
9383 return IRQ_HANDLED;
Jeff Garzik0edd5b42005-09-07 00:48:31 -04009384 none:
James Ketrenos43f66a62005-03-25 12:31:53 -06009385 spin_unlock(&priv->lock);
9386 return IRQ_NONE;
9387}
9388
9389static void ipw_rf_kill(void *adapter)
9390{
9391 struct ipw_priv *priv = adapter;
9392 unsigned long flags;
Jeff Garzikbf794512005-07-31 13:07:26 -04009393
James Ketrenos43f66a62005-03-25 12:31:53 -06009394 spin_lock_irqsave(&priv->lock, flags);
9395
9396 if (rf_kill_active(priv)) {
9397 IPW_DEBUG_RF_KILL("RF Kill active, rescheduling GPIO check\n");
9398 if (priv->workqueue)
9399 queue_delayed_work(priv->workqueue,
9400 &priv->rf_kill, 2 * HZ);
9401 goto exit_unlock;
9402 }
9403
9404 /* RF Kill is now disabled, so bring the device back up */
9405
9406 if (!(priv->status & STATUS_RF_KILL_MASK)) {
9407 IPW_DEBUG_RF_KILL("HW RF Kill no longer active, restarting "
9408 "device\n");
9409
9410 /* we can not do an adapter restart while inside an irq lock */
9411 queue_work(priv->workqueue, &priv->adapter_restart);
Jeff Garzikbf794512005-07-31 13:07:26 -04009412 } else
James Ketrenos43f66a62005-03-25 12:31:53 -06009413 IPW_DEBUG_RF_KILL("HW RF Kill deactivated. SW RF Kill still "
9414 "enabled\n");
9415
Jeff Garzik0edd5b42005-09-07 00:48:31 -04009416 exit_unlock:
James Ketrenos43f66a62005-03-25 12:31:53 -06009417 spin_unlock_irqrestore(&priv->lock, flags);
9418}
9419
James Ketrenosc848d0a2005-08-24 21:56:24 -05009420static void ipw_bg_rf_kill(void *data)
9421{
9422 struct ipw_priv *priv = data;
9423 down(&priv->sem);
9424 ipw_rf_kill(data);
9425 up(&priv->sem);
9426}
9427
James Ketrenosa613bff2005-08-24 21:43:11 -05009428void ipw_link_up(struct ipw_priv *priv)
9429{
9430 netif_carrier_on(priv->net_dev);
9431 if (netif_queue_stopped(priv->net_dev)) {
9432 IPW_DEBUG_NOTIF("waking queue\n");
9433 netif_wake_queue(priv->net_dev);
9434 } else {
9435 IPW_DEBUG_NOTIF("starting queue\n");
9436 netif_start_queue(priv->net_dev);
9437 }
9438
James Ketrenosc848d0a2005-08-24 21:56:24 -05009439 cancel_delayed_work(&priv->request_scan);
James Ketrenosa613bff2005-08-24 21:43:11 -05009440 ipw_reset_stats(priv);
9441 /* Ensure the rate is updated immediately */
9442 priv->last_rate = ipw_get_current_rate(priv);
9443 ipw_gather_stats(priv);
9444 ipw_led_link_up(priv);
9445 notify_wx_assoc_event(priv);
9446
9447 if (priv->config & CFG_BACKGROUND_SCAN)
9448 queue_delayed_work(priv->workqueue, &priv->request_scan, HZ);
9449}
9450
James Ketrenosc848d0a2005-08-24 21:56:24 -05009451static void ipw_bg_link_up(void *data)
9452{
9453 struct ipw_priv *priv = data;
9454 down(&priv->sem);
9455 ipw_link_up(data);
9456 up(&priv->sem);
9457}
9458
James Ketrenosa613bff2005-08-24 21:43:11 -05009459void ipw_link_down(struct ipw_priv *priv)
9460{
9461 ipw_led_link_down(priv);
9462 netif_carrier_off(priv->net_dev);
9463 netif_stop_queue(priv->net_dev);
9464 notify_wx_assoc_event(priv);
9465
9466 /* Cancel any queued work ... */
9467 cancel_delayed_work(&priv->request_scan);
9468 cancel_delayed_work(&priv->adhoc_check);
9469 cancel_delayed_work(&priv->gather_stats);
9470
9471 ipw_reset_stats(priv);
9472
9473 /* Queue up another scan... */
9474 queue_work(priv->workqueue, &priv->request_scan);
9475}
9476
James Ketrenosc848d0a2005-08-24 21:56:24 -05009477static void ipw_bg_link_down(void *data)
9478{
9479 struct ipw_priv *priv = data;
9480 down(&priv->sem);
9481 ipw_link_down(data);
9482 up(&priv->sem);
9483}
9484
James Ketrenos43f66a62005-03-25 12:31:53 -06009485static int ipw_setup_deferred_work(struct ipw_priv *priv)
9486{
9487 int ret = 0;
9488
James Ketrenos43f66a62005-03-25 12:31:53 -06009489 priv->workqueue = create_workqueue(DRV_NAME);
James Ketrenos43f66a62005-03-25 12:31:53 -06009490 init_waitqueue_head(&priv->wait_command_queue);
9491
James Ketrenosc848d0a2005-08-24 21:56:24 -05009492 INIT_WORK(&priv->adhoc_check, ipw_bg_adhoc_check, priv);
9493 INIT_WORK(&priv->associate, ipw_bg_associate, priv);
9494 INIT_WORK(&priv->disassociate, ipw_bg_disassociate, priv);
9495 INIT_WORK(&priv->rx_replenish, ipw_bg_rx_queue_replenish, priv);
9496 INIT_WORK(&priv->adapter_restart, ipw_bg_adapter_restart, priv);
9497 INIT_WORK(&priv->rf_kill, ipw_bg_rf_kill, priv);
9498 INIT_WORK(&priv->up, (void (*)(void *))ipw_bg_up, priv);
9499 INIT_WORK(&priv->down, (void (*)(void *))ipw_bg_down, priv);
Jeff Garzikbf794512005-07-31 13:07:26 -04009500 INIT_WORK(&priv->request_scan,
James Ketrenosb095c382005-08-24 22:04:42 -05009501 (void (*)(void *))ipw_request_scan, priv);
Jeff Garzikbf794512005-07-31 13:07:26 -04009502 INIT_WORK(&priv->gather_stats,
James Ketrenosc848d0a2005-08-24 21:56:24 -05009503 (void (*)(void *))ipw_bg_gather_stats, priv);
9504 INIT_WORK(&priv->abort_scan, (void (*)(void *))ipw_bg_abort_scan, priv);
9505 INIT_WORK(&priv->roam, ipw_bg_roam, priv);
9506 INIT_WORK(&priv->scan_check, ipw_bg_scan_check, priv);
9507 INIT_WORK(&priv->link_up, (void (*)(void *))ipw_bg_link_up, priv);
9508 INIT_WORK(&priv->link_down, (void (*)(void *))ipw_bg_link_down, priv);
9509 INIT_WORK(&priv->led_link_on, (void (*)(void *))ipw_bg_led_link_on,
James Ketrenosa613bff2005-08-24 21:43:11 -05009510 priv);
James Ketrenosc848d0a2005-08-24 21:56:24 -05009511 INIT_WORK(&priv->led_link_off, (void (*)(void *))ipw_bg_led_link_off,
James Ketrenosa613bff2005-08-24 21:43:11 -05009512 priv);
James Ketrenosc848d0a2005-08-24 21:56:24 -05009513 INIT_WORK(&priv->led_act_off, (void (*)(void *))ipw_bg_led_activity_off,
9514 priv);
9515 INIT_WORK(&priv->merge_networks,
9516 (void (*)(void *))ipw_merge_adhoc_network, priv);
James Ketrenos43f66a62005-03-25 12:31:53 -06009517
James Ketrenosb095c382005-08-24 22:04:42 -05009518#ifdef CONFIG_IPW_QOS
9519 INIT_WORK(&priv->qos_activate, (void (*)(void *))ipw_bg_qos_activate,
9520 priv);
9521#endif /* CONFIG_IPW_QOS */
9522
James Ketrenos43f66a62005-03-25 12:31:53 -06009523 tasklet_init(&priv->irq_tasklet, (void (*)(unsigned long))
9524 ipw_irq_tasklet, (unsigned long)priv);
9525
9526 return ret;
9527}
9528
James Ketrenos43f66a62005-03-25 12:31:53 -06009529static void shim__set_security(struct net_device *dev,
9530 struct ieee80211_security *sec)
9531{
9532 struct ipw_priv *priv = ieee80211_priv(dev);
9533 int i;
James Ketrenosc848d0a2005-08-24 21:56:24 -05009534 down(&priv->sem);
Jeff Garzikbf794512005-07-31 13:07:26 -04009535 for (i = 0; i < 4; i++) {
James Ketrenos43f66a62005-03-25 12:31:53 -06009536 if (sec->flags & (1 << i)) {
James Ketrenosb095c382005-08-24 22:04:42 -05009537 priv->ieee->sec.key_sizes[i] = sec->key_sizes[i];
James Ketrenos43f66a62005-03-25 12:31:53 -06009538 if (sec->key_sizes[i] == 0)
James Ketrenosb095c382005-08-24 22:04:42 -05009539 priv->ieee->sec.flags &= ~(1 << i);
9540 else {
9541 memcpy(priv->ieee->sec.keys[i], sec->keys[i],
James Ketrenos43f66a62005-03-25 12:31:53 -06009542 sec->key_sizes[i]);
James Ketrenosb095c382005-08-24 22:04:42 -05009543 priv->ieee->sec.flags |= (1 << i);
9544 }
James Ketrenos43f66a62005-03-25 12:31:53 -06009545 priv->status |= STATUS_SECURITY_UPDATED;
James Ketrenosb095c382005-08-24 22:04:42 -05009546 } else if (sec->level != SEC_LEVEL_1)
9547 priv->ieee->sec.flags &= ~(1 << i);
James Ketrenos43f66a62005-03-25 12:31:53 -06009548 }
9549
James Ketrenosb095c382005-08-24 22:04:42 -05009550 if (sec->flags & SEC_ACTIVE_KEY) {
James Ketrenos43f66a62005-03-25 12:31:53 -06009551 if (sec->active_key <= 3) {
James Ketrenosb095c382005-08-24 22:04:42 -05009552 priv->ieee->sec.active_key = sec->active_key;
9553 priv->ieee->sec.flags |= SEC_ACTIVE_KEY;
Jeff Garzikbf794512005-07-31 13:07:26 -04009554 } else
James Ketrenosb095c382005-08-24 22:04:42 -05009555 priv->ieee->sec.flags &= ~SEC_ACTIVE_KEY;
James Ketrenos43f66a62005-03-25 12:31:53 -06009556 priv->status |= STATUS_SECURITY_UPDATED;
James Ketrenosb095c382005-08-24 22:04:42 -05009557 } else
9558 priv->ieee->sec.flags &= ~SEC_ACTIVE_KEY;
James Ketrenos43f66a62005-03-25 12:31:53 -06009559
9560 if ((sec->flags & SEC_AUTH_MODE) &&
James Ketrenosb095c382005-08-24 22:04:42 -05009561 (priv->ieee->sec.auth_mode != sec->auth_mode)) {
9562 priv->ieee->sec.auth_mode = sec->auth_mode;
9563 priv->ieee->sec.flags |= SEC_AUTH_MODE;
James Ketrenos43f66a62005-03-25 12:31:53 -06009564 if (sec->auth_mode == WLAN_AUTH_SHARED_KEY)
9565 priv->capability |= CAP_SHARED_KEY;
9566 else
9567 priv->capability &= ~CAP_SHARED_KEY;
9568 priv->status |= STATUS_SECURITY_UPDATED;
9569 }
Jeff Garzikbf794512005-07-31 13:07:26 -04009570
James Ketrenosb095c382005-08-24 22:04:42 -05009571 if (sec->flags & SEC_ENABLED && priv->ieee->sec.enabled != sec->enabled) {
9572 priv->ieee->sec.flags |= SEC_ENABLED;
9573 priv->ieee->sec.enabled = sec->enabled;
James Ketrenos43f66a62005-03-25 12:31:53 -06009574 priv->status |= STATUS_SECURITY_UPDATED;
Jeff Garzikbf794512005-07-31 13:07:26 -04009575 if (sec->enabled)
James Ketrenos43f66a62005-03-25 12:31:53 -06009576 priv->capability |= CAP_PRIVACY_ON;
9577 else
9578 priv->capability &= ~CAP_PRIVACY_ON;
9579 }
James Ketrenosb095c382005-08-24 22:04:42 -05009580 priv->ieee->sec.encrypt = sec->encrypt;
Jeff Garzikbf794512005-07-31 13:07:26 -04009581
James Ketrenosb095c382005-08-24 22:04:42 -05009582 if (sec->flags & SEC_LEVEL && priv->ieee->sec.level != sec->level) {
9583 priv->ieee->sec.level = sec->level;
9584 priv->ieee->sec.flags |= SEC_LEVEL;
James Ketrenos43f66a62005-03-25 12:31:53 -06009585 priv->status |= STATUS_SECURITY_UPDATED;
9586 }
9587
James Ketrenosb095c382005-08-24 22:04:42 -05009588 if (!priv->ieee->host_encrypt)
9589 ipw_set_hwcrypto_keys(priv);
9590
Jeff Garzikbf794512005-07-31 13:07:26 -04009591 /* To match current functionality of ipw2100 (which works well w/
9592 * various supplicants, we don't force a disassociate if the
James Ketrenos43f66a62005-03-25 12:31:53 -06009593 * privacy capability changes ... */
9594#if 0
9595 if ((priv->status & (STATUS_ASSOCIATED | STATUS_ASSOCIATING)) &&
Jeff Garzikbf794512005-07-31 13:07:26 -04009596 (((priv->assoc_request.capability &
James Ketrenos43f66a62005-03-25 12:31:53 -06009597 WLAN_CAPABILITY_PRIVACY) && !sec->enabled) ||
Jeff Garzikbf794512005-07-31 13:07:26 -04009598 (!(priv->assoc_request.capability &
Jeff Garzik0edd5b42005-09-07 00:48:31 -04009599 WLAN_CAPABILITY_PRIVACY) && sec->enabled))) {
James Ketrenos43f66a62005-03-25 12:31:53 -06009600 IPW_DEBUG_ASSOC("Disassociating due to capability "
9601 "change.\n");
9602 ipw_disassociate(priv);
9603 }
9604#endif
James Ketrenosc848d0a2005-08-24 21:56:24 -05009605 up(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06009606}
9607
Jeff Garzikbf794512005-07-31 13:07:26 -04009608static int init_supported_rates(struct ipw_priv *priv,
James Ketrenos43f66a62005-03-25 12:31:53 -06009609 struct ipw_supported_rates *rates)
9610{
9611 /* TODO: Mask out rates based on priv->rates_mask */
9612
9613 memset(rates, 0, sizeof(*rates));
Jeff Garzik0edd5b42005-09-07 00:48:31 -04009614 /* configure supported rates */
James Ketrenos43f66a62005-03-25 12:31:53 -06009615 switch (priv->ieee->freq_band) {
9616 case IEEE80211_52GHZ_BAND:
9617 rates->ieee_mode = IPW_A_MODE;
9618 rates->purpose = IPW_RATE_CAPABILITIES;
9619 ipw_add_ofdm_scan_rates(rates, IEEE80211_CCK_MODULATION,
9620 IEEE80211_OFDM_DEFAULT_RATES_MASK);
9621 break;
9622
Jeff Garzik0edd5b42005-09-07 00:48:31 -04009623 default: /* Mixed or 2.4Ghz */
James Ketrenos43f66a62005-03-25 12:31:53 -06009624 rates->ieee_mode = IPW_G_MODE;
9625 rates->purpose = IPW_RATE_CAPABILITIES;
9626 ipw_add_cck_scan_rates(rates, IEEE80211_CCK_MODULATION,
9627 IEEE80211_CCK_DEFAULT_RATES_MASK);
9628 if (priv->ieee->modulation & IEEE80211_OFDM_MODULATION) {
9629 ipw_add_ofdm_scan_rates(rates, IEEE80211_CCK_MODULATION,
9630 IEEE80211_OFDM_DEFAULT_RATES_MASK);
9631 }
9632 break;
9633 }
9634
9635 return 0;
9636}
9637
Jeff Garzikbf794512005-07-31 13:07:26 -04009638static int ipw_config(struct ipw_priv *priv)
James Ketrenos43f66a62005-03-25 12:31:53 -06009639{
9640 int i;
9641 struct ipw_tx_power tx_power;
9642
9643 memset(&priv->sys_config, 0, sizeof(priv->sys_config));
9644 memset(&tx_power, 0, sizeof(tx_power));
9645
9646 /* This is only called from ipw_up, which resets/reloads the firmware
9647 so, we don't need to first disable the card before we configure
9648 it */
9649
9650 /* configure device for 'G' band */
9651 tx_power.ieee_mode = IPW_G_MODE;
9652 tx_power.num_channels = 11;
9653 for (i = 0; i < 11; i++) {
9654 tx_power.channels_tx_power[i].channel_number = i + 1;
9655 tx_power.channels_tx_power[i].tx_power = priv->tx_power;
9656 }
9657 if (ipw_send_tx_power(priv, &tx_power))
9658 goto error;
9659
9660 /* configure device to also handle 'B' band */
9661 tx_power.ieee_mode = IPW_B_MODE;
9662 if (ipw_send_tx_power(priv, &tx_power))
9663 goto error;
9664
9665 /* initialize adapter address */
9666 if (ipw_send_adapter_address(priv, priv->net_dev->dev_addr))
9667 goto error;
9668
9669 /* set basic system config settings */
9670 init_sys_config(&priv->sys_config);
James Ketrenosc848d0a2005-08-24 21:56:24 -05009671 if (priv->ieee->iw_mode == IW_MODE_ADHOC)
9672 priv->sys_config.answer_broadcast_ssid_probe = 1;
9673 else
9674 priv->sys_config.answer_broadcast_ssid_probe = 0;
9675
James Ketrenos43f66a62005-03-25 12:31:53 -06009676 if (ipw_send_system_config(priv, &priv->sys_config))
9677 goto error;
9678
Jeff Garzik0edd5b42005-09-07 00:48:31 -04009679 init_supported_rates(priv, &priv->rates);
9680 if (ipw_send_supported_rates(priv, &priv->rates))
James Ketrenos43f66a62005-03-25 12:31:53 -06009681 goto error;
9682
9683 /* Set request-to-send threshold */
9684 if (priv->rts_threshold) {
9685 if (ipw_send_rts_threshold(priv, priv->rts_threshold))
9686 goto error;
9687 }
James Ketrenosb095c382005-08-24 22:04:42 -05009688#ifdef CONFIG_IPW_QOS
9689 IPW_DEBUG_QOS("QoS: call ipw_qos_activate\n");
9690 ipw_qos_activate(priv, NULL);
9691#endif /* CONFIG_IPW_QOS */
James Ketrenos43f66a62005-03-25 12:31:53 -06009692
9693 if (ipw_set_random_seed(priv))
9694 goto error;
Jeff Garzikbf794512005-07-31 13:07:26 -04009695
James Ketrenos43f66a62005-03-25 12:31:53 -06009696 /* final state transition to the RUN state */
9697 if (ipw_send_host_complete(priv))
9698 goto error;
9699
9700 /* If configured to try and auto-associate, kick off a scan */
James Ketrenosb095c382005-08-24 22:04:42 -05009701 if (priv->config & CFG_ASSOCIATE)
9702 queue_work(priv->workqueue, &priv->request_scan);
James Ketrenos43f66a62005-03-25 12:31:53 -06009703
9704 return 0;
Jeff Garzikbf794512005-07-31 13:07:26 -04009705
Jeff Garzik0edd5b42005-09-07 00:48:31 -04009706 error:
James Ketrenos43f66a62005-03-25 12:31:53 -06009707 return -EIO;
9708}
9709
9710#define MAX_HW_RESTARTS 5
9711static int ipw_up(struct ipw_priv *priv)
9712{
9713 int rc, i;
9714
9715 if (priv->status & STATUS_EXIT_PENDING)
9716 return -EIO;
9717
Jeff Garzik0edd5b42005-09-07 00:48:31 -04009718 for (i = 0; i < MAX_HW_RESTARTS; i++) {
Jeff Garzikbf794512005-07-31 13:07:26 -04009719 /* Load the microcode, firmware, and eeprom.
James Ketrenos43f66a62005-03-25 12:31:53 -06009720 * Also start the clocks. */
9721 rc = ipw_load(priv);
9722 if (rc) {
Jeff Garzik0edd5b42005-09-07 00:48:31 -04009723 IPW_ERROR("Unable to load firmware: 0x%08X\n", rc);
James Ketrenos43f66a62005-03-25 12:31:53 -06009724 return rc;
9725 }
9726
9727 ipw_init_ordinals(priv);
9728 if (!(priv->config & CFG_CUSTOM_MAC))
9729 eeprom_parse_mac(priv, priv->mac_addr);
9730 memcpy(priv->net_dev->dev_addr, priv->mac_addr, ETH_ALEN);
9731
James Ketrenosb095c382005-08-24 22:04:42 -05009732 if (priv->status & STATUS_RF_KILL_SW) {
9733 IPW_WARNING("Radio disabled by module parameter.\n");
9734 return 0;
9735 } else if (rf_kill_active(priv)) {
9736 IPW_WARNING("Radio Frequency Kill Switch is On:\n"
9737 "Kill switch must be turned off for "
9738 "wireless networking to work.\n");
9739 queue_delayed_work(priv->workqueue, &priv->rf_kill,
9740 2 * HZ);
James Ketrenos43f66a62005-03-25 12:31:53 -06009741 return 0;
James Ketrenosc848d0a2005-08-24 21:56:24 -05009742 }
James Ketrenos43f66a62005-03-25 12:31:53 -06009743
9744 rc = ipw_config(priv);
9745 if (!rc) {
9746 IPW_DEBUG_INFO("Configured device on count %i\n", i);
James Ketrenosa613bff2005-08-24 21:43:11 -05009747 ipw_led_init(priv);
9748 ipw_led_radio_on(priv);
James Ketrenos43f66a62005-03-25 12:31:53 -06009749 priv->notif_missed_beacons = 0;
James Ketrenosc848d0a2005-08-24 21:56:24 -05009750 priv->status |= STATUS_INIT;
James Ketrenos43f66a62005-03-25 12:31:53 -06009751 return 0;
James Ketrenos43f66a62005-03-25 12:31:53 -06009752 }
Jeff Garzikbf794512005-07-31 13:07:26 -04009753
James Ketrenosc848d0a2005-08-24 21:56:24 -05009754 IPW_DEBUG_INFO("Device configuration failed: 0x%08X\n", rc);
James Ketrenos43f66a62005-03-25 12:31:53 -06009755 IPW_DEBUG_INFO("Failed to config device on retry %d of %d\n",
9756 i, MAX_HW_RESTARTS);
9757
9758 /* We had an error bringing up the hardware, so take it
9759 * all the way back down so we can try again */
9760 ipw_down(priv);
9761 }
9762
Jeff Garzikbf794512005-07-31 13:07:26 -04009763 /* tried to restart and config the device for as long as our
James Ketrenos43f66a62005-03-25 12:31:53 -06009764 * patience could withstand */
Jeff Garzik0edd5b42005-09-07 00:48:31 -04009765 IPW_ERROR("Unable to initialize device after %d attempts.\n", i);
James Ketrenosc848d0a2005-08-24 21:56:24 -05009766
James Ketrenos43f66a62005-03-25 12:31:53 -06009767 return -EIO;
9768}
9769
James Ketrenosc848d0a2005-08-24 21:56:24 -05009770static void ipw_bg_up(void *data)
9771{
9772 struct ipw_priv *priv = data;
9773 down(&priv->sem);
9774 ipw_up(data);
9775 up(&priv->sem);
9776}
9777
James Ketrenosb095c382005-08-24 22:04:42 -05009778static void ipw_deinit(struct ipw_priv *priv)
James Ketrenos43f66a62005-03-25 12:31:53 -06009779{
James Ketrenosb095c382005-08-24 22:04:42 -05009780 int i;
9781
9782 if (priv->status & STATUS_SCANNING) {
9783 IPW_DEBUG_INFO("Aborting scan during shutdown.\n");
9784 ipw_abort_scan(priv);
9785 }
9786
9787 if (priv->status & STATUS_ASSOCIATED) {
9788 IPW_DEBUG_INFO("Disassociating during shutdown.\n");
9789 ipw_disassociate(priv);
9790 }
9791
9792 ipw_led_shutdown(priv);
9793
9794 /* Wait up to 1s for status to change to not scanning and not
9795 * associated (disassociation can take a while for a ful 802.11
9796 * exchange */
9797 for (i = 1000; i && (priv->status &
9798 (STATUS_DISASSOCIATING |
9799 STATUS_ASSOCIATED | STATUS_SCANNING)); i--)
9800 udelay(10);
9801
9802 if (priv->status & (STATUS_DISASSOCIATING |
9803 STATUS_ASSOCIATED | STATUS_SCANNING))
9804 IPW_DEBUG_INFO("Still associated or scanning...\n");
9805 else
9806 IPW_DEBUG_INFO("Took %dms to de-init\n", 1000 - i);
9807
James Ketrenosc848d0a2005-08-24 21:56:24 -05009808 /* Attempt to disable the card */
James Ketrenos43f66a62005-03-25 12:31:53 -06009809 ipw_send_card_disable(priv, 0);
James Ketrenosb095c382005-08-24 22:04:42 -05009810
9811 priv->status &= ~STATUS_INIT;
9812}
9813
9814static void ipw_down(struct ipw_priv *priv)
9815{
9816 int exit_pending = priv->status & STATUS_EXIT_PENDING;
9817
9818 priv->status |= STATUS_EXIT_PENDING;
9819
9820 if (ipw_is_init(priv))
9821 ipw_deinit(priv);
9822
9823 /* Wipe out the EXIT_PENDING status bit if we are not actually
9824 * exiting the module */
9825 if (!exit_pending)
9826 priv->status &= ~STATUS_EXIT_PENDING;
James Ketrenos43f66a62005-03-25 12:31:53 -06009827
9828 /* tell the device to stop sending interrupts */
9829 ipw_disable_interrupts(priv);
9830
9831 /* Clear all bits but the RF Kill */
James Ketrenosb095c382005-08-24 22:04:42 -05009832 priv->status &= STATUS_RF_KILL_MASK | STATUS_EXIT_PENDING;
James Ketrenos43f66a62005-03-25 12:31:53 -06009833 netif_carrier_off(priv->net_dev);
9834 netif_stop_queue(priv->net_dev);
9835
9836 ipw_stop_nic(priv);
James Ketrenosa613bff2005-08-24 21:43:11 -05009837
9838 ipw_led_radio_off(priv);
James Ketrenos43f66a62005-03-25 12:31:53 -06009839}
9840
James Ketrenosc848d0a2005-08-24 21:56:24 -05009841static void ipw_bg_down(void *data)
9842{
9843 struct ipw_priv *priv = data;
9844 down(&priv->sem);
9845 ipw_down(data);
9846 up(&priv->sem);
9847}
9848
James Ketrenosea2b26e2005-08-24 21:25:16 -05009849static int ipw_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
9850{
James Ketrenosea2b26e2005-08-24 21:25:16 -05009851 struct iwreq *wrq = (struct iwreq *)rq;
9852 int ret = -1;
9853 switch (cmd) {
9854 case IPW_IOCTL_WPA_SUPPLICANT:
9855 ret = ipw_wpa_supplicant(dev, &wrq->u.data);
9856 return ret;
9857
9858 default:
9859 return -EOPNOTSUPP;
9860 }
9861
James Ketrenosea2b26e2005-08-24 21:25:16 -05009862 return -EOPNOTSUPP;
9863}
9864
James Ketrenos43f66a62005-03-25 12:31:53 -06009865/* Called by register_netdev() */
9866static int ipw_net_init(struct net_device *dev)
9867{
9868 struct ipw_priv *priv = ieee80211_priv(dev);
James Ketrenosc848d0a2005-08-24 21:56:24 -05009869 down(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06009870
James Ketrenosc848d0a2005-08-24 21:56:24 -05009871 if (ipw_up(priv)) {
9872 up(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06009873 return -EIO;
James Ketrenosc848d0a2005-08-24 21:56:24 -05009874 }
James Ketrenos43f66a62005-03-25 12:31:53 -06009875
James Ketrenosc848d0a2005-08-24 21:56:24 -05009876 up(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06009877 return 0;
9878}
9879
9880/* PCI driver stuff */
9881static struct pci_device_id card_ids[] = {
9882 {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2701, 0, 0, 0},
9883 {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2702, 0, 0, 0},
9884 {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2711, 0, 0, 0},
9885 {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2712, 0, 0, 0},
9886 {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2721, 0, 0, 0},
9887 {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2722, 0, 0, 0},
9888 {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2731, 0, 0, 0},
9889 {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2732, 0, 0, 0},
9890 {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2741, 0, 0, 0},
9891 {PCI_VENDOR_ID_INTEL, 0x1043, 0x103c, 0x2741, 0, 0, 0},
9892 {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2742, 0, 0, 0},
9893 {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2751, 0, 0, 0},
9894 {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2752, 0, 0, 0},
9895 {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2753, 0, 0, 0},
9896 {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2754, 0, 0, 0},
9897 {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2761, 0, 0, 0},
9898 {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2762, 0, 0, 0},
9899 {PCI_VENDOR_ID_INTEL, 0x104f, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
Jeff Garzik0edd5b42005-09-07 00:48:31 -04009900 {PCI_VENDOR_ID_INTEL, 0x4220, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, /* BG */
James Ketrenosa613bff2005-08-24 21:43:11 -05009901 {PCI_VENDOR_ID_INTEL, 0x4221, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, /* BG */
Jeff Garzik0edd5b42005-09-07 00:48:31 -04009902 {PCI_VENDOR_ID_INTEL, 0x4223, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, /* ABG */
9903 {PCI_VENDOR_ID_INTEL, 0x4224, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, /* ABG */
Jeff Garzikbf794512005-07-31 13:07:26 -04009904
James Ketrenos43f66a62005-03-25 12:31:53 -06009905 /* required last entry */
9906 {0,}
9907};
9908
9909MODULE_DEVICE_TABLE(pci, card_ids);
9910
9911static struct attribute *ipw_sysfs_entries[] = {
9912 &dev_attr_rf_kill.attr,
9913 &dev_attr_direct_dword.attr,
9914 &dev_attr_indirect_byte.attr,
9915 &dev_attr_indirect_dword.attr,
9916 &dev_attr_mem_gpio_reg.attr,
9917 &dev_attr_command_event_reg.attr,
9918 &dev_attr_nic_type.attr,
9919 &dev_attr_status.attr,
9920 &dev_attr_cfg.attr,
9921 &dev_attr_dump_errors.attr,
9922 &dev_attr_dump_events.attr,
9923 &dev_attr_eeprom_delay.attr,
9924 &dev_attr_ucode_version.attr,
9925 &dev_attr_rtc.attr,
James Ketrenosa613bff2005-08-24 21:43:11 -05009926 &dev_attr_scan_age.attr,
9927 &dev_attr_led.attr,
James Ketrenosb095c382005-08-24 22:04:42 -05009928 &dev_attr_speed_scan.attr,
9929 &dev_attr_net_stats.attr,
James Ketrenos43f66a62005-03-25 12:31:53 -06009930 NULL
9931};
9932
9933static struct attribute_group ipw_attribute_group = {
9934 .name = NULL, /* put in device directory */
Jeff Garzik0edd5b42005-09-07 00:48:31 -04009935 .attrs = ipw_sysfs_entries,
James Ketrenos43f66a62005-03-25 12:31:53 -06009936};
9937
Jeff Garzik0edd5b42005-09-07 00:48:31 -04009938static int ipw_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
James Ketrenos43f66a62005-03-25 12:31:53 -06009939{
9940 int err = 0;
9941 struct net_device *net_dev;
9942 void __iomem *base;
9943 u32 length, val;
9944 struct ipw_priv *priv;
James Ketrenos43f66a62005-03-25 12:31:53 -06009945
9946 net_dev = alloc_ieee80211(sizeof(struct ipw_priv));
9947 if (net_dev == NULL) {
9948 err = -ENOMEM;
9949 goto out;
9950 }
9951
9952 priv = ieee80211_priv(net_dev);
9953 priv->ieee = netdev_priv(net_dev);
James Ketrenosa613bff2005-08-24 21:43:11 -05009954
James Ketrenos43f66a62005-03-25 12:31:53 -06009955 priv->net_dev = net_dev;
9956 priv->pci_dev = pdev;
9957#ifdef CONFIG_IPW_DEBUG
9958 ipw_debug_level = debug;
9959#endif
9960 spin_lock_init(&priv->lock);
9961
James Ketrenosc848d0a2005-08-24 21:56:24 -05009962 init_MUTEX(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06009963 if (pci_enable_device(pdev)) {
9964 err = -ENODEV;
9965 goto out_free_ieee80211;
9966 }
9967
9968 pci_set_master(pdev);
9969
Tobias Klauser0e08b442005-06-20 14:28:41 -07009970 err = pci_set_dma_mask(pdev, DMA_32BIT_MASK);
Jeff Garzikbf794512005-07-31 13:07:26 -04009971 if (!err)
Tobias Klauser0e08b442005-06-20 14:28:41 -07009972 err = pci_set_consistent_dma_mask(pdev, DMA_32BIT_MASK);
James Ketrenos43f66a62005-03-25 12:31:53 -06009973 if (err) {
9974 printk(KERN_WARNING DRV_NAME ": No suitable DMA available.\n");
9975 goto out_pci_disable_device;
9976 }
9977
9978 pci_set_drvdata(pdev, priv);
9979
9980 err = pci_request_regions(pdev, DRV_NAME);
Jeff Garzikbf794512005-07-31 13:07:26 -04009981 if (err)
James Ketrenos43f66a62005-03-25 12:31:53 -06009982 goto out_pci_disable_device;
9983
Jeff Garzikbf794512005-07-31 13:07:26 -04009984 /* We disable the RETRY_TIMEOUT register (0x41) to keep
James Ketrenos43f66a62005-03-25 12:31:53 -06009985 * PCI Tx retries from interfering with C3 CPU state */
Jeff Garzikbf794512005-07-31 13:07:26 -04009986 pci_read_config_dword(pdev, 0x40, &val);
9987 if ((val & 0x0000ff00) != 0)
James Ketrenos43f66a62005-03-25 12:31:53 -06009988 pci_write_config_dword(pdev, 0x40, val & 0xffff00ff);
Jeff Garzikbf794512005-07-31 13:07:26 -04009989
James Ketrenos43f66a62005-03-25 12:31:53 -06009990 length = pci_resource_len(pdev, 0);
9991 priv->hw_len = length;
Jeff Garzikbf794512005-07-31 13:07:26 -04009992
James Ketrenos43f66a62005-03-25 12:31:53 -06009993 base = ioremap_nocache(pci_resource_start(pdev, 0), length);
9994 if (!base) {
9995 err = -ENODEV;
9996 goto out_pci_release_regions;
9997 }
9998
9999 priv->hw_base = base;
10000 IPW_DEBUG_INFO("pci_resource_len = 0x%08x\n", length);
10001 IPW_DEBUG_INFO("pci_resource_base = %p\n", base);
10002
10003 err = ipw_setup_deferred_work(priv);
10004 if (err) {
10005 IPW_ERROR("Unable to setup deferred work\n");
10006 goto out_iounmap;
10007 }
10008
James Ketrenosb095c382005-08-24 22:04:42 -050010009 ipw_sw_reset(priv, 1);
James Ketrenos43f66a62005-03-25 12:31:53 -060010010
Jeff Garzik0edd5b42005-09-07 00:48:31 -040010011 err = request_irq(pdev->irq, ipw_isr, SA_SHIRQ, DRV_NAME, priv);
James Ketrenos43f66a62005-03-25 12:31:53 -060010012 if (err) {
10013 IPW_ERROR("Error allocating IRQ %d\n", pdev->irq);
10014 goto out_destroy_workqueue;
10015 }
10016
10017 SET_MODULE_OWNER(net_dev);
10018 SET_NETDEV_DEV(net_dev, &pdev->dev);
10019
James Ketrenosa613bff2005-08-24 21:43:11 -050010020 ipw_wx_data.spy_data = &priv->ieee->spy_data;
10021 ipw_wx_data.ieee80211 = priv->ieee;
10022
James Ketrenosc848d0a2005-08-24 21:56:24 -050010023 down(&priv->sem);
10024
James Ketrenos43f66a62005-03-25 12:31:53 -060010025 priv->ieee->hard_start_xmit = ipw_net_hard_start_xmit;
10026 priv->ieee->set_security = shim__set_security;
10027
James Ketrenosb095c382005-08-24 22:04:42 -050010028#ifdef CONFIG_IPW_QOS
10029 priv->ieee->handle_management_frame = ipw_handle_management_frame;
10030#endif /* CONFIG_IPW_QOS */
10031
James Ketrenosc848d0a2005-08-24 21:56:24 -050010032 priv->ieee->perfect_rssi = -20;
10033 priv->ieee->worst_rssi = -85;
10034
James Ketrenos43f66a62005-03-25 12:31:53 -060010035 net_dev->open = ipw_net_open;
10036 net_dev->stop = ipw_net_stop;
10037 net_dev->init = ipw_net_init;
James Ketrenosea2b26e2005-08-24 21:25:16 -050010038 net_dev->do_ioctl = ipw_ioctl;
James Ketrenos43f66a62005-03-25 12:31:53 -060010039 net_dev->get_stats = ipw_net_get_stats;
10040 net_dev->set_multicast_list = ipw_net_set_multicast_list;
10041 net_dev->set_mac_address = ipw_net_set_mac_address;
10042 net_dev->get_wireless_stats = ipw_get_wireless_stats;
James Ketrenosa613bff2005-08-24 21:43:11 -050010043 net_dev->wireless_data = &ipw_wx_data;
James Ketrenos43f66a62005-03-25 12:31:53 -060010044 net_dev->wireless_handlers = &ipw_wx_handler_def;
10045 net_dev->ethtool_ops = &ipw_ethtool_ops;
10046 net_dev->irq = pdev->irq;
Jeff Garzik0edd5b42005-09-07 00:48:31 -040010047 net_dev->base_addr = (unsigned long)priv->hw_base;
James Ketrenos43f66a62005-03-25 12:31:53 -060010048 net_dev->mem_start = pci_resource_start(pdev, 0);
10049 net_dev->mem_end = net_dev->mem_start + pci_resource_len(pdev, 0) - 1;
10050
10051 err = sysfs_create_group(&pdev->dev.kobj, &ipw_attribute_group);
10052 if (err) {
10053 IPW_ERROR("failed to create sysfs device attributes\n");
James Ketrenosc848d0a2005-08-24 21:56:24 -050010054 up(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -060010055 goto out_release_irq;
10056 }
10057
James Ketrenosc848d0a2005-08-24 21:56:24 -050010058 up(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -060010059 err = register_netdev(net_dev);
10060 if (err) {
10061 IPW_ERROR("failed to register network device\n");
James Ketrenosa613bff2005-08-24 21:43:11 -050010062 goto out_remove_sysfs;
James Ketrenos43f66a62005-03-25 12:31:53 -060010063 }
James Ketrenos43f66a62005-03-25 12:31:53 -060010064 return 0;
10065
James Ketrenosa613bff2005-08-24 21:43:11 -050010066 out_remove_sysfs:
James Ketrenos43f66a62005-03-25 12:31:53 -060010067 sysfs_remove_group(&pdev->dev.kobj, &ipw_attribute_group);
Jeff Garzik0edd5b42005-09-07 00:48:31 -040010068 out_release_irq:
James Ketrenos43f66a62005-03-25 12:31:53 -060010069 free_irq(pdev->irq, priv);
Jeff Garzik0edd5b42005-09-07 00:48:31 -040010070 out_destroy_workqueue:
James Ketrenos43f66a62005-03-25 12:31:53 -060010071 destroy_workqueue(priv->workqueue);
10072 priv->workqueue = NULL;
Jeff Garzik0edd5b42005-09-07 00:48:31 -040010073 out_iounmap:
James Ketrenos43f66a62005-03-25 12:31:53 -060010074 iounmap(priv->hw_base);
Jeff Garzik0edd5b42005-09-07 00:48:31 -040010075 out_pci_release_regions:
James Ketrenos43f66a62005-03-25 12:31:53 -060010076 pci_release_regions(pdev);
Jeff Garzik0edd5b42005-09-07 00:48:31 -040010077 out_pci_disable_device:
James Ketrenos43f66a62005-03-25 12:31:53 -060010078 pci_disable_device(pdev);
10079 pci_set_drvdata(pdev, NULL);
Jeff Garzik0edd5b42005-09-07 00:48:31 -040010080 out_free_ieee80211:
James Ketrenos43f66a62005-03-25 12:31:53 -060010081 free_ieee80211(priv->net_dev);
Jeff Garzik0edd5b42005-09-07 00:48:31 -040010082 out:
James Ketrenos43f66a62005-03-25 12:31:53 -060010083 return err;
10084}
10085
10086static void ipw_pci_remove(struct pci_dev *pdev)
10087{
10088 struct ipw_priv *priv = pci_get_drvdata(pdev);
James Ketrenosb095c382005-08-24 22:04:42 -050010089
James Ketrenos43f66a62005-03-25 12:31:53 -060010090 if (!priv)
10091 return;
10092
James Ketrenosb095c382005-08-24 22:04:42 -050010093 down(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -060010094 ipw_down(priv);
James Ketrenosb095c382005-08-24 22:04:42 -050010095 sysfs_remove_group(&pdev->dev.kobj, &ipw_attribute_group);
10096 up(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -060010097
10098 unregister_netdev(priv->net_dev);
10099
10100 if (priv->rxq) {
10101 ipw_rx_queue_free(priv, priv->rxq);
10102 priv->rxq = NULL;
10103 }
10104 ipw_tx_queue_free(priv);
10105
10106 /* ipw_down will ensure that there is no more pending work
10107 * in the workqueue's, so we can safely remove them now. */
James Ketrenosa613bff2005-08-24 21:43:11 -050010108 cancel_delayed_work(&priv->adhoc_check);
10109 cancel_delayed_work(&priv->gather_stats);
10110 cancel_delayed_work(&priv->request_scan);
10111 cancel_delayed_work(&priv->rf_kill);
10112 cancel_delayed_work(&priv->scan_check);
10113 destroy_workqueue(priv->workqueue);
10114 priv->workqueue = NULL;
James Ketrenos43f66a62005-03-25 12:31:53 -060010115
10116 free_irq(pdev->irq, priv);
10117 iounmap(priv->hw_base);
10118 pci_release_regions(pdev);
10119 pci_disable_device(pdev);
10120 pci_set_drvdata(pdev, NULL);
10121 free_ieee80211(priv->net_dev);
10122
10123#ifdef CONFIG_PM
10124 if (fw_loaded) {
10125 release_firmware(bootfw);
10126 release_firmware(ucode);
10127 release_firmware(firmware);
10128 fw_loaded = 0;
10129 }
10130#endif
10131}
10132
James Ketrenos43f66a62005-03-25 12:31:53 -060010133#ifdef CONFIG_PM
Pavel Machek583a4e82005-09-03 15:56:58 -070010134static int ipw_pci_suspend(struct pci_dev *pdev, pm_message_t state)
James Ketrenos43f66a62005-03-25 12:31:53 -060010135{
10136 struct ipw_priv *priv = pci_get_drvdata(pdev);
10137 struct net_device *dev = priv->net_dev;
10138
10139 printk(KERN_INFO "%s: Going into suspend...\n", dev->name);
10140
Jeff Garzik0edd5b42005-09-07 00:48:31 -040010141 /* Take down the device; powers it off, etc. */
James Ketrenos43f66a62005-03-25 12:31:53 -060010142 ipw_down(priv);
10143
10144 /* Remove the PRESENT state of the device */
10145 netif_device_detach(dev);
10146
James Ketrenos43f66a62005-03-25 12:31:53 -060010147 pci_save_state(pdev);
James Ketrenos43f66a62005-03-25 12:31:53 -060010148 pci_disable_device(pdev);
Pavel Machek583a4e82005-09-03 15:56:58 -070010149 pci_set_power_state(pdev, pci_choose_state(pdev, state));
Jeff Garzikbf794512005-07-31 13:07:26 -040010150
James Ketrenos43f66a62005-03-25 12:31:53 -060010151 return 0;
10152}
10153
10154static int ipw_pci_resume(struct pci_dev *pdev)
10155{
10156 struct ipw_priv *priv = pci_get_drvdata(pdev);
10157 struct net_device *dev = priv->net_dev;
10158 u32 val;
Jeff Garzikbf794512005-07-31 13:07:26 -040010159
James Ketrenos43f66a62005-03-25 12:31:53 -060010160 printk(KERN_INFO "%s: Coming out of suspend...\n", dev->name);
10161
James Ketrenosea2b26e2005-08-24 21:25:16 -050010162 pci_set_power_state(pdev, PCI_D0);
James Ketrenos43f66a62005-03-25 12:31:53 -060010163 pci_enable_device(pdev);
James Ketrenos43f66a62005-03-25 12:31:53 -060010164 pci_restore_state(pdev);
James Ketrenosea2b26e2005-08-24 21:25:16 -050010165
James Ketrenos43f66a62005-03-25 12:31:53 -060010166 /*
10167 * Suspend/Resume resets the PCI configuration space, so we have to
10168 * re-disable the RETRY_TIMEOUT register (0x41) to keep PCI Tx retries
10169 * from interfering with C3 CPU state. pci_restore_state won't help
10170 * here since it only restores the first 64 bytes pci config header.
10171 */
Jeff Garzikbf794512005-07-31 13:07:26 -040010172 pci_read_config_dword(pdev, 0x40, &val);
10173 if ((val & 0x0000ff00) != 0)
James Ketrenos43f66a62005-03-25 12:31:53 -060010174 pci_write_config_dword(pdev, 0x40, val & 0xffff00ff);
10175
10176 /* Set the device back into the PRESENT state; this will also wake
10177 * the queue of needed */
10178 netif_device_attach(dev);
10179
10180 /* Bring the device back up */
10181 queue_work(priv->workqueue, &priv->up);
Jeff Garzikbf794512005-07-31 13:07:26 -040010182
James Ketrenos43f66a62005-03-25 12:31:53 -060010183 return 0;
10184}
10185#endif
10186
10187/* driver initialization stuff */
10188static struct pci_driver ipw_driver = {
10189 .name = DRV_NAME,
10190 .id_table = card_ids,
10191 .probe = ipw_pci_probe,
10192 .remove = __devexit_p(ipw_pci_remove),
10193#ifdef CONFIG_PM
10194 .suspend = ipw_pci_suspend,
10195 .resume = ipw_pci_resume,
10196#endif
10197};
10198
10199static int __init ipw_init(void)
10200{
10201 int ret;
10202
10203 printk(KERN_INFO DRV_NAME ": " DRV_DESCRIPTION ", " DRV_VERSION "\n");
10204 printk(KERN_INFO DRV_NAME ": " DRV_COPYRIGHT "\n");
10205
10206 ret = pci_module_init(&ipw_driver);
10207 if (ret) {
10208 IPW_ERROR("Unable to initialize PCI module\n");
10209 return ret;
10210 }
10211
Jeff Garzik0edd5b42005-09-07 00:48:31 -040010212 ret = driver_create_file(&ipw_driver.driver, &driver_attr_debug_level);
James Ketrenos43f66a62005-03-25 12:31:53 -060010213 if (ret) {
10214 IPW_ERROR("Unable to create driver sysfs file\n");
10215 pci_unregister_driver(&ipw_driver);
10216 return ret;
10217 }
10218
10219 return ret;
10220}
10221
10222static void __exit ipw_exit(void)
10223{
10224 driver_remove_file(&ipw_driver.driver, &driver_attr_debug_level);
10225 pci_unregister_driver(&ipw_driver);
10226}
10227
10228module_param(disable, int, 0444);
10229MODULE_PARM_DESC(disable, "manually disable the radio (default 0 [radio on])");
10230
10231module_param(associate, int, 0444);
10232MODULE_PARM_DESC(associate, "auto associate when scanning (default on)");
10233
10234module_param(auto_create, int, 0444);
10235MODULE_PARM_DESC(auto_create, "auto create adhoc network (default on)");
10236
James Ketrenosa613bff2005-08-24 21:43:11 -050010237module_param(led, int, 0444);
James Ketrenosc848d0a2005-08-24 21:56:24 -050010238MODULE_PARM_DESC(led, "enable led control on some systems (default 0 off)\n");
James Ketrenosa613bff2005-08-24 21:43:11 -050010239
James Ketrenos43f66a62005-03-25 12:31:53 -060010240module_param(debug, int, 0444);
10241MODULE_PARM_DESC(debug, "debug output mask");
10242
10243module_param(channel, int, 0444);
Jeff Garzikbf794512005-07-31 13:07:26 -040010244MODULE_PARM_DESC(channel, "channel to limit associate to (default 0 [ANY])");
James Ketrenos43f66a62005-03-25 12:31:53 -060010245
James Ketrenosb095c382005-08-24 22:04:42 -050010246#ifdef CONFIG_IPW_QOS
10247module_param(qos_enable, int, 0444);
10248MODULE_PARM_DESC(qos_enable, "enable all QoS functionalitis");
10249
10250module_param(qos_burst_enable, int, 0444);
10251MODULE_PARM_DESC(qos_burst_enable, "enable QoS burst mode");
10252
10253module_param(qos_no_ack_mask, int, 0444);
10254MODULE_PARM_DESC(qos_no_ack_mask, "mask Tx_Queue to no ack");
10255
10256module_param(burst_duration_CCK, int, 0444);
10257MODULE_PARM_DESC(burst_duration_CCK, "set CCK burst value");
10258
10259module_param(burst_duration_OFDM, int, 0444);
10260MODULE_PARM_DESC(burst_duration_OFDM, "set OFDM burst value");
10261#endif /* CONFIG_IPW_QOS */
10262
10263#ifdef CONFIG_IPW2200_MONITOR
James Ketrenos43f66a62005-03-25 12:31:53 -060010264module_param(mode, int, 0444);
10265MODULE_PARM_DESC(mode, "network mode (0=BSS,1=IBSS,2=Monitor)");
10266#else
10267module_param(mode, int, 0444);
10268MODULE_PARM_DESC(mode, "network mode (0=BSS,1=IBSS)");
10269#endif
10270
James Ketrenosb095c382005-08-24 22:04:42 -050010271module_param(hwcrypto, int, 0444);
10272MODULE_PARM_DESC(hwcrypto, "enable hardware crypto (default on)");
10273
James Ketrenos43f66a62005-03-25 12:31:53 -060010274module_exit(ipw_exit);
10275module_init(ipw_init);