blob: c5aad88e54d9a0ceddb1b6ef76748dc8376511bf [file] [log] [blame]
David Kilroy47445cb2009-02-04 23:05:48 +00001/* main.c - (formerly known as dldwd_cs.c, orinoco_cs.c and orinoco.c)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002 *
3 * A driver for Hermes or Prism 2 chipset based PCMCIA wireless
4 * adaptors, with Lucent/Agere, Intersil or Symbol firmware.
5 *
6 * Current maintainers (as of 29 September 2003) are:
7 * Pavel Roskin <proski AT gnu.org>
8 * and David Gibson <hermes AT gibson.dropbear.id.au>
9 *
10 * (C) Copyright David Gibson, IBM Corporation 2001-2003.
11 * Copyright (C) 2000 David Gibson, Linuxcare Australia.
12 * With some help from :
13 * Copyright (C) 2001 Jean Tourrilhes, HP Labs
14 * Copyright (C) 2001 Benjamin Herrenschmidt
15 *
16 * Based on dummy_cs.c 1.27 2000/06/12 21:27:25
17 *
18 * Portions based on wvlan_cs.c 1.0.6, Copyright Andreas Neuhaus <andy
19 * AT fasta.fh-dortmund.de>
20 * http://www.stud.fh-dortmund.de/~andy/wvlan/
21 *
22 * The contents of this file are subject to the Mozilla Public License
23 * Version 1.1 (the "License"); you may not use this file except in
24 * compliance with the License. You may obtain a copy of the License
25 * at http://www.mozilla.org/MPL/
26 *
27 * Software distributed under the License is distributed on an "AS IS"
28 * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
29 * the License for the specific language governing rights and
30 * limitations under the License.
31 *
32 * The initial developer of the original code is David A. Hinds
33 * <dahinds AT users.sourceforge.net>. Portions created by David
34 * A. Hinds are Copyright (C) 1999 David A. Hinds. All Rights
35 * Reserved.
36 *
37 * Alternatively, the contents of this file may be used under the
38 * terms of the GNU General Public License version 2 (the "GPL"), in
39 * which case the provisions of the GPL are applicable instead of the
40 * above. If you wish to allow the use of your version of this file
41 * only under the terms of the GPL and not to allow others to use your
42 * version of this file under the MPL, indicate your decision by
43 * deleting the provisions above and replace them with the notice and
44 * other provisions required by the GPL. If you do not delete the
45 * provisions above, a recipient may use your version of this file
46 * under either the MPL or the GPL. */
47
48/*
Linus Torvalds1da177e2005-04-16 15:20:36 -070049 * TODO
Linus Torvalds1da177e2005-04-16 15:20:36 -070050 * o Handle de-encapsulation within network layer, provide 802.11
51 * headers (patch from Thomas 'Dent' Mirlacher)
Linus Torvalds1da177e2005-04-16 15:20:36 -070052 * o Fix possible races in SPY handling.
53 * o Disconnect wireless extensions from fundamental configuration.
54 * o (maybe) Software WEP support (patch from Stano Meduna).
55 * o (maybe) Use multiple Tx buffers - driver handling queue
56 * rather than firmware.
57 */
58
59/* Locking and synchronization:
60 *
61 * The basic principle is that everything is serialized through a
62 * single spinlock, priv->lock. The lock is used in user, bh and irq
63 * context, so when taken outside hardirq context it should always be
64 * taken with interrupts disabled. The lock protects both the
65 * hardware and the struct orinoco_private.
66 *
67 * Another flag, priv->hw_unavailable indicates that the hardware is
68 * unavailable for an extended period of time (e.g. suspended, or in
69 * the middle of a hard reset). This flag is protected by the
70 * spinlock. All code which touches the hardware should check the
71 * flag after taking the lock, and if it is set, give up on whatever
72 * they are doing and drop the lock again. The orinoco_lock()
73 * function handles this (it unlocks and returns -EBUSY if
74 * hw_unavailable is non-zero).
75 */
76
77#define DRIVER_NAME "orinoco"
78
Linus Torvalds1da177e2005-04-16 15:20:36 -070079#include <linux/module.h>
80#include <linux/kernel.h>
81#include <linux/init.h>
David Kilroyd03032a2008-08-21 23:28:02 +010082#include <linux/delay.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070083#include <linux/netdevice.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070084#include <linux/etherdevice.h>
Christoph Hellwig1fab2e82005-06-19 01:27:40 +020085#include <linux/ethtool.h>
David Kilroy39d1ffe2008-11-22 10:37:28 +000086#include <linux/suspend.h>
Pavel Roskin9c974fb2006-08-15 20:45:03 -040087#include <linux/if_arp.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070088#include <linux/wireless.h>
Johannes Berg2c7060022008-10-30 22:09:54 +010089#include <linux/ieee80211.h>
Christoph Hellwig620554e2005-06-19 01:27:33 +020090#include <net/iw_handler.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070091
Linus Torvalds1da177e2005-04-16 15:20:36 -070092#include "hermes_rid.h"
David Kilroy3994d502008-08-21 23:27:54 +010093#include "hermes_dld.h"
David Kilroyfb791b12009-02-04 23:05:50 +000094#include "scan.h"
David Kilroy4adb4742009-02-04 23:05:51 +000095#include "mic.h"
David Kilroy37a2e562009-02-04 23:05:52 +000096#include "fw.h"
David Kilroyfb791b12009-02-04 23:05:50 +000097
Linus Torvalds1da177e2005-04-16 15:20:36 -070098#include "orinoco.h"
Linus Torvalds1da177e2005-04-16 15:20:36 -070099
100/********************************************************************/
101/* Module information */
102/********************************************************************/
103
David Kilroyb2f30a02009-02-04 23:05:46 +0000104MODULE_AUTHOR("Pavel Roskin <proski@gnu.org> & "
105 "David Gibson <hermes@gibson.dropbear.id.au>");
106MODULE_DESCRIPTION("Driver for Lucent Orinoco, Prism II based "
107 "and similar wireless cards");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700108MODULE_LICENSE("Dual MPL/GPL");
109
110/* Level of debugging. Used in the macros in orinoco.h */
111#ifdef ORINOCO_DEBUG
112int orinoco_debug = ORINOCO_DEBUG;
David Kilroy21312662009-02-04 23:05:47 +0000113EXPORT_SYMBOL(orinoco_debug);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700114module_param(orinoco_debug, int, 0644);
115MODULE_PARM_DESC(orinoco_debug, "Debug level");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700116#endif
117
118static int suppress_linkstatus; /* = 0 */
119module_param(suppress_linkstatus, bool, 0644);
120MODULE_PARM_DESC(suppress_linkstatus, "Don't log link status changes");
David Kilroyb2f30a02009-02-04 23:05:46 +0000121
David Gibson7bb7c3a2005-05-12 20:02:10 -0400122static int ignore_disconnect; /* = 0 */
123module_param(ignore_disconnect, int, 0644);
David Kilroyb2f30a02009-02-04 23:05:46 +0000124MODULE_PARM_DESC(ignore_disconnect,
125 "Don't report lost link to the network layer");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700126
Christoph Hellwig98c4cae2005-06-19 01:28:06 +0200127static int force_monitor; /* = 0 */
128module_param(force_monitor, int, 0644);
129MODULE_PARM_DESC(force_monitor, "Allow monitor mode for all firmware versions");
130
Linus Torvalds1da177e2005-04-16 15:20:36 -0700131/********************************************************************/
132/* Compile time configuration and compatibility stuff */
133/********************************************************************/
134
135/* We do this this way to avoid ifdefs in the actual code */
136#ifdef WIRELESS_SPY
Pavel Roskin343c6862005-09-09 18:43:02 -0400137#define SPY_NUMBER(priv) (priv->spy_data.spy_number)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700138#else
139#define SPY_NUMBER(priv) 0
140#endif /* WIRELESS_SPY */
141
142/********************************************************************/
143/* Internal constants */
144/********************************************************************/
145
Christoph Hellwig95dd91f2005-06-19 01:27:56 +0200146/* 802.2 LLC/SNAP header used for Ethernet encapsulation over 802.11 */
147static const u8 encaps_hdr[] = {0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00};
148#define ENCAPS_OVERHEAD (sizeof(encaps_hdr) + 2)
149
Linus Torvalds1da177e2005-04-16 15:20:36 -0700150#define ORINOCO_MIN_MTU 256
Johannes Berg2c7060022008-10-30 22:09:54 +0100151#define ORINOCO_MAX_MTU (IEEE80211_MAX_DATA_LEN - ENCAPS_OVERHEAD)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700152
153#define SYMBOL_MAX_VER_LEN (14)
154#define USER_BAP 0
155#define IRQ_BAP 1
156#define MAX_IRQLOOPS_PER_IRQ 10
157#define MAX_IRQLOOPS_PER_JIFFY (20000/HZ) /* Based on a guestimate of
158 * how many events the
159 * device could
160 * legitimately generate */
161#define SMALL_KEY_SIZE 5
162#define LARGE_KEY_SIZE 13
163#define TX_NICBUF_SIZE_BUG 1585 /* Bug in Symbol firmware */
164
165#define DUMMY_FID 0xFFFF
166
167/*#define MAX_MULTICAST(priv) (priv->firmware_type == FIRMWARE_TYPE_AGERE ? \
168 HERMES_MAX_MULTICAST : 0)*/
169#define MAX_MULTICAST(priv) (HERMES_MAX_MULTICAST)
170
171#define ORINOCO_INTEN (HERMES_EV_RX | HERMES_EV_ALLOC \
172 | HERMES_EV_TX | HERMES_EV_TXEXC \
173 | HERMES_EV_WTERR | HERMES_EV_INFO \
David Kilroya94e8422009-02-04 23:05:44 +0000174 | HERMES_EV_INFDROP)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700175
Christoph Hellwig620554e2005-06-19 01:27:33 +0200176#define MAX_RID_LEN 1024
177
178static const struct iw_handler_def orinoco_handler_def;
Jeff Garzik7282d492006-09-13 14:30:00 -0400179static const struct ethtool_ops orinoco_ethtool_ops;
Christoph Hellwig620554e2005-06-19 01:27:33 +0200180
Linus Torvalds1da177e2005-04-16 15:20:36 -0700181/********************************************************************/
182/* Data tables */
183/********************************************************************/
184
David Kilroy9ee677c2008-12-23 14:03:38 +0000185#define NUM_CHANNELS 14
Linus Torvalds1da177e2005-04-16 15:20:36 -0700186
187/* This tables gives the actual meanings of the bitrate IDs returned
188 * by the firmware. */
189static struct {
190 int bitrate; /* in 100s of kilobits */
191 int automatic;
192 u16 agere_txratectrl;
193 u16 intersil_txratectrl;
194} bitrate_table[] = {
195 {110, 1, 3, 15}, /* Entry 0 is the default */
196 {10, 0, 1, 1},
197 {10, 1, 1, 1},
198 {20, 0, 2, 2},
199 {20, 1, 6, 3},
200 {55, 0, 4, 4},
201 {55, 1, 7, 7},
202 {110, 0, 5, 8},
203};
204#define BITRATE_TABLE_SIZE ARRAY_SIZE(bitrate_table)
205
206/********************************************************************/
207/* Data types */
208/********************************************************************/
209
Pavel Roskin30c2d3b2006-04-07 04:10:34 -0400210/* Beginning of the Tx descriptor, used in TxExc handling */
211struct hermes_txexc_data {
212 struct hermes_tx_descriptor desc;
Pavel Roskind133ae42005-09-23 04:18:06 -0400213 __le16 frame_ctl;
214 __le16 duration_id;
Christoph Hellwig95dd91f2005-06-19 01:27:56 +0200215 u8 addr1[ETH_ALEN];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700216} __attribute__ ((packed));
217
Christoph Hellwig8f2abf42005-06-19 01:28:02 +0200218/* Rx frame header except compatibility 802.3 header */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700219struct hermes_rx_descriptor {
Christoph Hellwig8f2abf42005-06-19 01:28:02 +0200220 /* Control */
Pavel Roskind133ae42005-09-23 04:18:06 -0400221 __le16 status;
222 __le32 time;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700223 u8 silence;
224 u8 signal;
225 u8 rate;
226 u8 rxflow;
Pavel Roskind133ae42005-09-23 04:18:06 -0400227 __le32 reserved;
Christoph Hellwig8f2abf42005-06-19 01:28:02 +0200228
229 /* 802.11 header */
Pavel Roskind133ae42005-09-23 04:18:06 -0400230 __le16 frame_ctl;
231 __le16 duration_id;
Christoph Hellwig8f2abf42005-06-19 01:28:02 +0200232 u8 addr1[ETH_ALEN];
233 u8 addr2[ETH_ALEN];
234 u8 addr3[ETH_ALEN];
Pavel Roskind133ae42005-09-23 04:18:06 -0400235 __le16 seq_ctl;
Christoph Hellwig8f2abf42005-06-19 01:28:02 +0200236 u8 addr4[ETH_ALEN];
237
238 /* Data length */
Pavel Roskind133ae42005-09-23 04:18:06 -0400239 __le16 data_len;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700240} __attribute__ ((packed));
241
David Kilroy47166792009-01-07 00:43:54 +0000242struct orinoco_rx_data {
243 struct hermes_rx_descriptor *desc;
244 struct sk_buff *skb;
245 struct list_head list;
246};
247
Linus Torvalds1da177e2005-04-16 15:20:36 -0700248/********************************************************************/
249/* Function prototypes */
250/********************************************************************/
251
Linus Torvalds1da177e2005-04-16 15:20:36 -0700252static int __orinoco_program_rids(struct net_device *dev);
253static void __orinoco_set_multicast_list(struct net_device *dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700254
255/********************************************************************/
256/* Internal helper functions */
257/********************************************************************/
258
259static inline void set_port_type(struct orinoco_private *priv)
260{
261 switch (priv->iw_mode) {
262 case IW_MODE_INFRA:
263 priv->port_type = 1;
264 priv->createibss = 0;
265 break;
266 case IW_MODE_ADHOC:
267 if (priv->prefer_port3) {
268 priv->port_type = 3;
269 priv->createibss = 0;
270 } else {
271 priv->port_type = priv->ibss_port;
272 priv->createibss = 1;
273 }
274 break;
Christoph Hellwig98c4cae2005-06-19 01:28:06 +0200275 case IW_MODE_MONITOR:
276 priv->port_type = 3;
277 priv->createibss = 0;
278 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700279 default:
280 printk(KERN_ERR "%s: Invalid priv->iw_mode in set_port_type()\n",
281 priv->ndev->name);
282 }
283}
284
David Kilroycfeb1db2009-02-04 23:05:53 +0000285static int orinoco_get_bitratemode(int bitrate, int automatic)
286{
287 int ratemode = -1;
288 int i;
289
290 if ((bitrate != 10) && (bitrate != 20) &&
291 (bitrate != 55) && (bitrate != 110))
292 return ratemode;
293
294 for (i = 0; i < BITRATE_TABLE_SIZE; i++) {
295 if ((bitrate_table[i].bitrate == bitrate) &&
296 (bitrate_table[i].automatic == automatic)) {
297 ratemode = i;
298 break;
299 }
300 }
301 return ratemode;
302}
303
304static void orinoco_get_ratemode_cfg(int ratemode, int *bitrate, int *automatic)
305{
306 BUG_ON((ratemode < 0) || (ratemode >= BITRATE_TABLE_SIZE));
307
308 *bitrate = bitrate_table[ratemode].bitrate * 100000;
309 *automatic = bitrate_table[ratemode].automatic;
310}
311
David Kilroy01632fa2008-08-21 23:27:58 +0100312static inline u8 *orinoco_get_ie(u8 *data, size_t len,
Johannes Berg2c7060022008-10-30 22:09:54 +0100313 enum ieee80211_eid eid)
David Kilroy01632fa2008-08-21 23:27:58 +0100314{
315 u8 *p = data;
316 while ((p + 2) < (data + len)) {
317 if (p[0] == eid)
318 return p;
319 p += p[1] + 2;
320 }
321 return NULL;
322}
323
324#define WPA_OUI_TYPE "\x00\x50\xF2\x01"
325#define WPA_SELECTOR_LEN 4
326static inline u8 *orinoco_get_wpa_ie(u8 *data, size_t len)
327{
328 u8 *p = data;
329 while ((p + 2 + WPA_SELECTOR_LEN) < (data + len)) {
Johannes Berg2c7060022008-10-30 22:09:54 +0100330 if ((p[0] == WLAN_EID_GENERIC) &&
David Kilroy01632fa2008-08-21 23:27:58 +0100331 (memcmp(&p[2], WPA_OUI_TYPE, WPA_SELECTOR_LEN) == 0))
332 return p;
333 p += p[1] + 2;
334 }
335 return NULL;
Dan Williams1e3428e2007-10-10 23:56:25 -0400336}
337
David Kilroy3994d502008-08-21 23:27:54 +0100338
339/********************************************************************/
Linus Torvalds1da177e2005-04-16 15:20:36 -0700340/* Device methods */
341/********************************************************************/
342
343static int orinoco_open(struct net_device *dev)
344{
345 struct orinoco_private *priv = netdev_priv(dev);
346 unsigned long flags;
347 int err;
348
349 if (orinoco_lock(priv, &flags) != 0)
350 return -EBUSY;
351
352 err = __orinoco_up(dev);
353
David Kilroya94e8422009-02-04 23:05:44 +0000354 if (!err)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700355 priv->open = 1;
356
357 orinoco_unlock(priv, &flags);
358
359 return err;
360}
361
Christoph Hellwigad8f4512005-05-14 17:30:17 +0200362static int orinoco_stop(struct net_device *dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700363{
364 struct orinoco_private *priv = netdev_priv(dev);
365 int err = 0;
366
367 /* We mustn't use orinoco_lock() here, because we need to be
368 able to close the interface even if hw_unavailable is set
369 (e.g. as we're released after a PC Card removal) */
370 spin_lock_irq(&priv->lock);
371
372 priv->open = 0;
373
374 err = __orinoco_down(dev);
375
376 spin_unlock_irq(&priv->lock);
377
378 return err;
379}
380
381static struct net_device_stats *orinoco_get_stats(struct net_device *dev)
382{
383 struct orinoco_private *priv = netdev_priv(dev);
David Kilroy6fe9deb2009-02-04 23:05:43 +0000384
Linus Torvalds1da177e2005-04-16 15:20:36 -0700385 return &priv->stats;
386}
387
388static struct iw_statistics *orinoco_get_wireless_stats(struct net_device *dev)
389{
390 struct orinoco_private *priv = netdev_priv(dev);
391 hermes_t *hw = &priv->hw;
392 struct iw_statistics *wstats = &priv->wstats;
David Gibsone67d9d92005-05-12 20:01:22 -0400393 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700394 unsigned long flags;
395
David Kilroya94e8422009-02-04 23:05:44 +0000396 if (!netif_device_present(dev)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700397 printk(KERN_WARNING "%s: get_wireless_stats() called while device not present\n",
398 dev->name);
399 return NULL; /* FIXME: Can we do better than this? */
400 }
401
David Gibsone67d9d92005-05-12 20:01:22 -0400402 /* If busy, return the old stats. Returning NULL may cause
403 * the interface to disappear from /proc/net/wireless */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700404 if (orinoco_lock(priv, &flags) != 0)
David Gibsone67d9d92005-05-12 20:01:22 -0400405 return wstats;
406
407 /* We can't really wait for the tallies inquiry command to
408 * complete, so we just use the previous results and trigger
409 * a new tallies inquiry command for next time - Jean II */
410 /* FIXME: Really we should wait for the inquiry to come back -
411 * as it is the stats we give don't make a whole lot of sense.
412 * Unfortunately, it's not clear how to do that within the
413 * wireless extensions framework: I think we're in user
414 * context, but a lock seems to be held by the time we get in
415 * here so we're not safe to sleep here. */
416 hermes_inquire(hw, HERMES_INQ_TALLIES);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700417
418 if (priv->iw_mode == IW_MODE_ADHOC) {
419 memset(&wstats->qual, 0, sizeof(wstats->qual));
420 /* If a spy address is defined, we report stats of the
421 * first spy address - Jean II */
422 if (SPY_NUMBER(priv)) {
Pavel Roskin343c6862005-09-09 18:43:02 -0400423 wstats->qual.qual = priv->spy_data.spy_stat[0].qual;
424 wstats->qual.level = priv->spy_data.spy_stat[0].level;
425 wstats->qual.noise = priv->spy_data.spy_stat[0].noise;
David Kilroyb2f30a02009-02-04 23:05:46 +0000426 wstats->qual.updated =
427 priv->spy_data.spy_stat[0].updated;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700428 }
429 } else {
430 struct {
Pavel Roskina208c4e2006-04-07 04:10:26 -0400431 __le16 qual, signal, noise, unused;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700432 } __attribute__ ((packed)) cq;
433
434 err = HERMES_READ_RECORD(hw, USER_BAP,
435 HERMES_RID_COMMSQUALITY, &cq);
David Gibsone67d9d92005-05-12 20:01:22 -0400436
437 if (!err) {
438 wstats->qual.qual = (int)le16_to_cpu(cq.qual);
439 wstats->qual.level = (int)le16_to_cpu(cq.signal) - 0x95;
440 wstats->qual.noise = (int)le16_to_cpu(cq.noise) - 0x95;
David Kilroyb2f30a02009-02-04 23:05:46 +0000441 wstats->qual.updated =
442 IW_QUAL_ALL_UPDATED | IW_QUAL_DBM;
David Gibsone67d9d92005-05-12 20:01:22 -0400443 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700444 }
445
Linus Torvalds1da177e2005-04-16 15:20:36 -0700446 orinoco_unlock(priv, &flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700447 return wstats;
448}
449
450static void orinoco_set_multicast_list(struct net_device *dev)
451{
452 struct orinoco_private *priv = netdev_priv(dev);
453 unsigned long flags;
454
455 if (orinoco_lock(priv, &flags) != 0) {
456 printk(KERN_DEBUG "%s: orinoco_set_multicast_list() "
457 "called when hw_unavailable\n", dev->name);
458 return;
459 }
460
461 __orinoco_set_multicast_list(dev);
462 orinoco_unlock(priv, &flags);
463}
464
465static int orinoco_change_mtu(struct net_device *dev, int new_mtu)
466{
467 struct orinoco_private *priv = netdev_priv(dev);
468
David Kilroya94e8422009-02-04 23:05:44 +0000469 if ((new_mtu < ORINOCO_MIN_MTU) || (new_mtu > ORINOCO_MAX_MTU))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700470 return -EINVAL;
471
Johannes Berg2c7060022008-10-30 22:09:54 +0100472 /* MTU + encapsulation + header length */
David Kilroya94e8422009-02-04 23:05:44 +0000473 if ((new_mtu + ENCAPS_OVERHEAD + sizeof(struct ieee80211_hdr)) >
474 (priv->nicbuf_size - ETH_HLEN))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700475 return -EINVAL;
476
477 dev->mtu = new_mtu;
478
479 return 0;
480}
481
482/********************************************************************/
483/* Tx path */
484/********************************************************************/
485
486static int orinoco_xmit(struct sk_buff *skb, struct net_device *dev)
487{
488 struct orinoco_private *priv = netdev_priv(dev);
489 struct net_device_stats *stats = &priv->stats;
490 hermes_t *hw = &priv->hw;
491 int err = 0;
492 u16 txfid = priv->txfid;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700493 struct ethhdr *eh;
David Kilroy6eecad72008-08-21 23:27:56 +0100494 int tx_control;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700495 unsigned long flags;
496
David Kilroya94e8422009-02-04 23:05:44 +0000497 if (!netif_running(dev)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700498 printk(KERN_ERR "%s: Tx on stopped device!\n",
499 dev->name);
Pavel Roskinb34b8672006-04-07 04:10:36 -0400500 return NETDEV_TX_BUSY;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700501 }
David Kilroy6fe9deb2009-02-04 23:05:43 +0000502
Linus Torvalds1da177e2005-04-16 15:20:36 -0700503 if (netif_queue_stopped(dev)) {
David Kilroy6fe9deb2009-02-04 23:05:43 +0000504 printk(KERN_DEBUG "%s: Tx while transmitter busy!\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700505 dev->name);
Pavel Roskinb34b8672006-04-07 04:10:36 -0400506 return NETDEV_TX_BUSY;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700507 }
David Kilroy6fe9deb2009-02-04 23:05:43 +0000508
Linus Torvalds1da177e2005-04-16 15:20:36 -0700509 if (orinoco_lock(priv, &flags) != 0) {
510 printk(KERN_ERR "%s: orinoco_xmit() called while hw_unavailable\n",
511 dev->name);
Pavel Roskinb34b8672006-04-07 04:10:36 -0400512 return NETDEV_TX_BUSY;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700513 }
514
David Kilroya94e8422009-02-04 23:05:44 +0000515 if (!netif_carrier_ok(dev) || (priv->iw_mode == IW_MODE_MONITOR)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700516 /* Oops, the firmware hasn't established a connection,
David Kilroy6fe9deb2009-02-04 23:05:43 +0000517 silently drop the packet (this seems to be the
518 safest approach). */
Pavel Roskin470e2aa2006-04-07 04:10:43 -0400519 goto drop;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700520 }
521
Pavel Roskin8d5be082006-04-07 04:10:41 -0400522 /* Check packet length */
Pavel Roskina28dc812006-04-07 04:10:45 -0400523 if (skb->len < ETH_HLEN)
Pavel Roskin470e2aa2006-04-07 04:10:43 -0400524 goto drop;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700525
David Kilroy6eecad72008-08-21 23:27:56 +0100526 tx_control = HERMES_TXCTRL_TX_OK | HERMES_TXCTRL_TX_EX;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700527
David Kilroy23edcc42008-08-21 23:28:05 +0100528 if (priv->encode_alg == IW_ENCODE_ALG_TKIP)
529 tx_control |= (priv->tx_key << HERMES_MIC_KEY_ID_SHIFT) |
530 HERMES_TXCTRL_MIC;
531
David Kilroy6eecad72008-08-21 23:27:56 +0100532 if (priv->has_alt_txcntl) {
533 /* WPA enabled firmwares have tx_cntl at the end of
534 * the 802.11 header. So write zeroed descriptor and
535 * 802.11 header at the same time
536 */
537 char desc[HERMES_802_3_OFFSET];
538 __le16 *txcntl = (__le16 *) &desc[HERMES_TXCNTL2_OFFSET];
539
540 memset(&desc, 0, sizeof(desc));
541
542 *txcntl = cpu_to_le16(tx_control);
543 err = hermes_bap_pwrite(hw, USER_BAP, &desc, sizeof(desc),
544 txfid, 0);
545 if (err) {
546 if (net_ratelimit())
547 printk(KERN_ERR "%s: Error %d writing Tx "
548 "descriptor to BAP\n", dev->name, err);
549 goto busy;
550 }
551 } else {
552 struct hermes_tx_descriptor desc;
553
554 memset(&desc, 0, sizeof(desc));
555
556 desc.tx_control = cpu_to_le16(tx_control);
557 err = hermes_bap_pwrite(hw, USER_BAP, &desc, sizeof(desc),
558 txfid, 0);
559 if (err) {
560 if (net_ratelimit())
561 printk(KERN_ERR "%s: Error %d writing Tx "
562 "descriptor to BAP\n", dev->name, err);
563 goto busy;
564 }
565
566 /* Clear the 802.11 header and data length fields - some
567 * firmwares (e.g. Lucent/Agere 8.xx) appear to get confused
568 * if this isn't done. */
569 hermes_clear_words(hw, HERMES_DATA0,
570 HERMES_802_3_OFFSET - HERMES_802_11_OFFSET);
571 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700572
David Kilroy23edcc42008-08-21 23:28:05 +0100573 eh = (struct ethhdr *)skb->data;
574
Linus Torvalds1da177e2005-04-16 15:20:36 -0700575 /* Encapsulate Ethernet-II frames */
576 if (ntohs(eh->h_proto) > ETH_DATA_LEN) { /* Ethernet-II frame */
Pavel Roskina28dc812006-04-07 04:10:45 -0400577 struct header_struct {
578 struct ethhdr eth; /* 802.3 header */
579 u8 encap[6]; /* 802.2 header */
580 } __attribute__ ((packed)) hdr;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700581
Pavel Roskina28dc812006-04-07 04:10:45 -0400582 /* Strip destination and source from the data */
583 skb_pull(skb, 2 * ETH_ALEN);
Pavel Roskina28dc812006-04-07 04:10:45 -0400584
585 /* And move them to a separate header */
586 memcpy(&hdr.eth, eh, 2 * ETH_ALEN);
587 hdr.eth.h_proto = htons(sizeof(encaps_hdr) + skb->len);
588 memcpy(hdr.encap, encaps_hdr, sizeof(encaps_hdr));
589
David Kilroy23edcc42008-08-21 23:28:05 +0100590 /* Insert the SNAP header */
591 if (skb_headroom(skb) < sizeof(hdr)) {
592 printk(KERN_ERR
593 "%s: Not enough headroom for 802.2 headers %d\n",
594 dev->name, skb_headroom(skb));
595 goto drop;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700596 }
David Kilroy23edcc42008-08-21 23:28:05 +0100597 eh = (struct ethhdr *) skb_push(skb, sizeof(hdr));
598 memcpy(eh, &hdr, sizeof(hdr));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700599 }
600
Pavel Roskina28dc812006-04-07 04:10:45 -0400601 err = hermes_bap_pwrite(hw, USER_BAP, skb->data, skb->len,
David Kilroy23edcc42008-08-21 23:28:05 +0100602 txfid, HERMES_802_3_OFFSET);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700603 if (err) {
604 printk(KERN_ERR "%s: Error %d writing packet to BAP\n",
605 dev->name, err);
Pavel Roskin470e2aa2006-04-07 04:10:43 -0400606 goto busy;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700607 }
608
David Kilroy23edcc42008-08-21 23:28:05 +0100609 /* Calculate Michael MIC */
610 if (priv->encode_alg == IW_ENCODE_ALG_TKIP) {
611 u8 mic_buf[MICHAEL_MIC_LEN + 1];
612 u8 *mic;
613 size_t offset;
614 size_t len;
615
616 if (skb->len % 2) {
617 /* MIC start is on an odd boundary */
618 mic_buf[0] = skb->data[skb->len - 1];
619 mic = &mic_buf[1];
620 offset = skb->len - 1;
621 len = MICHAEL_MIC_LEN + 1;
622 } else {
623 mic = &mic_buf[0];
624 offset = skb->len;
625 len = MICHAEL_MIC_LEN;
626 }
627
David Kilroy4adb4742009-02-04 23:05:51 +0000628 orinoco_mic(priv->tx_tfm_mic,
David Kilroy23edcc42008-08-21 23:28:05 +0100629 priv->tkip_key[priv->tx_key].tx_mic,
630 eh->h_dest, eh->h_source, 0 /* priority */,
631 skb->data + ETH_HLEN, skb->len - ETH_HLEN, mic);
632
633 /* Write the MIC */
634 err = hermes_bap_pwrite(hw, USER_BAP, &mic_buf[0], len,
635 txfid, HERMES_802_3_OFFSET + offset);
636 if (err) {
637 printk(KERN_ERR "%s: Error %d writing MIC to BAP\n",
638 dev->name, err);
639 goto busy;
640 }
641 }
642
Linus Torvalds1da177e2005-04-16 15:20:36 -0700643 /* Finally, we actually initiate the send */
644 netif_stop_queue(dev);
645
646 err = hermes_docmd_wait(hw, HERMES_CMD_TX | HERMES_CMD_RECL,
647 txfid, NULL);
648 if (err) {
649 netif_start_queue(dev);
Andrew Mortonc367c212005-10-19 21:23:44 -0700650 if (net_ratelimit())
651 printk(KERN_ERR "%s: Error %d transmitting packet\n",
652 dev->name, err);
Pavel Roskin470e2aa2006-04-07 04:10:43 -0400653 goto busy;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700654 }
655
656 dev->trans_start = jiffies;
David Kilroy23edcc42008-08-21 23:28:05 +0100657 stats->tx_bytes += HERMES_802_3_OFFSET + skb->len;
Pavel Roskin470e2aa2006-04-07 04:10:43 -0400658 goto ok;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700659
Pavel Roskin470e2aa2006-04-07 04:10:43 -0400660 drop:
661 stats->tx_errors++;
662 stats->tx_dropped++;
663
664 ok:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700665 orinoco_unlock(priv, &flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700666 dev_kfree_skb(skb);
Pavel Roskinb34b8672006-04-07 04:10:36 -0400667 return NETDEV_TX_OK;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700668
Pavel Roskin470e2aa2006-04-07 04:10:43 -0400669 busy:
Jiri Benc2c1bd262006-04-07 04:10:47 -0400670 if (err == -EIO)
671 schedule_work(&priv->reset_work);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700672 orinoco_unlock(priv, &flags);
Pavel Roskinb34b8672006-04-07 04:10:36 -0400673 return NETDEV_TX_BUSY;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700674}
675
676static void __orinoco_ev_alloc(struct net_device *dev, hermes_t *hw)
677{
678 struct orinoco_private *priv = netdev_priv(dev);
679 u16 fid = hermes_read_regn(hw, ALLOCFID);
680
681 if (fid != priv->txfid) {
682 if (fid != DUMMY_FID)
683 printk(KERN_WARNING "%s: Allocate event on unexpected fid (%04X)\n",
684 dev->name, fid);
685 return;
686 }
687
688 hermes_write_regn(hw, ALLOCFID, DUMMY_FID);
689}
690
691static void __orinoco_ev_tx(struct net_device *dev, hermes_t *hw)
692{
693 struct orinoco_private *priv = netdev_priv(dev);
694 struct net_device_stats *stats = &priv->stats;
695
696 stats->tx_packets++;
697
698 netif_wake_queue(dev);
699
700 hermes_write_regn(hw, TXCOMPLFID, DUMMY_FID);
701}
702
703static void __orinoco_ev_txexc(struct net_device *dev, hermes_t *hw)
704{
705 struct orinoco_private *priv = netdev_priv(dev);
706 struct net_device_stats *stats = &priv->stats;
707 u16 fid = hermes_read_regn(hw, TXCOMPLFID);
Pavel Roskind133ae42005-09-23 04:18:06 -0400708 u16 status;
Pavel Roskin30c2d3b2006-04-07 04:10:34 -0400709 struct hermes_txexc_data hdr;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700710 int err = 0;
711
712 if (fid == DUMMY_FID)
713 return; /* Nothing's really happened */
714
Pavel Roskin48ca7032005-09-23 04:18:06 -0400715 /* Read part of the frame header - we need status and addr1 */
Christoph Hellwig95dd91f2005-06-19 01:27:56 +0200716 err = hermes_bap_pread(hw, IRQ_BAP, &hdr,
Pavel Roskin30c2d3b2006-04-07 04:10:34 -0400717 sizeof(struct hermes_txexc_data),
Christoph Hellwig95dd91f2005-06-19 01:27:56 +0200718 fid, 0);
719
720 hermes_write_regn(hw, TXCOMPLFID, DUMMY_FID);
721 stats->tx_errors++;
722
Linus Torvalds1da177e2005-04-16 15:20:36 -0700723 if (err) {
724 printk(KERN_WARNING "%s: Unable to read descriptor on Tx error "
725 "(FID=%04X error %d)\n",
726 dev->name, fid, err);
Christoph Hellwig95dd91f2005-06-19 01:27:56 +0200727 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700728 }
David Kilroy6fe9deb2009-02-04 23:05:43 +0000729
Christoph Hellwig95dd91f2005-06-19 01:27:56 +0200730 DEBUG(1, "%s: Tx error, err %d (FID=%04X)\n", dev->name,
731 err, fid);
David Kilroy6fe9deb2009-02-04 23:05:43 +0000732
Christoph Hellwig95dd91f2005-06-19 01:27:56 +0200733 /* We produce a TXDROP event only for retry or lifetime
734 * exceeded, because that's the only status that really mean
735 * that this particular node went away.
736 * Other errors means that *we* screwed up. - Jean II */
Pavel Roskin30c2d3b2006-04-07 04:10:34 -0400737 status = le16_to_cpu(hdr.desc.status);
Pavel Roskind133ae42005-09-23 04:18:06 -0400738 if (status & (HERMES_TXSTAT_RETRYERR | HERMES_TXSTAT_AGEDERR)) {
Christoph Hellwig95dd91f2005-06-19 01:27:56 +0200739 union iwreq_data wrqu;
740
741 /* Copy 802.11 dest address.
742 * We use the 802.11 header because the frame may
743 * not be 802.3 or may be mangled...
744 * In Ad-Hoc mode, it will be the node address.
745 * In managed mode, it will be most likely the AP addr
746 * User space will figure out how to convert it to
747 * whatever it needs (IP address or else).
748 * - Jean II */
749 memcpy(wrqu.addr.sa_data, hdr.addr1, ETH_ALEN);
750 wrqu.addr.sa_family = ARPHRD_ETHER;
751
752 /* Send event to user space */
753 wireless_send_event(dev, IWEVTXDROP, &wrqu, NULL);
754 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700755
756 netif_wake_queue(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700757}
758
759static void orinoco_tx_timeout(struct net_device *dev)
760{
761 struct orinoco_private *priv = netdev_priv(dev);
762 struct net_device_stats *stats = &priv->stats;
763 struct hermes *hw = &priv->hw;
764
765 printk(KERN_WARNING "%s: Tx timeout! "
766 "ALLOCFID=%04x, TXCOMPLFID=%04x, EVSTAT=%04x\n",
767 dev->name, hermes_read_regn(hw, ALLOCFID),
768 hermes_read_regn(hw, TXCOMPLFID), hermes_read_regn(hw, EVSTAT));
769
770 stats->tx_errors++;
771
772 schedule_work(&priv->reset_work);
773}
774
775/********************************************************************/
776/* Rx path (data frames) */
777/********************************************************************/
778
779/* Does the frame have a SNAP header indicating it should be
780 * de-encapsulated to Ethernet-II? */
781static inline int is_ethersnap(void *_hdr)
782{
783 u8 *hdr = _hdr;
784
785 /* We de-encapsulate all packets which, a) have SNAP headers
786 * (i.e. SSAP=DSAP=0xaa and CTRL=0x3 in the 802.2 LLC header
787 * and where b) the OUI of the SNAP header is 00:00:00 or
788 * 00:00:f8 - we need both because different APs appear to use
789 * different OUIs for some reason */
790 return (memcmp(hdr, &encaps_hdr, 5) == 0)
David Kilroya94e8422009-02-04 23:05:44 +0000791 && ((hdr[5] == 0x00) || (hdr[5] == 0xf8));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700792}
793
794static inline void orinoco_spy_gather(struct net_device *dev, u_char *mac,
795 int level, int noise)
796{
Pavel Roskin343c6862005-09-09 18:43:02 -0400797 struct iw_quality wstats;
798 wstats.level = level - 0x95;
799 wstats.noise = noise - 0x95;
800 wstats.qual = (level > noise) ? (level - noise) : 0;
Andrey Borzenkovf941f852008-11-15 17:15:09 +0300801 wstats.updated = IW_QUAL_ALL_UPDATED | IW_QUAL_DBM;
Pavel Roskin343c6862005-09-09 18:43:02 -0400802 /* Update spy records */
803 wireless_spy_update(dev, mac, &wstats);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700804}
805
806static void orinoco_stat_gather(struct net_device *dev,
807 struct sk_buff *skb,
808 struct hermes_rx_descriptor *desc)
809{
810 struct orinoco_private *priv = netdev_priv(dev);
811
812 /* Using spy support with lots of Rx packets, like in an
813 * infrastructure (AP), will really slow down everything, because
814 * the MAC address must be compared to each entry of the spy list.
815 * If the user really asks for it (set some address in the
816 * spy list), we do it, but he will pay the price.
817 * Note that to get here, you need both WIRELESS_SPY
818 * compiled in AND some addresses in the list !!!
819 */
820 /* Note : gcc will optimise the whole section away if
821 * WIRELESS_SPY is not defined... - Jean II */
822 if (SPY_NUMBER(priv)) {
Arnaldo Carvalho de Melo98e399f2007-03-19 15:33:04 -0700823 orinoco_spy_gather(dev, skb_mac_header(skb) + ETH_ALEN,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700824 desc->signal, desc->silence);
825 }
826}
827
Christoph Hellwig98c4cae2005-06-19 01:28:06 +0200828/*
829 * orinoco_rx_monitor - handle received monitor frames.
830 *
831 * Arguments:
832 * dev network device
833 * rxfid received FID
834 * desc rx descriptor of the frame
835 *
836 * Call context: interrupt
837 */
838static void orinoco_rx_monitor(struct net_device *dev, u16 rxfid,
839 struct hermes_rx_descriptor *desc)
840{
841 u32 hdrlen = 30; /* return full header by default */
842 u32 datalen = 0;
843 u16 fc;
844 int err;
845 int len;
846 struct sk_buff *skb;
847 struct orinoco_private *priv = netdev_priv(dev);
848 struct net_device_stats *stats = &priv->stats;
849 hermes_t *hw = &priv->hw;
850
851 len = le16_to_cpu(desc->data_len);
852
853 /* Determine the size of the header and the data */
854 fc = le16_to_cpu(desc->frame_ctl);
855 switch (fc & IEEE80211_FCTL_FTYPE) {
856 case IEEE80211_FTYPE_DATA:
857 if ((fc & IEEE80211_FCTL_TODS)
858 && (fc & IEEE80211_FCTL_FROMDS))
859 hdrlen = 30;
860 else
861 hdrlen = 24;
862 datalen = len;
863 break;
864 case IEEE80211_FTYPE_MGMT:
865 hdrlen = 24;
866 datalen = len;
867 break;
868 case IEEE80211_FTYPE_CTL:
869 switch (fc & IEEE80211_FCTL_STYPE) {
870 case IEEE80211_STYPE_PSPOLL:
871 case IEEE80211_STYPE_RTS:
872 case IEEE80211_STYPE_CFEND:
873 case IEEE80211_STYPE_CFENDACK:
874 hdrlen = 16;
875 break;
876 case IEEE80211_STYPE_CTS:
877 case IEEE80211_STYPE_ACK:
878 hdrlen = 10;
879 break;
880 }
881 break;
882 default:
883 /* Unknown frame type */
884 break;
885 }
886
887 /* sanity check the length */
Johannes Berg2c7060022008-10-30 22:09:54 +0100888 if (datalen > IEEE80211_MAX_DATA_LEN + 12) {
Christoph Hellwig98c4cae2005-06-19 01:28:06 +0200889 printk(KERN_DEBUG "%s: oversized monitor frame, "
890 "data length = %d\n", dev->name, datalen);
Christoph Hellwig98c4cae2005-06-19 01:28:06 +0200891 stats->rx_length_errors++;
892 goto update_stats;
893 }
894
895 skb = dev_alloc_skb(hdrlen + datalen);
896 if (!skb) {
897 printk(KERN_WARNING "%s: Cannot allocate skb for monitor frame\n",
898 dev->name);
Florin Malitabb6e0932006-05-22 22:35:30 -0700899 goto update_stats;
Christoph Hellwig98c4cae2005-06-19 01:28:06 +0200900 }
901
902 /* Copy the 802.11 header to the skb */
903 memcpy(skb_put(skb, hdrlen), &(desc->frame_ctl), hdrlen);
Arnaldo Carvalho de Melo459a98e2007-03-19 15:30:44 -0700904 skb_reset_mac_header(skb);
Christoph Hellwig98c4cae2005-06-19 01:28:06 +0200905
906 /* If any, copy the data from the card to the skb */
907 if (datalen > 0) {
908 err = hermes_bap_pread(hw, IRQ_BAP, skb_put(skb, datalen),
909 ALIGN(datalen, 2), rxfid,
910 HERMES_802_2_OFFSET);
911 if (err) {
912 printk(KERN_ERR "%s: error %d reading monitor frame\n",
913 dev->name, err);
914 goto drop;
915 }
916 }
917
918 skb->dev = dev;
919 skb->ip_summed = CHECKSUM_NONE;
920 skb->pkt_type = PACKET_OTHERHOST;
Harvey Harrisonc1b4aa32009-01-29 13:26:44 -0800921 skb->protocol = cpu_to_be16(ETH_P_802_2);
David Kilroy6fe9deb2009-02-04 23:05:43 +0000922
Christoph Hellwig98c4cae2005-06-19 01:28:06 +0200923 stats->rx_packets++;
924 stats->rx_bytes += skb->len;
925
926 netif_rx(skb);
927 return;
928
929 drop:
930 dev_kfree_skb_irq(skb);
931 update_stats:
932 stats->rx_errors++;
933 stats->rx_dropped++;
934}
935
David Kilroy23edcc42008-08-21 23:28:05 +0100936/* Get tsc from the firmware */
937static int orinoco_hw_get_tkip_iv(struct orinoco_private *priv, int key,
938 u8 *tsc)
939{
940 hermes_t *hw = &priv->hw;
941 int err = 0;
942 u8 tsc_arr[4][IW_ENCODE_SEQ_MAX_SIZE];
943
944 if ((key < 0) || (key > 4))
945 return -EINVAL;
946
947 err = hermes_read_ltv(hw, USER_BAP, HERMES_RID_CURRENT_TKIP_IV,
948 sizeof(tsc_arr), NULL, &tsc_arr);
949 if (!err)
950 memcpy(tsc, &tsc_arr[key][0], sizeof(tsc_arr[0]));
951
952 return err;
953}
954
Linus Torvalds1da177e2005-04-16 15:20:36 -0700955static void __orinoco_ev_rx(struct net_device *dev, hermes_t *hw)
956{
957 struct orinoco_private *priv = netdev_priv(dev);
958 struct net_device_stats *stats = &priv->stats;
959 struct iw_statistics *wstats = &priv->wstats;
960 struct sk_buff *skb = NULL;
David Kilroy31afcef2008-08-21 23:28:04 +0100961 u16 rxfid, status;
Christoph Hellwig8f2abf42005-06-19 01:28:02 +0200962 int length;
David Kilroy31afcef2008-08-21 23:28:04 +0100963 struct hermes_rx_descriptor *desc;
964 struct orinoco_rx_data *rx_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700965 int err;
966
David Kilroy31afcef2008-08-21 23:28:04 +0100967 desc = kmalloc(sizeof(*desc), GFP_ATOMIC);
968 if (!desc) {
969 printk(KERN_WARNING
970 "%s: Can't allocate space for RX descriptor\n",
971 dev->name);
972 goto update_stats;
973 }
974
Linus Torvalds1da177e2005-04-16 15:20:36 -0700975 rxfid = hermes_read_regn(hw, RXFID);
976
David Kilroy31afcef2008-08-21 23:28:04 +0100977 err = hermes_bap_pread(hw, IRQ_BAP, desc, sizeof(*desc),
Linus Torvalds1da177e2005-04-16 15:20:36 -0700978 rxfid, 0);
979 if (err) {
980 printk(KERN_ERR "%s: error %d reading Rx descriptor. "
981 "Frame dropped.\n", dev->name, err);
Christoph Hellwig98c4cae2005-06-19 01:28:06 +0200982 goto update_stats;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700983 }
984
David Kilroy31afcef2008-08-21 23:28:04 +0100985 status = le16_to_cpu(desc->status);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700986
Christoph Hellwig98c4cae2005-06-19 01:28:06 +0200987 if (status & HERMES_RXSTAT_BADCRC) {
988 DEBUG(1, "%s: Bad CRC on Rx. Frame dropped.\n",
989 dev->name);
990 stats->rx_crc_errors++;
991 goto update_stats;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700992 }
993
Christoph Hellwig98c4cae2005-06-19 01:28:06 +0200994 /* Handle frames in monitor mode */
995 if (priv->iw_mode == IW_MODE_MONITOR) {
David Kilroy31afcef2008-08-21 23:28:04 +0100996 orinoco_rx_monitor(dev, rxfid, desc);
997 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700998 }
999
Christoph Hellwig98c4cae2005-06-19 01:28:06 +02001000 if (status & HERMES_RXSTAT_UNDECRYPTABLE) {
1001 DEBUG(1, "%s: Undecryptable frame on Rx. Frame dropped.\n",
1002 dev->name);
1003 wstats->discard.code++;
1004 goto update_stats;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001005 }
1006
David Kilroy31afcef2008-08-21 23:28:04 +01001007 length = le16_to_cpu(desc->data_len);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001008
Linus Torvalds1da177e2005-04-16 15:20:36 -07001009 /* Sanity checks */
1010 if (length < 3) { /* No for even an 802.2 LLC header */
1011 /* At least on Symbol firmware with PCF we get quite a
David Kilroy6fe9deb2009-02-04 23:05:43 +00001012 lot of these legitimately - Poll frames with no
1013 data. */
David Kilroy31afcef2008-08-21 23:28:04 +01001014 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001015 }
Johannes Berg2c7060022008-10-30 22:09:54 +01001016 if (length > IEEE80211_MAX_DATA_LEN) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001017 printk(KERN_WARNING "%s: Oversized frame received (%d bytes)\n",
1018 dev->name, length);
1019 stats->rx_length_errors++;
Christoph Hellwig98c4cae2005-06-19 01:28:06 +02001020 goto update_stats;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001021 }
1022
David Kilroy23edcc42008-08-21 23:28:05 +01001023 /* Payload size does not include Michael MIC. Increase payload
1024 * size to read it together with the data. */
1025 if (status & HERMES_RXSTAT_MIC)
1026 length += MICHAEL_MIC_LEN;
1027
Linus Torvalds1da177e2005-04-16 15:20:36 -07001028 /* We need space for the packet data itself, plus an ethernet
1029 header, plus 2 bytes so we can align the IP header on a
1030 32bit boundary, plus 1 byte so we can read in odd length
1031 packets from the card, which has an IO granularity of 16
David Kilroy6fe9deb2009-02-04 23:05:43 +00001032 bits */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001033 skb = dev_alloc_skb(length+ETH_HLEN+2+1);
1034 if (!skb) {
1035 printk(KERN_WARNING "%s: Can't allocate skb for Rx\n",
1036 dev->name);
Christoph Hellwig98c4cae2005-06-19 01:28:06 +02001037 goto update_stats;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001038 }
1039
Christoph Hellwig8f2abf42005-06-19 01:28:02 +02001040 /* We'll prepend the header, so reserve space for it. The worst
1041 case is no decapsulation, when 802.3 header is prepended and
1042 nothing is removed. 2 is for aligning the IP header. */
1043 skb_reserve(skb, ETH_HLEN + 2);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001044
Christoph Hellwig8f2abf42005-06-19 01:28:02 +02001045 err = hermes_bap_pread(hw, IRQ_BAP, skb_put(skb, length),
1046 ALIGN(length, 2), rxfid,
1047 HERMES_802_2_OFFSET);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001048 if (err) {
1049 printk(KERN_ERR "%s: error %d reading frame. "
1050 "Frame dropped.\n", dev->name, err);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001051 goto drop;
1052 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001053
David Kilroy31afcef2008-08-21 23:28:04 +01001054 /* Add desc and skb to rx queue */
1055 rx_data = kzalloc(sizeof(*rx_data), GFP_ATOMIC);
1056 if (!rx_data) {
1057 printk(KERN_WARNING "%s: Can't allocate RX packet\n",
1058 dev->name);
1059 goto drop;
1060 }
1061 rx_data->desc = desc;
1062 rx_data->skb = skb;
1063 list_add_tail(&rx_data->list, &priv->rx_list);
1064 tasklet_schedule(&priv->rx_tasklet);
1065
1066 return;
1067
1068drop:
1069 dev_kfree_skb_irq(skb);
1070update_stats:
1071 stats->rx_errors++;
1072 stats->rx_dropped++;
1073out:
1074 kfree(desc);
1075}
1076
1077static void orinoco_rx(struct net_device *dev,
1078 struct hermes_rx_descriptor *desc,
1079 struct sk_buff *skb)
1080{
1081 struct orinoco_private *priv = netdev_priv(dev);
1082 struct net_device_stats *stats = &priv->stats;
1083 u16 status, fc;
1084 int length;
1085 struct ethhdr *hdr;
1086
1087 status = le16_to_cpu(desc->status);
1088 length = le16_to_cpu(desc->data_len);
1089 fc = le16_to_cpu(desc->frame_ctl);
1090
David Kilroy23edcc42008-08-21 23:28:05 +01001091 /* Calculate and check MIC */
1092 if (status & HERMES_RXSTAT_MIC) {
1093 int key_id = ((status & HERMES_RXSTAT_MIC_KEY_ID) >>
1094 HERMES_MIC_KEY_ID_SHIFT);
1095 u8 mic[MICHAEL_MIC_LEN];
1096 u8 *rxmic;
1097 u8 *src = (fc & IEEE80211_FCTL_FROMDS) ?
1098 desc->addr3 : desc->addr2;
1099
1100 /* Extract Michael MIC from payload */
1101 rxmic = skb->data + skb->len - MICHAEL_MIC_LEN;
1102
1103 skb_trim(skb, skb->len - MICHAEL_MIC_LEN);
1104 length -= MICHAEL_MIC_LEN;
1105
David Kilroy4adb4742009-02-04 23:05:51 +00001106 orinoco_mic(priv->rx_tfm_mic,
David Kilroy23edcc42008-08-21 23:28:05 +01001107 priv->tkip_key[key_id].rx_mic,
1108 desc->addr1,
1109 src,
1110 0, /* priority or QoS? */
1111 skb->data,
1112 skb->len,
1113 &mic[0]);
1114
1115 if (memcmp(mic, rxmic,
1116 MICHAEL_MIC_LEN)) {
1117 union iwreq_data wrqu;
1118 struct iw_michaelmicfailure wxmic;
David Kilroy23edcc42008-08-21 23:28:05 +01001119
1120 printk(KERN_WARNING "%s: "
Johannes Berge1749612008-10-27 15:59:26 -07001121 "Invalid Michael MIC in data frame from %pM, "
David Kilroy23edcc42008-08-21 23:28:05 +01001122 "using key %i\n",
Johannes Berge1749612008-10-27 15:59:26 -07001123 dev->name, src, key_id);
David Kilroy23edcc42008-08-21 23:28:05 +01001124
1125 /* TODO: update stats */
1126
1127 /* Notify userspace */
1128 memset(&wxmic, 0, sizeof(wxmic));
1129 wxmic.flags = key_id & IW_MICFAILURE_KEY_ID;
1130 wxmic.flags |= (desc->addr1[0] & 1) ?
1131 IW_MICFAILURE_GROUP : IW_MICFAILURE_PAIRWISE;
1132 wxmic.src_addr.sa_family = ARPHRD_ETHER;
1133 memcpy(wxmic.src_addr.sa_data, src, ETH_ALEN);
1134
1135 (void) orinoco_hw_get_tkip_iv(priv, key_id,
1136 &wxmic.tsc[0]);
1137
1138 memset(&wrqu, 0, sizeof(wrqu));
1139 wrqu.data.length = sizeof(wxmic);
1140 wireless_send_event(dev, IWEVMICHAELMICFAILURE, &wrqu,
1141 (char *) &wxmic);
1142
1143 goto drop;
1144 }
1145 }
1146
Linus Torvalds1da177e2005-04-16 15:20:36 -07001147 /* Handle decapsulation
1148 * In most cases, the firmware tell us about SNAP frames.
1149 * For some reason, the SNAP frames sent by LinkSys APs
1150 * are not properly recognised by most firmwares.
1151 * So, check ourselves */
Christoph Hellwig8f2abf42005-06-19 01:28:02 +02001152 if (length >= ENCAPS_OVERHEAD &&
1153 (((status & HERMES_RXSTAT_MSGTYPE) == HERMES_RXSTAT_1042) ||
1154 ((status & HERMES_RXSTAT_MSGTYPE) == HERMES_RXSTAT_TUNNEL) ||
1155 is_ethersnap(skb->data))) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001156 /* These indicate a SNAP within 802.2 LLC within
1157 802.11 frame which we'll need to de-encapsulate to
1158 the original EthernetII frame. */
David Kilroyb2f30a02009-02-04 23:05:46 +00001159 hdr = (struct ethhdr *)skb_push(skb,
1160 ETH_HLEN - ENCAPS_OVERHEAD);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001161 } else {
Christoph Hellwig8f2abf42005-06-19 01:28:02 +02001162 /* 802.3 frame - prepend 802.3 header as is */
1163 hdr = (struct ethhdr *)skb_push(skb, ETH_HLEN);
1164 hdr->h_proto = htons(length);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001165 }
David Kilroy31afcef2008-08-21 23:28:04 +01001166 memcpy(hdr->h_dest, desc->addr1, ETH_ALEN);
Christoph Hellwig8f2abf42005-06-19 01:28:02 +02001167 if (fc & IEEE80211_FCTL_FROMDS)
David Kilroy31afcef2008-08-21 23:28:04 +01001168 memcpy(hdr->h_source, desc->addr3, ETH_ALEN);
Christoph Hellwig8f2abf42005-06-19 01:28:02 +02001169 else
David Kilroy31afcef2008-08-21 23:28:04 +01001170 memcpy(hdr->h_source, desc->addr2, ETH_ALEN);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001171
Linus Torvalds1da177e2005-04-16 15:20:36 -07001172 skb->protocol = eth_type_trans(skb, dev);
1173 skb->ip_summed = CHECKSUM_NONE;
Christoph Hellwig8f2abf42005-06-19 01:28:02 +02001174 if (fc & IEEE80211_FCTL_TODS)
1175 skb->pkt_type = PACKET_OTHERHOST;
David Kilroy6fe9deb2009-02-04 23:05:43 +00001176
Linus Torvalds1da177e2005-04-16 15:20:36 -07001177 /* Process the wireless stats if needed */
David Kilroy31afcef2008-08-21 23:28:04 +01001178 orinoco_stat_gather(dev, skb, desc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001179
1180 /* Pass the packet to the networking stack */
1181 netif_rx(skb);
1182 stats->rx_packets++;
1183 stats->rx_bytes += length;
1184
1185 return;
David Kilroy23edcc42008-08-21 23:28:05 +01001186
1187 drop:
1188 dev_kfree_skb(skb);
1189 stats->rx_errors++;
1190 stats->rx_dropped++;
David Kilroy31afcef2008-08-21 23:28:04 +01001191}
Linus Torvalds1da177e2005-04-16 15:20:36 -07001192
David Kilroy31afcef2008-08-21 23:28:04 +01001193static void orinoco_rx_isr_tasklet(unsigned long data)
1194{
1195 struct net_device *dev = (struct net_device *) data;
1196 struct orinoco_private *priv = netdev_priv(dev);
1197 struct orinoco_rx_data *rx_data, *temp;
1198 struct hermes_rx_descriptor *desc;
1199 struct sk_buff *skb;
David Kilroy20953ad2009-01-07 00:23:55 +00001200 unsigned long flags;
1201
1202 /* orinoco_rx requires the driver lock, and we also need to
1203 * protect priv->rx_list, so just hold the lock over the
1204 * lot.
1205 *
1206 * If orinoco_lock fails, we've unplugged the card. In this
1207 * case just abort. */
1208 if (orinoco_lock(priv, &flags) != 0)
1209 return;
David Kilroy31afcef2008-08-21 23:28:04 +01001210
1211 /* extract desc and skb from queue */
1212 list_for_each_entry_safe(rx_data, temp, &priv->rx_list, list) {
1213 desc = rx_data->desc;
1214 skb = rx_data->skb;
1215 list_del(&rx_data->list);
1216 kfree(rx_data);
1217
1218 orinoco_rx(dev, desc, skb);
1219
1220 kfree(desc);
1221 }
David Kilroy20953ad2009-01-07 00:23:55 +00001222
1223 orinoco_unlock(priv, &flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001224}
1225
1226/********************************************************************/
1227/* Rx path (info frames) */
1228/********************************************************************/
1229
1230static void print_linkstatus(struct net_device *dev, u16 status)
1231{
David Kilroy21312662009-02-04 23:05:47 +00001232 char *s;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001233
1234 if (suppress_linkstatus)
1235 return;
1236
1237 switch (status) {
1238 case HERMES_LINKSTATUS_NOT_CONNECTED:
1239 s = "Not Connected";
1240 break;
1241 case HERMES_LINKSTATUS_CONNECTED:
1242 s = "Connected";
1243 break;
1244 case HERMES_LINKSTATUS_DISCONNECTED:
1245 s = "Disconnected";
1246 break;
1247 case HERMES_LINKSTATUS_AP_CHANGE:
1248 s = "AP Changed";
1249 break;
1250 case HERMES_LINKSTATUS_AP_OUT_OF_RANGE:
1251 s = "AP Out of Range";
1252 break;
1253 case HERMES_LINKSTATUS_AP_IN_RANGE:
1254 s = "AP In Range";
1255 break;
1256 case HERMES_LINKSTATUS_ASSOC_FAILED:
1257 s = "Association Failed";
1258 break;
1259 default:
1260 s = "UNKNOWN";
1261 }
David Kilroy6fe9deb2009-02-04 23:05:43 +00001262
Pavel Roskin11eaea42009-01-18 23:20:58 -05001263 printk(KERN_DEBUG "%s: New link status: %s (%04x)\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -07001264 dev->name, s, status);
1265}
1266
Christoph Hellwig16739b02005-06-19 01:27:51 +02001267/* Search scan results for requested BSSID, join it if found */
David Howellsc4028952006-11-22 14:57:56 +00001268static void orinoco_join_ap(struct work_struct *work)
Christoph Hellwig16739b02005-06-19 01:27:51 +02001269{
David Howellsc4028952006-11-22 14:57:56 +00001270 struct orinoco_private *priv =
1271 container_of(work, struct orinoco_private, join_work);
1272 struct net_device *dev = priv->ndev;
Christoph Hellwig16739b02005-06-19 01:27:51 +02001273 struct hermes *hw = &priv->hw;
1274 int err;
1275 unsigned long flags;
1276 struct join_req {
1277 u8 bssid[ETH_ALEN];
Pavel Roskind133ae42005-09-23 04:18:06 -04001278 __le16 channel;
Christoph Hellwig16739b02005-06-19 01:27:51 +02001279 } __attribute__ ((packed)) req;
1280 const int atom_len = offsetof(struct prism2_scan_apinfo, atim);
Pavel Roskinc89cc222005-09-01 20:06:06 -04001281 struct prism2_scan_apinfo *atom = NULL;
Christoph Hellwig16739b02005-06-19 01:27:51 +02001282 int offset = 4;
Pavel Roskinc89cc222005-09-01 20:06:06 -04001283 int found = 0;
Christoph Hellwig16739b02005-06-19 01:27:51 +02001284 u8 *buf;
1285 u16 len;
1286
1287 /* Allocate buffer for scan results */
1288 buf = kmalloc(MAX_SCAN_LEN, GFP_KERNEL);
David Kilroya94e8422009-02-04 23:05:44 +00001289 if (!buf)
Christoph Hellwig16739b02005-06-19 01:27:51 +02001290 return;
1291
1292 if (orinoco_lock(priv, &flags) != 0)
Pavel Roskinf3cb4cc2005-09-23 04:18:06 -04001293 goto fail_lock;
Christoph Hellwig16739b02005-06-19 01:27:51 +02001294
1295 /* Sanity checks in case user changed something in the meantime */
David Kilroya94e8422009-02-04 23:05:44 +00001296 if (!priv->bssid_fixed)
Christoph Hellwig16739b02005-06-19 01:27:51 +02001297 goto out;
1298
1299 if (strlen(priv->desired_essid) == 0)
1300 goto out;
1301
1302 /* Read scan results from the firmware */
1303 err = hermes_read_ltv(hw, USER_BAP,
1304 HERMES_RID_SCANRESULTSTABLE,
1305 MAX_SCAN_LEN, &len, buf);
1306 if (err) {
1307 printk(KERN_ERR "%s: Cannot read scan results\n",
1308 dev->name);
1309 goto out;
1310 }
1311
1312 len = HERMES_RECLEN_TO_BYTES(len);
1313
1314 /* Go through the scan results looking for the channel of the AP
1315 * we were requested to join */
1316 for (; offset + atom_len <= len; offset += atom_len) {
1317 atom = (struct prism2_scan_apinfo *) (buf + offset);
Pavel Roskinc89cc222005-09-01 20:06:06 -04001318 if (memcmp(&atom->bssid, priv->desired_bssid, ETH_ALEN) == 0) {
1319 found = 1;
1320 break;
1321 }
Christoph Hellwig16739b02005-06-19 01:27:51 +02001322 }
1323
David Kilroya94e8422009-02-04 23:05:44 +00001324 if (!found) {
Pavel Roskinc89cc222005-09-01 20:06:06 -04001325 DEBUG(1, "%s: Requested AP not found in scan results\n",
1326 dev->name);
1327 goto out;
1328 }
Christoph Hellwig16739b02005-06-19 01:27:51 +02001329
Christoph Hellwig16739b02005-06-19 01:27:51 +02001330 memcpy(req.bssid, priv->desired_bssid, ETH_ALEN);
1331 req.channel = atom->channel; /* both are little-endian */
1332 err = HERMES_WRITE_RECORD(hw, USER_BAP, HERMES_RID_CNFJOINREQUEST,
1333 &req);
1334 if (err)
1335 printk(KERN_ERR "%s: Error issuing join request\n", dev->name);
1336
1337 out:
Christoph Hellwig16739b02005-06-19 01:27:51 +02001338 orinoco_unlock(priv, &flags);
Pavel Roskinf3cb4cc2005-09-23 04:18:06 -04001339
1340 fail_lock:
1341 kfree(buf);
Christoph Hellwig16739b02005-06-19 01:27:51 +02001342}
1343
Christoph Hellwig95dd91f2005-06-19 01:27:56 +02001344/* Send new BSSID to userspace */
David Kilroy6cd90b12008-08-21 23:28:00 +01001345static void orinoco_send_bssid_wevent(struct orinoco_private *priv)
Christoph Hellwig95dd91f2005-06-19 01:27:56 +02001346{
David Howellsc4028952006-11-22 14:57:56 +00001347 struct net_device *dev = priv->ndev;
Christoph Hellwig95dd91f2005-06-19 01:27:56 +02001348 struct hermes *hw = &priv->hw;
1349 union iwreq_data wrqu;
1350 int err;
Christoph Hellwig95dd91f2005-06-19 01:27:56 +02001351
David Kilroy499b7022008-12-09 21:46:29 +00001352 err = hermes_read_ltv(hw, USER_BAP, HERMES_RID_CURRENTBSSID,
Christoph Hellwig95dd91f2005-06-19 01:27:56 +02001353 ETH_ALEN, NULL, wrqu.ap_addr.sa_data);
1354 if (err != 0)
David Kilroy6cd90b12008-08-21 23:28:00 +01001355 return;
Christoph Hellwig95dd91f2005-06-19 01:27:56 +02001356
1357 wrqu.ap_addr.sa_family = ARPHRD_ETHER;
1358
1359 /* Send event to user space */
1360 wireless_send_event(dev, SIOCGIWAP, &wrqu, NULL);
Christoph Hellwig95dd91f2005-06-19 01:27:56 +02001361}
1362
David Kilroy06009fd2008-08-21 23:28:03 +01001363static void orinoco_send_assocreqie_wevent(struct orinoco_private *priv)
1364{
1365 struct net_device *dev = priv->ndev;
1366 struct hermes *hw = &priv->hw;
1367 union iwreq_data wrqu;
1368 int err;
1369 u8 buf[88];
1370 u8 *ie;
1371
1372 if (!priv->has_wpa)
1373 return;
1374
David Kilroy499b7022008-12-09 21:46:29 +00001375 err = hermes_read_ltv(hw, USER_BAP, HERMES_RID_CURRENT_ASSOC_REQ_INFO,
David Kilroy06009fd2008-08-21 23:28:03 +01001376 sizeof(buf), NULL, &buf);
1377 if (err != 0)
1378 return;
1379
1380 ie = orinoco_get_wpa_ie(buf, sizeof(buf));
1381 if (ie) {
1382 int rem = sizeof(buf) - (ie - &buf[0]);
1383 wrqu.data.length = ie[1] + 2;
1384 if (wrqu.data.length > rem)
1385 wrqu.data.length = rem;
1386
1387 if (wrqu.data.length)
1388 /* Send event to user space */
1389 wireless_send_event(dev, IWEVASSOCREQIE, &wrqu, ie);
1390 }
1391}
1392
1393static void orinoco_send_assocrespie_wevent(struct orinoco_private *priv)
1394{
1395 struct net_device *dev = priv->ndev;
1396 struct hermes *hw = &priv->hw;
1397 union iwreq_data wrqu;
1398 int err;
1399 u8 buf[88]; /* TODO: verify max size or IW_GENERIC_IE_MAX */
1400 u8 *ie;
1401
1402 if (!priv->has_wpa)
1403 return;
1404
David Kilroy499b7022008-12-09 21:46:29 +00001405 err = hermes_read_ltv(hw, USER_BAP, HERMES_RID_CURRENT_ASSOC_RESP_INFO,
David Kilroy06009fd2008-08-21 23:28:03 +01001406 sizeof(buf), NULL, &buf);
1407 if (err != 0)
1408 return;
1409
1410 ie = orinoco_get_wpa_ie(buf, sizeof(buf));
1411 if (ie) {
1412 int rem = sizeof(buf) - (ie - &buf[0]);
1413 wrqu.data.length = ie[1] + 2;
1414 if (wrqu.data.length > rem)
1415 wrqu.data.length = rem;
1416
1417 if (wrqu.data.length)
1418 /* Send event to user space */
1419 wireless_send_event(dev, IWEVASSOCRESPIE, &wrqu, ie);
1420 }
1421}
1422
David Kilroy6cd90b12008-08-21 23:28:00 +01001423static void orinoco_send_wevents(struct work_struct *work)
1424{
1425 struct orinoco_private *priv =
1426 container_of(work, struct orinoco_private, wevent_work);
1427 unsigned long flags;
1428
1429 if (orinoco_lock(priv, &flags) != 0)
1430 return;
1431
David Kilroy06009fd2008-08-21 23:28:03 +01001432 orinoco_send_assocreqie_wevent(priv);
1433 orinoco_send_assocrespie_wevent(priv);
David Kilroy6cd90b12008-08-21 23:28:00 +01001434 orinoco_send_bssid_wevent(priv);
1435
1436 orinoco_unlock(priv, &flags);
1437}
Dan Williams1e3428e2007-10-10 23:56:25 -04001438
Linus Torvalds1da177e2005-04-16 15:20:36 -07001439static void __orinoco_ev_info(struct net_device *dev, hermes_t *hw)
1440{
1441 struct orinoco_private *priv = netdev_priv(dev);
1442 u16 infofid;
1443 struct {
Pavel Roskind133ae42005-09-23 04:18:06 -04001444 __le16 len;
1445 __le16 type;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001446 } __attribute__ ((packed)) info;
1447 int len, type;
1448 int err;
1449
1450 /* This is an answer to an INQUIRE command that we did earlier,
1451 * or an information "event" generated by the card
1452 * The controller return to us a pseudo frame containing
1453 * the information in question - Jean II */
1454 infofid = hermes_read_regn(hw, INFOFID);
1455
1456 /* Read the info frame header - don't try too hard */
1457 err = hermes_bap_pread(hw, IRQ_BAP, &info, sizeof(info),
1458 infofid, 0);
1459 if (err) {
1460 printk(KERN_ERR "%s: error %d reading info frame. "
1461 "Frame dropped.\n", dev->name, err);
1462 return;
1463 }
David Kilroy6fe9deb2009-02-04 23:05:43 +00001464
Linus Torvalds1da177e2005-04-16 15:20:36 -07001465 len = HERMES_RECLEN_TO_BYTES(le16_to_cpu(info.len));
1466 type = le16_to_cpu(info.type);
1467
1468 switch (type) {
1469 case HERMES_INQ_TALLIES: {
1470 struct hermes_tallies_frame tallies;
1471 struct iw_statistics *wstats = &priv->wstats;
David Kilroy6fe9deb2009-02-04 23:05:43 +00001472
Linus Torvalds1da177e2005-04-16 15:20:36 -07001473 if (len > sizeof(tallies)) {
1474 printk(KERN_WARNING "%s: Tallies frame too long (%d bytes)\n",
1475 dev->name, len);
1476 len = sizeof(tallies);
1477 }
David Kilroy6fe9deb2009-02-04 23:05:43 +00001478
Christoph Hellwig84d8a2f2005-05-14 17:30:22 +02001479 err = hermes_bap_pread(hw, IRQ_BAP, &tallies, len,
1480 infofid, sizeof(info));
1481 if (err)
1482 break;
David Kilroy6fe9deb2009-02-04 23:05:43 +00001483
Linus Torvalds1da177e2005-04-16 15:20:36 -07001484 /* Increment our various counters */
1485 /* wstats->discard.nwid - no wrong BSSID stuff */
1486 wstats->discard.code +=
1487 le16_to_cpu(tallies.RxWEPUndecryptable);
David Kilroy6fe9deb2009-02-04 23:05:43 +00001488 if (len == sizeof(tallies))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001489 wstats->discard.code +=
1490 le16_to_cpu(tallies.RxDiscards_WEPICVError) +
1491 le16_to_cpu(tallies.RxDiscards_WEPExcluded);
1492 wstats->discard.misc +=
1493 le16_to_cpu(tallies.TxDiscardsWrongSA);
1494 wstats->discard.fragment +=
1495 le16_to_cpu(tallies.RxMsgInBadMsgFragments);
1496 wstats->discard.retries +=
1497 le16_to_cpu(tallies.TxRetryLimitExceeded);
1498 /* wstats->miss.beacon - no match */
1499 }
1500 break;
1501 case HERMES_INQ_LINKSTATUS: {
1502 struct hermes_linkstatus linkstatus;
1503 u16 newstatus;
1504 int connected;
1505
Christoph Hellwig8f2abf42005-06-19 01:28:02 +02001506 if (priv->iw_mode == IW_MODE_MONITOR)
1507 break;
1508
Linus Torvalds1da177e2005-04-16 15:20:36 -07001509 if (len != sizeof(linkstatus)) {
1510 printk(KERN_WARNING "%s: Unexpected size for linkstatus frame (%d bytes)\n",
1511 dev->name, len);
1512 break;
1513 }
1514
Christoph Hellwig84d8a2f2005-05-14 17:30:22 +02001515 err = hermes_bap_pread(hw, IRQ_BAP, &linkstatus, len,
1516 infofid, sizeof(info));
1517 if (err)
1518 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001519 newstatus = le16_to_cpu(linkstatus.linkstatus);
1520
Christoph Hellwig95dd91f2005-06-19 01:27:56 +02001521 /* Symbol firmware uses "out of range" to signal that
1522 * the hostscan frame can be requested. */
1523 if (newstatus == HERMES_LINKSTATUS_AP_OUT_OF_RANGE &&
1524 priv->firmware_type == FIRMWARE_TYPE_SYMBOL &&
1525 priv->has_hostscan && priv->scan_inprogress) {
1526 hermes_inquire(hw, HERMES_INQ_HOSTSCAN_SYMBOL);
1527 break;
1528 }
1529
Linus Torvalds1da177e2005-04-16 15:20:36 -07001530 connected = (newstatus == HERMES_LINKSTATUS_CONNECTED)
1531 || (newstatus == HERMES_LINKSTATUS_AP_CHANGE)
1532 || (newstatus == HERMES_LINKSTATUS_AP_IN_RANGE);
1533
1534 if (connected)
1535 netif_carrier_on(dev);
David Gibson7bb7c3a2005-05-12 20:02:10 -04001536 else if (!ignore_disconnect)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001537 netif_carrier_off(dev);
1538
Christoph Hellwig95dd91f2005-06-19 01:27:56 +02001539 if (newstatus != priv->last_linkstatus) {
1540 priv->last_linkstatus = newstatus;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001541 print_linkstatus(dev, newstatus);
Christoph Hellwig95dd91f2005-06-19 01:27:56 +02001542 /* The info frame contains only one word which is the
1543 * status (see hermes.h). The status is pretty boring
1544 * in itself, that's why we export the new BSSID...
1545 * Jean II */
1546 schedule_work(&priv->wevent_work);
1547 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001548 }
1549 break;
Christoph Hellwig95dd91f2005-06-19 01:27:56 +02001550 case HERMES_INQ_SCAN:
1551 if (!priv->scan_inprogress && priv->bssid_fixed &&
1552 priv->firmware_type == FIRMWARE_TYPE_INTERSIL) {
1553 schedule_work(&priv->join_work);
1554 break;
1555 }
1556 /* fall through */
1557 case HERMES_INQ_HOSTSCAN:
1558 case HERMES_INQ_HOSTSCAN_SYMBOL: {
1559 /* Result of a scanning. Contains information about
1560 * cells in the vicinity - Jean II */
1561 union iwreq_data wrqu;
1562 unsigned char *buf;
1563
Dan Williams1e3428e2007-10-10 23:56:25 -04001564 /* Scan is no longer in progress */
1565 priv->scan_inprogress = 0;
1566
Christoph Hellwig95dd91f2005-06-19 01:27:56 +02001567 /* Sanity check */
1568 if (len > 4096) {
1569 printk(KERN_WARNING "%s: Scan results too large (%d bytes)\n",
1570 dev->name, len);
1571 break;
1572 }
1573
Christoph Hellwig95dd91f2005-06-19 01:27:56 +02001574 /* Allocate buffer for results */
1575 buf = kmalloc(len, GFP_ATOMIC);
1576 if (buf == NULL)
1577 /* No memory, so can't printk()... */
1578 break;
1579
1580 /* Read scan data */
1581 err = hermes_bap_pread(hw, IRQ_BAP, (void *) buf, len,
1582 infofid, sizeof(info));
Pavel Roskin708218b2005-09-01 20:05:19 -04001583 if (err) {
1584 kfree(buf);
Christoph Hellwig95dd91f2005-06-19 01:27:56 +02001585 break;
Pavel Roskin708218b2005-09-01 20:05:19 -04001586 }
Christoph Hellwig95dd91f2005-06-19 01:27:56 +02001587
1588#ifdef ORINOCO_DEBUG
1589 {
1590 int i;
1591 printk(KERN_DEBUG "Scan result [%02X", buf[0]);
David Kilroya94e8422009-02-04 23:05:44 +00001592 for (i = 1; i < (len * 2); i++)
Christoph Hellwig95dd91f2005-06-19 01:27:56 +02001593 printk(":%02X", buf[i]);
1594 printk("]\n");
1595 }
1596#endif /* ORINOCO_DEBUG */
1597
David Kilroyaea48b12009-02-04 23:05:49 +00001598 if (orinoco_process_scan_results(priv, buf, len) == 0) {
Dan Williams1e3428e2007-10-10 23:56:25 -04001599 /* Send an empty event to user space.
1600 * We don't send the received data on the event because
1601 * it would require us to do complex transcoding, and
1602 * we want to minimise the work done in the irq handler
1603 * Use a request to extract the data - Jean II */
1604 wrqu.data.length = 0;
1605 wrqu.data.flags = 0;
1606 wireless_send_event(dev, SIOCGIWSCAN, &wrqu, NULL);
1607 }
1608 kfree(buf);
Christoph Hellwig95dd91f2005-06-19 01:27:56 +02001609 }
1610 break;
David Kilroy01632fa2008-08-21 23:27:58 +01001611 case HERMES_INQ_CHANNELINFO:
1612 {
1613 struct agere_ext_scan_info *bss;
1614
1615 if (!priv->scan_inprogress) {
1616 printk(KERN_DEBUG "%s: Got chaninfo without scan, "
1617 "len=%d\n", dev->name, len);
1618 break;
1619 }
1620
1621 /* An empty result indicates that the scan is complete */
1622 if (len == 0) {
1623 union iwreq_data wrqu;
1624
1625 /* Scan is no longer in progress */
1626 priv->scan_inprogress = 0;
1627
1628 wrqu.data.length = 0;
1629 wrqu.data.flags = 0;
1630 wireless_send_event(dev, SIOCGIWSCAN, &wrqu, NULL);
1631 break;
1632 }
1633
1634 /* Sanity check */
1635 else if (len > sizeof(*bss)) {
1636 printk(KERN_WARNING
1637 "%s: Ext scan results too large (%d bytes). "
1638 "Truncating results to %zd bytes.\n",
1639 dev->name, len, sizeof(*bss));
1640 len = sizeof(*bss);
1641 } else if (len < (offsetof(struct agere_ext_scan_info,
1642 data) + 2)) {
1643 /* Drop this result now so we don't have to
1644 * keep checking later */
1645 printk(KERN_WARNING
1646 "%s: Ext scan results too short (%d bytes)\n",
1647 dev->name, len);
1648 break;
1649 }
1650
1651 bss = kmalloc(sizeof(*bss), GFP_ATOMIC);
1652 if (bss == NULL)
1653 break;
1654
1655 /* Read scan data */
1656 err = hermes_bap_pread(hw, IRQ_BAP, (void *) bss, len,
1657 infofid, sizeof(info));
1658 if (err) {
1659 kfree(bss);
1660 break;
1661 }
1662
1663 orinoco_add_ext_scan_result(priv, bss);
1664
1665 kfree(bss);
1666 break;
1667 }
Christoph Hellwig95dd91f2005-06-19 01:27:56 +02001668 case HERMES_INQ_SEC_STAT_AGERE:
1669 /* Security status (Agere specific) */
1670 /* Ignore this frame for now */
1671 if (priv->firmware_type == FIRMWARE_TYPE_AGERE)
1672 break;
1673 /* fall through */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001674 default:
1675 printk(KERN_DEBUG "%s: Unknown information frame received: "
1676 "type 0x%04x, length %d\n", dev->name, type, len);
1677 /* We don't actually do anything about it */
1678 break;
1679 }
1680}
1681
1682static void __orinoco_ev_infdrop(struct net_device *dev, hermes_t *hw)
1683{
1684 if (net_ratelimit())
1685 printk(KERN_DEBUG "%s: Information frame lost.\n", dev->name);
1686}
1687
1688/********************************************************************/
1689/* Internal hardware control routines */
1690/********************************************************************/
1691
1692int __orinoco_up(struct net_device *dev)
1693{
1694 struct orinoco_private *priv = netdev_priv(dev);
1695 struct hermes *hw = &priv->hw;
1696 int err;
1697
Christoph Hellwig84d8a2f2005-05-14 17:30:22 +02001698 netif_carrier_off(dev); /* just to make sure */
1699
Linus Torvalds1da177e2005-04-16 15:20:36 -07001700 err = __orinoco_program_rids(dev);
1701 if (err) {
1702 printk(KERN_ERR "%s: Error %d configuring card\n",
1703 dev->name, err);
1704 return err;
1705 }
1706
1707 /* Fire things up again */
1708 hermes_set_irqmask(hw, ORINOCO_INTEN);
1709 err = hermes_enable_port(hw, 0);
1710 if (err) {
1711 printk(KERN_ERR "%s: Error %d enabling MAC port\n",
1712 dev->name, err);
1713 return err;
1714 }
1715
1716 netif_start_queue(dev);
1717
1718 return 0;
1719}
David Kilroy21312662009-02-04 23:05:47 +00001720EXPORT_SYMBOL(__orinoco_up);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001721
1722int __orinoco_down(struct net_device *dev)
1723{
1724 struct orinoco_private *priv = netdev_priv(dev);
1725 struct hermes *hw = &priv->hw;
1726 int err;
1727
1728 netif_stop_queue(dev);
1729
David Kilroya94e8422009-02-04 23:05:44 +00001730 if (!priv->hw_unavailable) {
1731 if (!priv->broken_disableport) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001732 err = hermes_disable_port(hw, 0);
1733 if (err) {
1734 /* Some firmwares (e.g. Intersil 1.3.x) seem
1735 * to have problems disabling the port, oh
1736 * well, too bad. */
1737 printk(KERN_WARNING "%s: Error %d disabling MAC port\n",
1738 dev->name, err);
1739 priv->broken_disableport = 1;
1740 }
1741 }
1742 hermes_set_irqmask(hw, 0);
1743 hermes_write_regn(hw, EVACK, 0xffff);
1744 }
David Kilroy6fe9deb2009-02-04 23:05:43 +00001745
Linus Torvalds1da177e2005-04-16 15:20:36 -07001746 /* firmware will have to reassociate */
1747 netif_carrier_off(dev);
1748 priv->last_linkstatus = 0xffff;
1749
1750 return 0;
1751}
David Kilroy21312662009-02-04 23:05:47 +00001752EXPORT_SYMBOL(__orinoco_down);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001753
Pavel Roskin37a6c612006-04-07 04:10:49 -04001754static int orinoco_allocate_fid(struct net_device *dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001755{
1756 struct orinoco_private *priv = netdev_priv(dev);
1757 struct hermes *hw = &priv->hw;
1758 int err;
1759
Linus Torvalds1da177e2005-04-16 15:20:36 -07001760 err = hermes_allocate(hw, priv->nicbuf_size, &priv->txfid);
David Gibsonb24d4582005-05-12 20:04:16 -04001761 if (err == -EIO && priv->nicbuf_size > TX_NICBUF_SIZE_BUG) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001762 /* Try workaround for old Symbol firmware bug */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001763 priv->nicbuf_size = TX_NICBUF_SIZE_BUG;
1764 err = hermes_allocate(hw, priv->nicbuf_size, &priv->txfid);
David Kilroy21312662009-02-04 23:05:47 +00001765
1766 printk(KERN_WARNING "%s: firmware ALLOC bug detected "
1767 "(old Symbol firmware?). Work around %s\n",
1768 dev->name, err ? "failed!" : "ok.");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001769 }
1770
1771 return err;
1772}
1773
Pavel Roskin37a6c612006-04-07 04:10:49 -04001774int orinoco_reinit_firmware(struct net_device *dev)
1775{
1776 struct orinoco_private *priv = netdev_priv(dev);
1777 struct hermes *hw = &priv->hw;
1778 int err;
1779
1780 err = hermes_init(hw);
Andrey Borzenkov0df6cbb2008-10-12 20:15:43 +04001781 if (priv->do_fw_download && !err) {
1782 err = orinoco_download(priv);
1783 if (err)
1784 priv->do_fw_download = 0;
1785 }
Pavel Roskin37a6c612006-04-07 04:10:49 -04001786 if (!err)
1787 err = orinoco_allocate_fid(dev);
1788
1789 return err;
1790}
David Kilroy21312662009-02-04 23:05:47 +00001791EXPORT_SYMBOL(orinoco_reinit_firmware);
Pavel Roskin37a6c612006-04-07 04:10:49 -04001792
Linus Torvalds1da177e2005-04-16 15:20:36 -07001793static int __orinoco_hw_set_bitrate(struct orinoco_private *priv)
1794{
1795 hermes_t *hw = &priv->hw;
David Kilroyb2f30a02009-02-04 23:05:46 +00001796 int ratemode = priv->bitratemode;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001797 int err = 0;
1798
David Kilroyb2f30a02009-02-04 23:05:46 +00001799 if (ratemode >= BITRATE_TABLE_SIZE) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001800 printk(KERN_ERR "%s: BUG: Invalid bitrate mode %d\n",
David Kilroyb2f30a02009-02-04 23:05:46 +00001801 priv->ndev->name, ratemode);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001802 return -EINVAL;
1803 }
1804
1805 switch (priv->firmware_type) {
1806 case FIRMWARE_TYPE_AGERE:
1807 err = hermes_write_wordrec(hw, USER_BAP,
David Kilroyb2f30a02009-02-04 23:05:46 +00001808 HERMES_RID_CNFTXRATECONTROL,
1809 bitrate_table[ratemode].agere_txratectrl);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001810 break;
1811 case FIRMWARE_TYPE_INTERSIL:
1812 case FIRMWARE_TYPE_SYMBOL:
1813 err = hermes_write_wordrec(hw, USER_BAP,
David Kilroyb2f30a02009-02-04 23:05:46 +00001814 HERMES_RID_CNFTXRATECONTROL,
1815 bitrate_table[ratemode].intersil_txratectrl);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001816 break;
1817 default:
1818 BUG();
1819 }
1820
1821 return err;
1822}
1823
David Kilroycfeb1db2009-02-04 23:05:53 +00001824static int orinoco_hw_get_act_bitrate(struct orinoco_private *priv,
1825 int *bitrate)
1826{
1827 hermes_t *hw = &priv->hw;
1828 int i;
1829 int err = 0;
1830 u16 val;
1831
1832 err = hermes_read_wordrec(hw, USER_BAP,
1833 HERMES_RID_CURRENTTXRATE, &val);
1834 if (err)
1835 return err;
1836
1837 switch (priv->firmware_type) {
1838 case FIRMWARE_TYPE_AGERE: /* Lucent style rate */
1839 /* Note : in Lucent firmware, the return value of
1840 * HERMES_RID_CURRENTTXRATE is the bitrate in Mb/s,
1841 * and therefore is totally different from the
1842 * encoding of HERMES_RID_CNFTXRATECONTROL.
1843 * Don't forget that 6Mb/s is really 5.5Mb/s */
1844 if (val == 6)
1845 *bitrate = 5500000;
1846 else
1847 *bitrate = val * 1000000;
1848 break;
1849 case FIRMWARE_TYPE_INTERSIL: /* Intersil style rate */
1850 case FIRMWARE_TYPE_SYMBOL: /* Symbol style rate */
1851 for (i = 0; i < BITRATE_TABLE_SIZE; i++)
1852 if (bitrate_table[i].intersil_txratectrl == val)
1853 break;
1854
1855 if (i >= BITRATE_TABLE_SIZE)
1856 printk(KERN_INFO "%s: Unable to determine current bitrate (0x%04hx)\n",
1857 priv->ndev->name, val);
1858
1859 *bitrate = bitrate_table[i].bitrate * 100000;
1860 break;
1861 default:
1862 BUG();
1863 }
1864
1865 return err;
1866}
1867
Christoph Hellwig16739b02005-06-19 01:27:51 +02001868/* Set fixed AP address */
1869static int __orinoco_hw_set_wap(struct orinoco_private *priv)
1870{
1871 int roaming_flag;
1872 int err = 0;
1873 hermes_t *hw = &priv->hw;
1874
1875 switch (priv->firmware_type) {
1876 case FIRMWARE_TYPE_AGERE:
1877 /* not supported */
1878 break;
1879 case FIRMWARE_TYPE_INTERSIL:
1880 if (priv->bssid_fixed)
1881 roaming_flag = 2;
1882 else
1883 roaming_flag = 1;
1884
1885 err = hermes_write_wordrec(hw, USER_BAP,
1886 HERMES_RID_CNFROAMINGMODE,
1887 roaming_flag);
1888 break;
1889 case FIRMWARE_TYPE_SYMBOL:
1890 err = HERMES_WRITE_RECORD(hw, USER_BAP,
1891 HERMES_RID_CNFMANDATORYBSSID_SYMBOL,
1892 &priv->desired_bssid);
1893 break;
1894 }
1895 return err;
1896}
1897
Linus Torvalds1da177e2005-04-16 15:20:36 -07001898/* Change the WEP keys and/or the current keys. Can be called
David Kilroyd03032a2008-08-21 23:28:02 +01001899 * either from __orinoco_hw_setup_enc() or directly from
Linus Torvalds1da177e2005-04-16 15:20:36 -07001900 * orinoco_ioctl_setiwencode(). In the later case the association
1901 * with the AP is not broken (if the firmware can handle it),
1902 * which is needed for 802.1x implementations. */
1903static int __orinoco_hw_setup_wepkeys(struct orinoco_private *priv)
1904{
1905 hermes_t *hw = &priv->hw;
1906 int err = 0;
1907
1908 switch (priv->firmware_type) {
1909 case FIRMWARE_TYPE_AGERE:
1910 err = HERMES_WRITE_RECORD(hw, USER_BAP,
1911 HERMES_RID_CNFWEPKEYS_AGERE,
1912 &priv->keys);
1913 if (err)
1914 return err;
1915 err = hermes_write_wordrec(hw, USER_BAP,
1916 HERMES_RID_CNFTXKEY_AGERE,
1917 priv->tx_key);
1918 if (err)
1919 return err;
1920 break;
1921 case FIRMWARE_TYPE_INTERSIL:
1922 case FIRMWARE_TYPE_SYMBOL:
1923 {
1924 int keylen;
1925 int i;
1926
David Kilroyb2f30a02009-02-04 23:05:46 +00001927 /* Force uniform key length to work around
1928 * firmware bugs */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001929 keylen = le16_to_cpu(priv->keys[priv->tx_key].len);
David Kilroy6fe9deb2009-02-04 23:05:43 +00001930
Linus Torvalds1da177e2005-04-16 15:20:36 -07001931 if (keylen > LARGE_KEY_SIZE) {
1932 printk(KERN_ERR "%s: BUG: Key %d has oversize length %d.\n",
1933 priv->ndev->name, priv->tx_key, keylen);
1934 return -E2BIG;
1935 }
1936
1937 /* Write all 4 keys */
David Kilroya94e8422009-02-04 23:05:44 +00001938 for (i = 0; i < ORINOCO_MAX_KEYS; i++) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001939 err = hermes_write_ltv(hw, USER_BAP,
David Kilroyb2f30a02009-02-04 23:05:46 +00001940 HERMES_RID_CNFDEFAULTKEY0 + i,
1941 HERMES_BYTES_TO_RECLEN(keylen),
1942 priv->keys[i].data);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001943 if (err)
1944 return err;
1945 }
1946
1947 /* Write the index of the key used in transmission */
1948 err = hermes_write_wordrec(hw, USER_BAP,
David Kilroyb2f30a02009-02-04 23:05:46 +00001949 HERMES_RID_CNFWEPDEFAULTKEYID,
1950 priv->tx_key);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001951 if (err)
1952 return err;
1953 }
1954 break;
1955 }
1956
1957 return 0;
1958}
1959
David Kilroyd03032a2008-08-21 23:28:02 +01001960static int __orinoco_hw_setup_enc(struct orinoco_private *priv)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001961{
1962 hermes_t *hw = &priv->hw;
1963 int err = 0;
1964 int master_wep_flag;
1965 int auth_flag;
David Kilroy4ae6ee22008-08-21 23:27:59 +01001966 int enc_flag;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001967
David Kilroyd03032a2008-08-21 23:28:02 +01001968 /* Setup WEP keys for WEP and WPA */
1969 if (priv->encode_alg)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001970 __orinoco_hw_setup_wepkeys(priv);
1971
1972 if (priv->wep_restrict)
1973 auth_flag = HERMES_AUTH_SHARED_KEY;
1974 else
1975 auth_flag = HERMES_AUTH_OPEN;
1976
David Kilroyd03032a2008-08-21 23:28:02 +01001977 if (priv->wpa_enabled)
1978 enc_flag = 2;
1979 else if (priv->encode_alg == IW_ENCODE_ALG_WEP)
David Kilroy4ae6ee22008-08-21 23:27:59 +01001980 enc_flag = 1;
1981 else
1982 enc_flag = 0;
1983
Linus Torvalds1da177e2005-04-16 15:20:36 -07001984 switch (priv->firmware_type) {
1985 case FIRMWARE_TYPE_AGERE: /* Agere style WEP */
David Kilroy4ae6ee22008-08-21 23:27:59 +01001986 if (priv->encode_alg == IW_ENCODE_ALG_WEP) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001987 /* Enable the shared-key authentication. */
1988 err = hermes_write_wordrec(hw, USER_BAP,
David Kilroyb2f30a02009-02-04 23:05:46 +00001989 HERMES_RID_CNFAUTHENTICATION_AGERE,
1990 auth_flag);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001991 }
1992 err = hermes_write_wordrec(hw, USER_BAP,
1993 HERMES_RID_CNFWEPENABLED_AGERE,
David Kilroy4ae6ee22008-08-21 23:27:59 +01001994 enc_flag);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001995 if (err)
1996 return err;
David Kilroyd03032a2008-08-21 23:28:02 +01001997
1998 if (priv->has_wpa) {
1999 /* Set WPA key management */
2000 err = hermes_write_wordrec(hw, USER_BAP,
2001 HERMES_RID_CNFSETWPAAUTHMGMTSUITE_AGERE,
2002 priv->key_mgmt);
2003 if (err)
2004 return err;
2005 }
2006
Linus Torvalds1da177e2005-04-16 15:20:36 -07002007 break;
2008
2009 case FIRMWARE_TYPE_INTERSIL: /* Intersil style WEP */
2010 case FIRMWARE_TYPE_SYMBOL: /* Symbol style WEP */
David Kilroy4ae6ee22008-08-21 23:27:59 +01002011 if (priv->encode_alg == IW_ENCODE_ALG_WEP) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002012 if (priv->wep_restrict ||
2013 (priv->firmware_type == FIRMWARE_TYPE_SYMBOL))
2014 master_wep_flag = HERMES_WEP_PRIVACY_INVOKED |
2015 HERMES_WEP_EXCL_UNENCRYPTED;
2016 else
2017 master_wep_flag = HERMES_WEP_PRIVACY_INVOKED;
2018
2019 err = hermes_write_wordrec(hw, USER_BAP,
2020 HERMES_RID_CNFAUTHENTICATION,
2021 auth_flag);
2022 if (err)
2023 return err;
2024 } else
2025 master_wep_flag = 0;
2026
2027 if (priv->iw_mode == IW_MODE_MONITOR)
2028 master_wep_flag |= HERMES_WEP_HOST_DECRYPT;
2029
2030 /* Master WEP setting : on/off */
2031 err = hermes_write_wordrec(hw, USER_BAP,
2032 HERMES_RID_CNFWEPFLAGS_INTERSIL,
2033 master_wep_flag);
2034 if (err)
David Kilroy6fe9deb2009-02-04 23:05:43 +00002035 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002036
2037 break;
2038 }
2039
2040 return 0;
2041}
2042
David Kilroyd03032a2008-08-21 23:28:02 +01002043/* key must be 32 bytes, including the tx and rx MIC keys.
2044 * rsc must be 8 bytes
2045 * tsc must be 8 bytes or NULL
2046 */
2047static int __orinoco_hw_set_tkip_key(hermes_t *hw, int key_idx, int set_tx,
2048 u8 *key, u8 *rsc, u8 *tsc)
2049{
2050 struct {
2051 __le16 idx;
2052 u8 rsc[IW_ENCODE_SEQ_MAX_SIZE];
2053 u8 key[TKIP_KEYLEN];
2054 u8 tx_mic[MIC_KEYLEN];
2055 u8 rx_mic[MIC_KEYLEN];
2056 u8 tsc[IW_ENCODE_SEQ_MAX_SIZE];
2057 } __attribute__ ((packed)) buf;
2058 int ret;
2059 int err;
2060 int k;
2061 u16 xmitting;
2062
2063 key_idx &= 0x3;
2064
2065 if (set_tx)
2066 key_idx |= 0x8000;
2067
2068 buf.idx = cpu_to_le16(key_idx);
2069 memcpy(buf.key, key,
2070 sizeof(buf.key) + sizeof(buf.tx_mic) + sizeof(buf.rx_mic));
2071
2072 if (rsc == NULL)
2073 memset(buf.rsc, 0, sizeof(buf.rsc));
2074 else
2075 memcpy(buf.rsc, rsc, sizeof(buf.rsc));
2076
2077 if (tsc == NULL) {
2078 memset(buf.tsc, 0, sizeof(buf.tsc));
2079 buf.tsc[4] = 0x10;
2080 } else {
2081 memcpy(buf.tsc, tsc, sizeof(buf.tsc));
2082 }
2083
2084 /* Wait upto 100ms for tx queue to empty */
2085 k = 100;
2086 do {
2087 k--;
2088 udelay(1000);
2089 ret = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_TXQUEUEEMPTY,
2090 &xmitting);
2091 if (ret)
2092 break;
2093 } while ((k > 0) && xmitting);
2094
2095 if (k == 0)
2096 ret = -ETIMEDOUT;
2097
2098 err = HERMES_WRITE_RECORD(hw, USER_BAP,
2099 HERMES_RID_CNFADDDEFAULTTKIPKEY_AGERE,
2100 &buf);
2101
2102 return ret ? ret : err;
2103}
2104
2105static int orinoco_clear_tkip_key(struct orinoco_private *priv,
2106 int key_idx)
2107{
2108 hermes_t *hw = &priv->hw;
2109 int err;
2110
2111 memset(&priv->tkip_key[key_idx], 0, sizeof(priv->tkip_key[key_idx]));
2112 err = hermes_write_wordrec(hw, USER_BAP,
2113 HERMES_RID_CNFREMDEFAULTTKIPKEY_AGERE,
2114 key_idx);
2115 if (err)
2116 printk(KERN_WARNING "%s: Error %d clearing TKIP key %d\n",
2117 priv->ndev->name, err, key_idx);
2118 return err;
2119}
2120
Linus Torvalds1da177e2005-04-16 15:20:36 -07002121static int __orinoco_program_rids(struct net_device *dev)
2122{
2123 struct orinoco_private *priv = netdev_priv(dev);
2124 hermes_t *hw = &priv->hw;
2125 int err;
2126 struct hermes_idstring idbuf;
2127
2128 /* Set the MAC address */
2129 err = hermes_write_ltv(hw, USER_BAP, HERMES_RID_CNFOWNMACADDR,
2130 HERMES_BYTES_TO_RECLEN(ETH_ALEN), dev->dev_addr);
2131 if (err) {
2132 printk(KERN_ERR "%s: Error %d setting MAC address\n",
2133 dev->name, err);
2134 return err;
2135 }
2136
2137 /* Set up the link mode */
2138 err = hermes_write_wordrec(hw, USER_BAP, HERMES_RID_CNFPORTTYPE,
2139 priv->port_type);
2140 if (err) {
2141 printk(KERN_ERR "%s: Error %d setting port type\n",
2142 dev->name, err);
2143 return err;
2144 }
2145 /* Set the channel/frequency */
David Gibsond51d8b12005-05-12 20:03:36 -04002146 if (priv->channel != 0 && priv->iw_mode != IW_MODE_INFRA) {
2147 err = hermes_write_wordrec(hw, USER_BAP,
2148 HERMES_RID_CNFOWNCHANNEL,
2149 priv->channel);
2150 if (err) {
2151 printk(KERN_ERR "%s: Error %d setting channel %d\n",
2152 dev->name, err, priv->channel);
2153 return err;
2154 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002155 }
2156
2157 if (priv->has_ibss) {
2158 u16 createibss;
2159
2160 if ((strlen(priv->desired_essid) == 0) && (priv->createibss)) {
2161 printk(KERN_WARNING "%s: This firmware requires an "
2162 "ESSID in IBSS-Ad-Hoc mode.\n", dev->name);
2163 /* With wvlan_cs, in this case, we would crash.
2164 * hopefully, this driver will behave better...
2165 * Jean II */
2166 createibss = 0;
2167 } else {
2168 createibss = priv->createibss;
2169 }
David Kilroy6fe9deb2009-02-04 23:05:43 +00002170
Linus Torvalds1da177e2005-04-16 15:20:36 -07002171 err = hermes_write_wordrec(hw, USER_BAP,
2172 HERMES_RID_CNFCREATEIBSS,
2173 createibss);
2174 if (err) {
2175 printk(KERN_ERR "%s: Error %d setting CREATEIBSS\n",
2176 dev->name, err);
2177 return err;
2178 }
2179 }
2180
Christoph Hellwig16739b02005-06-19 01:27:51 +02002181 /* Set the desired BSSID */
2182 err = __orinoco_hw_set_wap(priv);
2183 if (err) {
2184 printk(KERN_ERR "%s: Error %d setting AP address\n",
2185 dev->name, err);
2186 return err;
2187 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002188 /* Set the desired ESSID */
2189 idbuf.len = cpu_to_le16(strlen(priv->desired_essid));
2190 memcpy(&idbuf.val, priv->desired_essid, sizeof(idbuf.val));
2191 /* WinXP wants partner to configure OWNSSID even in IBSS mode. (jimc) */
2192 err = hermes_write_ltv(hw, USER_BAP, HERMES_RID_CNFOWNSSID,
David Kilroyb2f30a02009-02-04 23:05:46 +00002193 HERMES_BYTES_TO_RECLEN(strlen(priv->desired_essid)+2),
2194 &idbuf);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002195 if (err) {
2196 printk(KERN_ERR "%s: Error %d setting OWNSSID\n",
2197 dev->name, err);
2198 return err;
2199 }
2200 err = hermes_write_ltv(hw, USER_BAP, HERMES_RID_CNFDESIREDSSID,
David Kilroyb2f30a02009-02-04 23:05:46 +00002201 HERMES_BYTES_TO_RECLEN(strlen(priv->desired_essid)+2),
2202 &idbuf);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002203 if (err) {
2204 printk(KERN_ERR "%s: Error %d setting DESIREDSSID\n",
2205 dev->name, err);
2206 return err;
2207 }
2208
2209 /* Set the station name */
2210 idbuf.len = cpu_to_le16(strlen(priv->nick));
2211 memcpy(&idbuf.val, priv->nick, sizeof(idbuf.val));
2212 err = hermes_write_ltv(hw, USER_BAP, HERMES_RID_CNFOWNNAME,
2213 HERMES_BYTES_TO_RECLEN(strlen(priv->nick)+2),
2214 &idbuf);
2215 if (err) {
2216 printk(KERN_ERR "%s: Error %d setting nickname\n",
2217 dev->name, err);
2218 return err;
2219 }
2220
2221 /* Set AP density */
2222 if (priv->has_sensitivity) {
2223 err = hermes_write_wordrec(hw, USER_BAP,
2224 HERMES_RID_CNFSYSTEMSCALE,
2225 priv->ap_density);
2226 if (err) {
David Kilroyb2f30a02009-02-04 23:05:46 +00002227 printk(KERN_WARNING "%s: Error %d setting SYSTEMSCALE. "
Linus Torvalds1da177e2005-04-16 15:20:36 -07002228 "Disabling sensitivity control\n",
2229 dev->name, err);
2230
2231 priv->has_sensitivity = 0;
2232 }
2233 }
2234
2235 /* Set RTS threshold */
2236 err = hermes_write_wordrec(hw, USER_BAP, HERMES_RID_CNFRTSTHRESHOLD,
2237 priv->rts_thresh);
2238 if (err) {
2239 printk(KERN_ERR "%s: Error %d setting RTS threshold\n",
2240 dev->name, err);
2241 return err;
2242 }
2243
2244 /* Set fragmentation threshold or MWO robustness */
2245 if (priv->has_mwo)
2246 err = hermes_write_wordrec(hw, USER_BAP,
2247 HERMES_RID_CNFMWOROBUST_AGERE,
2248 priv->mwo_robust);
2249 else
2250 err = hermes_write_wordrec(hw, USER_BAP,
2251 HERMES_RID_CNFFRAGMENTATIONTHRESHOLD,
2252 priv->frag_thresh);
2253 if (err) {
2254 printk(KERN_ERR "%s: Error %d setting fragmentation\n",
2255 dev->name, err);
2256 return err;
2257 }
2258
2259 /* Set bitrate */
2260 err = __orinoco_hw_set_bitrate(priv);
2261 if (err) {
2262 printk(KERN_ERR "%s: Error %d setting bitrate\n",
2263 dev->name, err);
2264 return err;
2265 }
2266
2267 /* Set power management */
2268 if (priv->has_pm) {
2269 err = hermes_write_wordrec(hw, USER_BAP,
2270 HERMES_RID_CNFPMENABLED,
2271 priv->pm_on);
2272 if (err) {
2273 printk(KERN_ERR "%s: Error %d setting up PM\n",
2274 dev->name, err);
2275 return err;
2276 }
2277
2278 err = hermes_write_wordrec(hw, USER_BAP,
2279 HERMES_RID_CNFMULTICASTRECEIVE,
2280 priv->pm_mcast);
2281 if (err) {
2282 printk(KERN_ERR "%s: Error %d setting up PM\n",
2283 dev->name, err);
2284 return err;
2285 }
2286 err = hermes_write_wordrec(hw, USER_BAP,
2287 HERMES_RID_CNFMAXSLEEPDURATION,
2288 priv->pm_period);
2289 if (err) {
2290 printk(KERN_ERR "%s: Error %d setting up PM\n",
2291 dev->name, err);
2292 return err;
2293 }
2294 err = hermes_write_wordrec(hw, USER_BAP,
2295 HERMES_RID_CNFPMHOLDOVERDURATION,
2296 priv->pm_timeout);
2297 if (err) {
2298 printk(KERN_ERR "%s: Error %d setting up PM\n",
2299 dev->name, err);
2300 return err;
2301 }
2302 }
2303
2304 /* Set preamble - only for Symbol so far... */
2305 if (priv->has_preamble) {
2306 err = hermes_write_wordrec(hw, USER_BAP,
2307 HERMES_RID_CNFPREAMBLE_SYMBOL,
2308 priv->preamble);
2309 if (err) {
2310 printk(KERN_ERR "%s: Error %d setting preamble\n",
2311 dev->name, err);
2312 return err;
2313 }
2314 }
2315
2316 /* Set up encryption */
David Kilroyd03032a2008-08-21 23:28:02 +01002317 if (priv->has_wep || priv->has_wpa) {
2318 err = __orinoco_hw_setup_enc(priv);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002319 if (err) {
David Kilroyd03032a2008-08-21 23:28:02 +01002320 printk(KERN_ERR "%s: Error %d activating encryption\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -07002321 dev->name, err);
2322 return err;
2323 }
2324 }
2325
Christoph Hellwig98c4cae2005-06-19 01:28:06 +02002326 if (priv->iw_mode == IW_MODE_MONITOR) {
2327 /* Enable monitor mode */
2328 dev->type = ARPHRD_IEEE80211;
David Kilroy6fe9deb2009-02-04 23:05:43 +00002329 err = hermes_docmd_wait(hw, HERMES_CMD_TEST |
Christoph Hellwig98c4cae2005-06-19 01:28:06 +02002330 HERMES_TEST_MONITOR, 0, NULL);
2331 } else {
2332 /* Disable monitor mode */
2333 dev->type = ARPHRD_ETHER;
2334 err = hermes_docmd_wait(hw, HERMES_CMD_TEST |
2335 HERMES_TEST_STOP, 0, NULL);
2336 }
2337 if (err)
2338 return err;
2339
Linus Torvalds1da177e2005-04-16 15:20:36 -07002340 /* Set promiscuity / multicast*/
2341 priv->promiscuous = 0;
2342 priv->mc_count = 0;
Herbert Xu932ff272006-06-09 12:20:56 -07002343
2344 /* FIXME: what about netif_tx_lock */
2345 __orinoco_set_multicast_list(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002346
2347 return 0;
2348}
2349
2350/* FIXME: return int? */
2351static void
2352__orinoco_set_multicast_list(struct net_device *dev)
2353{
2354 struct orinoco_private *priv = netdev_priv(dev);
2355 hermes_t *hw = &priv->hw;
2356 int err = 0;
2357 int promisc, mc_count;
2358
2359 /* The Hermes doesn't seem to have an allmulti mode, so we go
2360 * into promiscuous mode and let the upper levels deal. */
David Kilroya94e8422009-02-04 23:05:44 +00002361 if ((dev->flags & IFF_PROMISC) || (dev->flags & IFF_ALLMULTI) ||
2362 (dev->mc_count > MAX_MULTICAST(priv))) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002363 promisc = 1;
2364 mc_count = 0;
2365 } else {
2366 promisc = 0;
2367 mc_count = dev->mc_count;
2368 }
2369
2370 if (promisc != priv->promiscuous) {
2371 err = hermes_write_wordrec(hw, USER_BAP,
2372 HERMES_RID_CNFPROMISCUOUSMODE,
2373 promisc);
2374 if (err) {
2375 printk(KERN_ERR "%s: Error %d setting PROMISCUOUSMODE to 1.\n",
2376 dev->name, err);
David Kilroy6fe9deb2009-02-04 23:05:43 +00002377 } else
Linus Torvalds1da177e2005-04-16 15:20:36 -07002378 priv->promiscuous = promisc;
2379 }
2380
David Kilroy667d4102008-08-23 19:03:34 +01002381 /* If we're not in promiscuous mode, then we need to set the
2382 * group address if either we want to multicast, or if we were
2383 * multicasting and want to stop */
David Kilroya94e8422009-02-04 23:05:44 +00002384 if (!promisc && (mc_count || priv->mc_count)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002385 struct dev_mc_list *p = dev->mc_list;
2386 struct hermes_multicast mclist;
2387 int i;
2388
2389 for (i = 0; i < mc_count; i++) {
2390 /* paranoia: is list shorter than mc_count? */
David Kilroya94e8422009-02-04 23:05:44 +00002391 BUG_ON(!p);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002392 /* paranoia: bad address size in list? */
2393 BUG_ON(p->dmi_addrlen != ETH_ALEN);
David Kilroy6fe9deb2009-02-04 23:05:43 +00002394
Linus Torvalds1da177e2005-04-16 15:20:36 -07002395 memcpy(mclist.addr[i], p->dmi_addr, ETH_ALEN);
2396 p = p->next;
2397 }
David Kilroy6fe9deb2009-02-04 23:05:43 +00002398
Linus Torvalds1da177e2005-04-16 15:20:36 -07002399 if (p)
2400 printk(KERN_WARNING "%s: Multicast list is "
2401 "longer than mc_count\n", dev->name);
2402
David Kilroy667d4102008-08-23 19:03:34 +01002403 err = hermes_write_ltv(hw, USER_BAP,
2404 HERMES_RID_CNFGROUPADDRESSES,
2405 HERMES_BYTES_TO_RECLEN(mc_count * ETH_ALEN),
2406 &mclist);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002407 if (err)
2408 printk(KERN_ERR "%s: Error %d setting multicast list.\n",
2409 dev->name, err);
2410 else
2411 priv->mc_count = mc_count;
2412 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002413}
2414
Linus Torvalds1da177e2005-04-16 15:20:36 -07002415/* This must be called from user context, without locks held - use
2416 * schedule_work() */
David Howellsc4028952006-11-22 14:57:56 +00002417static void orinoco_reset(struct work_struct *work)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002418{
David Howellsc4028952006-11-22 14:57:56 +00002419 struct orinoco_private *priv =
2420 container_of(work, struct orinoco_private, reset_work);
2421 struct net_device *dev = priv->ndev;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002422 struct hermes *hw = &priv->hw;
Christoph Hellwig8551cb92005-05-14 17:30:04 +02002423 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002424 unsigned long flags;
2425
2426 if (orinoco_lock(priv, &flags) != 0)
2427 /* When the hardware becomes available again, whatever
2428 * detects that is responsible for re-initializing
2429 * it. So no need for anything further */
2430 return;
2431
2432 netif_stop_queue(dev);
2433
2434 /* Shut off interrupts. Depending on what state the hardware
2435 * is in, this might not work, but we'll try anyway */
2436 hermes_set_irqmask(hw, 0);
2437 hermes_write_regn(hw, EVACK, 0xffff);
2438
2439 priv->hw_unavailable++;
2440 priv->last_linkstatus = 0xffff; /* firmware will have to reassociate */
2441 netif_carrier_off(dev);
2442
2443 orinoco_unlock(priv, &flags);
2444
David Kilroy6fe9deb2009-02-04 23:05:43 +00002445 /* Scanning support: Cleanup of driver struct */
Dan Williams1e3428e2007-10-10 23:56:25 -04002446 orinoco_clear_scan_results(priv, 0);
Christoph Hellwig95dd91f2005-06-19 01:27:56 +02002447 priv->scan_inprogress = 0;
2448
Christoph Hellwig8551cb92005-05-14 17:30:04 +02002449 if (priv->hard_reset) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002450 err = (*priv->hard_reset)(priv);
Christoph Hellwig8551cb92005-05-14 17:30:04 +02002451 if (err) {
2452 printk(KERN_ERR "%s: orinoco_reset: Error %d "
2453 "performing hard reset\n", dev->name, err);
2454 goto disable;
2455 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002456 }
2457
2458 err = orinoco_reinit_firmware(dev);
2459 if (err) {
2460 printk(KERN_ERR "%s: orinoco_reset: Error %d re-initializing firmware\n",
2461 dev->name, err);
Christoph Hellwig8551cb92005-05-14 17:30:04 +02002462 goto disable;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002463 }
2464
David Kilroyb2f30a02009-02-04 23:05:46 +00002465 /* This has to be called from user context */
2466 spin_lock_irq(&priv->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002467
2468 priv->hw_unavailable--;
2469
2470 /* priv->open or priv->hw_unavailable might have changed while
2471 * we dropped the lock */
David Kilroya94e8422009-02-04 23:05:44 +00002472 if (priv->open && (!priv->hw_unavailable)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002473 err = __orinoco_up(dev);
2474 if (err) {
2475 printk(KERN_ERR "%s: orinoco_reset: Error %d reenabling card\n",
2476 dev->name, err);
2477 } else
2478 dev->trans_start = jiffies;
2479 }
2480
2481 spin_unlock_irq(&priv->lock);
2482
2483 return;
Christoph Hellwig8551cb92005-05-14 17:30:04 +02002484 disable:
2485 hermes_set_irqmask(hw, 0);
2486 netif_device_detach(dev);
2487 printk(KERN_ERR "%s: Device has been disabled!\n", dev->name);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002488}
2489
2490/********************************************************************/
2491/* Interrupt handler */
2492/********************************************************************/
2493
2494static void __orinoco_ev_tick(struct net_device *dev, hermes_t *hw)
2495{
2496 printk(KERN_DEBUG "%s: TICK\n", dev->name);
2497}
2498
2499static void __orinoco_ev_wterr(struct net_device *dev, hermes_t *hw)
2500{
2501 /* This seems to happen a fair bit under load, but ignoring it
2502 seems to work fine...*/
2503 printk(KERN_DEBUG "%s: MAC controller error (WTERR). Ignoring.\n",
2504 dev->name);
2505}
2506
David Howells7d12e782006-10-05 14:55:46 +01002507irqreturn_t orinoco_interrupt(int irq, void *dev_id)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002508{
Jeff Garzikc31f28e2006-10-06 14:56:04 -04002509 struct net_device *dev = dev_id;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002510 struct orinoco_private *priv = netdev_priv(dev);
2511 hermes_t *hw = &priv->hw;
2512 int count = MAX_IRQLOOPS_PER_IRQ;
2513 u16 evstat, events;
David Kilroy21312662009-02-04 23:05:47 +00002514 /* These are used to detect a runaway interrupt situation.
2515 *
2516 * If we get more than MAX_IRQLOOPS_PER_JIFFY iterations in a jiffy,
2517 * we panic and shut down the hardware
2518 */
2519 /* jiffies value the last time we were called */
2520 static int last_irq_jiffy; /* = 0 */
2521 static int loops_this_jiffy; /* = 0 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002522 unsigned long flags;
2523
2524 if (orinoco_lock(priv, &flags) != 0) {
2525 /* If hw is unavailable - we don't know if the irq was
2526 * for us or not */
2527 return IRQ_HANDLED;
2528 }
2529
2530 evstat = hermes_read_regn(hw, EVSTAT);
2531 events = evstat & hw->inten;
David Kilroya94e8422009-02-04 23:05:44 +00002532 if (!events) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002533 orinoco_unlock(priv, &flags);
2534 return IRQ_NONE;
2535 }
David Kilroy6fe9deb2009-02-04 23:05:43 +00002536
Linus Torvalds1da177e2005-04-16 15:20:36 -07002537 if (jiffies != last_irq_jiffy)
2538 loops_this_jiffy = 0;
2539 last_irq_jiffy = jiffies;
2540
2541 while (events && count--) {
2542 if (++loops_this_jiffy > MAX_IRQLOOPS_PER_JIFFY) {
2543 printk(KERN_WARNING "%s: IRQ handler is looping too "
2544 "much! Resetting.\n", dev->name);
2545 /* Disable interrupts for now */
2546 hermes_set_irqmask(hw, 0);
2547 schedule_work(&priv->reset_work);
2548 break;
2549 }
2550
2551 /* Check the card hasn't been removed */
David Kilroya94e8422009-02-04 23:05:44 +00002552 if (!hermes_present(hw)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002553 DEBUG(0, "orinoco_interrupt(): card removed\n");
2554 break;
2555 }
2556
2557 if (events & HERMES_EV_TICK)
2558 __orinoco_ev_tick(dev, hw);
2559 if (events & HERMES_EV_WTERR)
2560 __orinoco_ev_wterr(dev, hw);
2561 if (events & HERMES_EV_INFDROP)
2562 __orinoco_ev_infdrop(dev, hw);
2563 if (events & HERMES_EV_INFO)
2564 __orinoco_ev_info(dev, hw);
2565 if (events & HERMES_EV_RX)
2566 __orinoco_ev_rx(dev, hw);
2567 if (events & HERMES_EV_TXEXC)
2568 __orinoco_ev_txexc(dev, hw);
2569 if (events & HERMES_EV_TX)
2570 __orinoco_ev_tx(dev, hw);
2571 if (events & HERMES_EV_ALLOC)
2572 __orinoco_ev_alloc(dev, hw);
David Kilroy6fe9deb2009-02-04 23:05:43 +00002573
Christoph Hellwig84d8a2f2005-05-14 17:30:22 +02002574 hermes_write_regn(hw, EVACK, evstat);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002575
2576 evstat = hermes_read_regn(hw, EVSTAT);
2577 events = evstat & hw->inten;
2578 };
2579
2580 orinoco_unlock(priv, &flags);
2581 return IRQ_HANDLED;
2582}
David Kilroy21312662009-02-04 23:05:47 +00002583EXPORT_SYMBOL(orinoco_interrupt);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002584
2585/********************************************************************/
David Kilroy39d1ffe2008-11-22 10:37:28 +00002586/* Power management */
2587/********************************************************************/
2588#if defined(CONFIG_PM_SLEEP) && !defined(CONFIG_HERMES_CACHE_FW_ON_INIT)
2589static int orinoco_pm_notifier(struct notifier_block *notifier,
2590 unsigned long pm_event,
2591 void *unused)
2592{
2593 struct orinoco_private *priv = container_of(notifier,
2594 struct orinoco_private,
2595 pm_notifier);
2596
2597 /* All we need to do is cache the firmware before suspend, and
2598 * release it when we come out.
2599 *
2600 * Only need to do this if we're downloading firmware. */
2601 if (!priv->do_fw_download)
2602 return NOTIFY_DONE;
2603
2604 switch (pm_event) {
2605 case PM_HIBERNATION_PREPARE:
2606 case PM_SUSPEND_PREPARE:
2607 orinoco_cache_fw(priv, 0);
2608 break;
2609
2610 case PM_POST_RESTORE:
2611 /* Restore from hibernation failed. We need to clean
2612 * up in exactly the same way, so fall through. */
2613 case PM_POST_HIBERNATION:
2614 case PM_POST_SUSPEND:
2615 orinoco_uncache_fw(priv);
2616 break;
2617
2618 case PM_RESTORE_PREPARE:
2619 default:
2620 break;
2621 }
2622
2623 return NOTIFY_DONE;
2624}
2625#else /* !PM_SLEEP || HERMES_CACHE_FW_ON_INIT */
2626#define orinoco_pm_notifier NULL
2627#endif
2628
2629/********************************************************************/
Linus Torvalds1da177e2005-04-16 15:20:36 -07002630/* Initialization */
2631/********************************************************************/
2632
2633struct comp_id {
2634 u16 id, variant, major, minor;
2635} __attribute__ ((packed));
2636
2637static inline fwtype_t determine_firmware_type(struct comp_id *nic_id)
2638{
2639 if (nic_id->id < 0x8000)
2640 return FIRMWARE_TYPE_AGERE;
2641 else if (nic_id->id == 0x8000 && nic_id->major == 0)
2642 return FIRMWARE_TYPE_SYMBOL;
2643 else
2644 return FIRMWARE_TYPE_INTERSIL;
2645}
2646
2647/* Set priv->firmware type, determine firmware properties */
2648static int determine_firmware(struct net_device *dev)
2649{
2650 struct orinoco_private *priv = netdev_priv(dev);
2651 hermes_t *hw = &priv->hw;
2652 int err;
2653 struct comp_id nic_id, sta_id;
2654 unsigned int firmver;
Hennerich, Michaeldde6d432007-02-05 16:41:35 -08002655 char tmp[SYMBOL_MAX_VER_LEN+1] __attribute__((aligned(2)));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002656
2657 /* Get the hardware version */
2658 err = HERMES_READ_RECORD(hw, USER_BAP, HERMES_RID_NICID, &nic_id);
2659 if (err) {
2660 printk(KERN_ERR "%s: Cannot read hardware identity: error %d\n",
2661 dev->name, err);
2662 return err;
2663 }
2664
2665 le16_to_cpus(&nic_id.id);
2666 le16_to_cpus(&nic_id.variant);
2667 le16_to_cpus(&nic_id.major);
2668 le16_to_cpus(&nic_id.minor);
2669 printk(KERN_DEBUG "%s: Hardware identity %04x:%04x:%04x:%04x\n",
2670 dev->name, nic_id.id, nic_id.variant,
2671 nic_id.major, nic_id.minor);
2672
2673 priv->firmware_type = determine_firmware_type(&nic_id);
2674
2675 /* Get the firmware version */
2676 err = HERMES_READ_RECORD(hw, USER_BAP, HERMES_RID_STAID, &sta_id);
2677 if (err) {
2678 printk(KERN_ERR "%s: Cannot read station identity: error %d\n",
2679 dev->name, err);
2680 return err;
2681 }
2682
2683 le16_to_cpus(&sta_id.id);
2684 le16_to_cpus(&sta_id.variant);
2685 le16_to_cpus(&sta_id.major);
2686 le16_to_cpus(&sta_id.minor);
2687 printk(KERN_DEBUG "%s: Station identity %04x:%04x:%04x:%04x\n",
2688 dev->name, sta_id.id, sta_id.variant,
2689 sta_id.major, sta_id.minor);
2690
2691 switch (sta_id.id) {
2692 case 0x15:
2693 printk(KERN_ERR "%s: Primary firmware is active\n",
2694 dev->name);
2695 return -ENODEV;
2696 case 0x14b:
2697 printk(KERN_ERR "%s: Tertiary firmware is active\n",
2698 dev->name);
2699 return -ENODEV;
2700 case 0x1f: /* Intersil, Agere, Symbol Spectrum24 */
2701 case 0x21: /* Symbol Spectrum24 Trilogy */
2702 break;
2703 default:
2704 printk(KERN_NOTICE "%s: Unknown station ID, please report\n",
2705 dev->name);
2706 break;
2707 }
2708
2709 /* Default capabilities */
2710 priv->has_sensitivity = 1;
2711 priv->has_mwo = 0;
2712 priv->has_preamble = 0;
2713 priv->has_port3 = 1;
2714 priv->has_ibss = 1;
2715 priv->has_wep = 0;
2716 priv->has_big_wep = 0;
David Kilroy6eecad72008-08-21 23:27:56 +01002717 priv->has_alt_txcntl = 0;
David Kilroy01632fa2008-08-21 23:27:58 +01002718 priv->has_ext_scan = 0;
David Kilroyd03032a2008-08-21 23:28:02 +01002719 priv->has_wpa = 0;
David Kilroy3994d502008-08-21 23:27:54 +01002720 priv->do_fw_download = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002721
2722 /* Determine capabilities from the firmware version */
2723 switch (priv->firmware_type) {
2724 case FIRMWARE_TYPE_AGERE:
2725 /* Lucent Wavelan IEEE, Lucent Orinoco, Cabletron RoamAbout,
2726 ELSA, Melco, HP, IBM, Dell 1150, Compaq 110/210 */
2727 snprintf(priv->fw_name, sizeof(priv->fw_name) - 1,
2728 "Lucent/Agere %d.%02d", sta_id.major, sta_id.minor);
2729
2730 firmver = ((unsigned long)sta_id.major << 16) | sta_id.minor;
2731
2732 priv->has_ibss = (firmver >= 0x60006);
2733 priv->has_wep = (firmver >= 0x40020);
2734 priv->has_big_wep = 1; /* FIXME: this is wrong - how do we tell
2735 Gold cards from the others? */
2736 priv->has_mwo = (firmver >= 0x60000);
2737 priv->has_pm = (firmver >= 0x40020); /* Don't work in 7.52 ? */
2738 priv->ibss_port = 1;
Christoph Hellwig95dd91f2005-06-19 01:27:56 +02002739 priv->has_hostscan = (firmver >= 0x8000a);
David Kilroy3994d502008-08-21 23:27:54 +01002740 priv->do_fw_download = 1;
Christoph Hellwig98c4cae2005-06-19 01:28:06 +02002741 priv->broken_monitor = (firmver >= 0x80000);
David Kilroy6eecad72008-08-21 23:27:56 +01002742 priv->has_alt_txcntl = (firmver >= 0x90000); /* All 9.x ? */
David Kilroy01632fa2008-08-21 23:27:58 +01002743 priv->has_ext_scan = (firmver >= 0x90000); /* All 9.x ? */
David Kilroyd03032a2008-08-21 23:28:02 +01002744 priv->has_wpa = (firmver >= 0x9002a);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002745 /* Tested with Agere firmware :
2746 * 1.16 ; 4.08 ; 4.52 ; 6.04 ; 6.16 ; 7.28 => Jean II
2747 * Tested CableTron firmware : 4.32 => Anton */
2748 break;
2749 case FIRMWARE_TYPE_SYMBOL:
2750 /* Symbol , 3Com AirConnect, Intel, Ericsson WLAN */
2751 /* Intel MAC : 00:02:B3:* */
2752 /* 3Com MAC : 00:50:DA:* */
2753 memset(tmp, 0, sizeof(tmp));
2754 /* Get the Symbol firmware version */
2755 err = hermes_read_ltv(hw, USER_BAP,
2756 HERMES_RID_SECONDARYVERSION_SYMBOL,
2757 SYMBOL_MAX_VER_LEN, NULL, &tmp);
2758 if (err) {
2759 printk(KERN_WARNING
David Kilroyb2f30a02009-02-04 23:05:46 +00002760 "%s: Error %d reading Symbol firmware info. "
2761 "Wildly guessing capabilities...\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -07002762 dev->name, err);
2763 firmver = 0;
2764 tmp[0] = '\0';
2765 } else {
2766 /* The firmware revision is a string, the format is
2767 * something like : "V2.20-01".
2768 * Quick and dirty parsing... - Jean II
2769 */
David Kilroyb2f30a02009-02-04 23:05:46 +00002770 firmver = ((tmp[1] - '0') << 16)
2771 | ((tmp[3] - '0') << 12)
2772 | ((tmp[4] - '0') << 8)
2773 | ((tmp[6] - '0') << 4)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002774 | (tmp[7] - '0');
2775
2776 tmp[SYMBOL_MAX_VER_LEN] = '\0';
2777 }
2778
2779 snprintf(priv->fw_name, sizeof(priv->fw_name) - 1,
2780 "Symbol %s", tmp);
2781
2782 priv->has_ibss = (firmver >= 0x20000);
2783 priv->has_wep = (firmver >= 0x15012);
2784 priv->has_big_wep = (firmver >= 0x20000);
David Kilroy6fe9deb2009-02-04 23:05:43 +00002785 priv->has_pm = (firmver >= 0x20000 && firmver < 0x22000) ||
Linus Torvalds1da177e2005-04-16 15:20:36 -07002786 (firmver >= 0x29000 && firmver < 0x30000) ||
2787 firmver >= 0x31000;
2788 priv->has_preamble = (firmver >= 0x20000);
2789 priv->ibss_port = 4;
David Kilroy3994d502008-08-21 23:27:54 +01002790
2791 /* Symbol firmware is found on various cards, but
2792 * there has been no attempt to check firmware
2793 * download on non-spectrum_cs based cards.
2794 *
2795 * Given that the Agere firmware download works
2796 * differently, we should avoid doing a firmware
2797 * download with the Symbol algorithm on non-spectrum
2798 * cards.
2799 *
2800 * For now we can identify a spectrum_cs based card
2801 * because it has a firmware reset function.
2802 */
2803 priv->do_fw_download = (priv->stop_fw != NULL);
2804
David Kilroy6fe9deb2009-02-04 23:05:43 +00002805 priv->broken_disableport = (firmver == 0x25013) ||
David Kilroyb2f30a02009-02-04 23:05:46 +00002806 (firmver >= 0x30000 && firmver <= 0x31000);
Christoph Hellwig95dd91f2005-06-19 01:27:56 +02002807 priv->has_hostscan = (firmver >= 0x31001) ||
2808 (firmver >= 0x29057 && firmver < 0x30000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002809 /* Tested with Intel firmware : 0x20015 => Jean II */
2810 /* Tested with 3Com firmware : 0x15012 & 0x22001 => Jean II */
2811 break;
2812 case FIRMWARE_TYPE_INTERSIL:
2813 /* D-Link, Linksys, Adtron, ZoomAir, and many others...
2814 * Samsung, Compaq 100/200 and Proxim are slightly
2815 * different and less well tested */
2816 /* D-Link MAC : 00:40:05:* */
2817 /* Addtron MAC : 00:90:D1:* */
2818 snprintf(priv->fw_name, sizeof(priv->fw_name) - 1,
2819 "Intersil %d.%d.%d", sta_id.major, sta_id.minor,
2820 sta_id.variant);
2821
2822 firmver = ((unsigned long)sta_id.major << 16) |
2823 ((unsigned long)sta_id.minor << 8) | sta_id.variant;
2824
2825 priv->has_ibss = (firmver >= 0x000700); /* FIXME */
2826 priv->has_big_wep = priv->has_wep = (firmver >= 0x000800);
2827 priv->has_pm = (firmver >= 0x000700);
Christoph Hellwig95dd91f2005-06-19 01:27:56 +02002828 priv->has_hostscan = (firmver >= 0x010301);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002829
2830 if (firmver >= 0x000800)
2831 priv->ibss_port = 0;
2832 else {
2833 printk(KERN_NOTICE "%s: Intersil firmware earlier "
2834 "than v0.8.x - several features not supported\n",
2835 dev->name);
2836 priv->ibss_port = 1;
2837 }
2838 break;
2839 }
2840 printk(KERN_DEBUG "%s: Firmware determined as %s\n", dev->name,
2841 priv->fw_name);
2842
2843 return 0;
2844}
2845
2846static int orinoco_init(struct net_device *dev)
2847{
2848 struct orinoco_private *priv = netdev_priv(dev);
2849 hermes_t *hw = &priv->hw;
2850 int err = 0;
2851 struct hermes_idstring nickbuf;
2852 u16 reclen;
2853 int len;
2854
Linus Torvalds1da177e2005-04-16 15:20:36 -07002855 /* No need to lock, the hw_unavailable flag is already set in
2856 * alloc_orinocodev() */
Johannes Berg2c7060022008-10-30 22:09:54 +01002857 priv->nicbuf_size = IEEE80211_MAX_FRAME_LEN + ETH_HLEN;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002858
2859 /* Initialize the firmware */
Pavel Roskin37a6c612006-04-07 04:10:49 -04002860 err = hermes_init(hw);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002861 if (err != 0) {
2862 printk(KERN_ERR "%s: failed to initialize firmware (err = %d)\n",
2863 dev->name, err);
2864 goto out;
2865 }
2866
2867 err = determine_firmware(dev);
2868 if (err != 0) {
2869 printk(KERN_ERR "%s: Incompatible firmware, aborting\n",
2870 dev->name);
2871 goto out;
2872 }
2873
David Kilroy3994d502008-08-21 23:27:54 +01002874 if (priv->do_fw_download) {
David Kilroy39d1ffe2008-11-22 10:37:28 +00002875#ifdef CONFIG_HERMES_CACHE_FW_ON_INIT
David Kilroy74734312008-11-22 10:37:25 +00002876 orinoco_cache_fw(priv, 0);
David Kilroy39d1ffe2008-11-22 10:37:28 +00002877#endif
David Kilroy74734312008-11-22 10:37:25 +00002878
David Kilroy3994d502008-08-21 23:27:54 +01002879 err = orinoco_download(priv);
2880 if (err)
2881 priv->do_fw_download = 0;
2882
2883 /* Check firmware version again */
2884 err = determine_firmware(dev);
2885 if (err != 0) {
2886 printk(KERN_ERR "%s: Incompatible firmware, aborting\n",
2887 dev->name);
2888 goto out;
2889 }
2890 }
2891
Linus Torvalds1da177e2005-04-16 15:20:36 -07002892 if (priv->has_port3)
David Kilroyb2f30a02009-02-04 23:05:46 +00002893 printk(KERN_DEBUG "%s: Ad-hoc demo mode supported\n",
2894 dev->name);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002895 if (priv->has_ibss)
2896 printk(KERN_DEBUG "%s: IEEE standard IBSS ad-hoc mode supported\n",
2897 dev->name);
2898 if (priv->has_wep) {
David Kilroy21312662009-02-04 23:05:47 +00002899 printk(KERN_DEBUG "%s: WEP supported, %s-bit key\n", dev->name,
2900 priv->has_big_wep ? "104" : "40");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002901 }
David Kilroy23edcc42008-08-21 23:28:05 +01002902 if (priv->has_wpa) {
David Kilroyd03032a2008-08-21 23:28:02 +01002903 printk(KERN_DEBUG "%s: WPA-PSK supported\n", dev->name);
David Kilroy23edcc42008-08-21 23:28:05 +01002904 if (orinoco_mic_init(priv)) {
2905 printk(KERN_ERR "%s: Failed to setup MIC crypto "
2906 "algorithm. Disabling WPA support\n", dev->name);
2907 priv->has_wpa = 0;
2908 }
2909 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002910
David Kilroy01632fa2008-08-21 23:27:58 +01002911 /* Now we have the firmware capabilities, allocate appropiate
2912 * sized scan buffers */
2913 if (orinoco_bss_data_allocate(priv))
2914 goto out;
2915 orinoco_bss_data_init(priv);
2916
Linus Torvalds1da177e2005-04-16 15:20:36 -07002917 /* Get the MAC address */
2918 err = hermes_read_ltv(hw, USER_BAP, HERMES_RID_CNFOWNMACADDR,
2919 ETH_ALEN, NULL, dev->dev_addr);
2920 if (err) {
2921 printk(KERN_WARNING "%s: failed to read MAC address!\n",
2922 dev->name);
2923 goto out;
2924 }
2925
Johannes Berge1749612008-10-27 15:59:26 -07002926 printk(KERN_DEBUG "%s: MAC address %pM\n",
2927 dev->name, dev->dev_addr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002928
2929 /* Get the station name */
2930 err = hermes_read_ltv(hw, USER_BAP, HERMES_RID_CNFOWNNAME,
2931 sizeof(nickbuf), &reclen, &nickbuf);
2932 if (err) {
2933 printk(KERN_ERR "%s: failed to read station name\n",
2934 dev->name);
2935 goto out;
2936 }
2937 if (nickbuf.len)
2938 len = min(IW_ESSID_MAX_SIZE, (int)le16_to_cpu(nickbuf.len));
2939 else
2940 len = min(IW_ESSID_MAX_SIZE, 2 * reclen);
2941 memcpy(priv->nick, &nickbuf.val, len);
2942 priv->nick[len] = '\0';
2943
2944 printk(KERN_DEBUG "%s: Station name \"%s\"\n", dev->name, priv->nick);
2945
Pavel Roskin37a6c612006-04-07 04:10:49 -04002946 err = orinoco_allocate_fid(dev);
2947 if (err) {
2948 printk(KERN_ERR "%s: failed to allocate NIC buffer!\n",
2949 dev->name);
2950 goto out;
2951 }
2952
Linus Torvalds1da177e2005-04-16 15:20:36 -07002953 /* Get allowed channels */
2954 err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_CHANNELLIST,
2955 &priv->channel_mask);
2956 if (err) {
2957 printk(KERN_ERR "%s: failed to read channel list!\n",
2958 dev->name);
2959 goto out;
2960 }
2961
2962 /* Get initial AP density */
2963 err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_CNFSYSTEMSCALE,
2964 &priv->ap_density);
David Kilroy566f2d92009-02-04 23:05:45 +00002965 if (err || priv->ap_density < 1 || priv->ap_density > 3)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002966 priv->has_sensitivity = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002967
2968 /* Get initial RTS threshold */
2969 err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_CNFRTSTHRESHOLD,
2970 &priv->rts_thresh);
2971 if (err) {
2972 printk(KERN_ERR "%s: failed to read RTS threshold!\n",
2973 dev->name);
2974 goto out;
2975 }
2976
2977 /* Get initial fragmentation settings */
2978 if (priv->has_mwo)
2979 err = hermes_read_wordrec(hw, USER_BAP,
2980 HERMES_RID_CNFMWOROBUST_AGERE,
2981 &priv->mwo_robust);
2982 else
David Kilroyb2f30a02009-02-04 23:05:46 +00002983 err = hermes_read_wordrec(hw, USER_BAP,
2984 HERMES_RID_CNFFRAGMENTATIONTHRESHOLD,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002985 &priv->frag_thresh);
2986 if (err) {
2987 printk(KERN_ERR "%s: failed to read fragmentation settings!\n",
2988 dev->name);
2989 goto out;
2990 }
2991
2992 /* Power management setup */
2993 if (priv->has_pm) {
2994 priv->pm_on = 0;
2995 priv->pm_mcast = 1;
2996 err = hermes_read_wordrec(hw, USER_BAP,
2997 HERMES_RID_CNFMAXSLEEPDURATION,
2998 &priv->pm_period);
2999 if (err) {
3000 printk(KERN_ERR "%s: failed to read power management period!\n",
3001 dev->name);
3002 goto out;
3003 }
3004 err = hermes_read_wordrec(hw, USER_BAP,
3005 HERMES_RID_CNFPMHOLDOVERDURATION,
3006 &priv->pm_timeout);
3007 if (err) {
3008 printk(KERN_ERR "%s: failed to read power management timeout!\n",
3009 dev->name);
3010 goto out;
3011 }
3012 }
3013
3014 /* Preamble setup */
3015 if (priv->has_preamble) {
3016 err = hermes_read_wordrec(hw, USER_BAP,
3017 HERMES_RID_CNFPREAMBLE_SYMBOL,
3018 &priv->preamble);
3019 if (err)
3020 goto out;
3021 }
David Kilroy6fe9deb2009-02-04 23:05:43 +00003022
Linus Torvalds1da177e2005-04-16 15:20:36 -07003023 /* Set up the default configuration */
3024 priv->iw_mode = IW_MODE_INFRA;
3025 /* By default use IEEE/IBSS ad-hoc mode if we have it */
David Kilroya94e8422009-02-04 23:05:44 +00003026 priv->prefer_port3 = priv->has_port3 && (!priv->has_ibss);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003027 set_port_type(priv);
David Gibsond51d8b12005-05-12 20:03:36 -04003028 priv->channel = 0; /* use firmware default */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003029
3030 priv->promiscuous = 0;
David Kilroy4ae6ee22008-08-21 23:27:59 +01003031 priv->encode_alg = IW_ENCODE_ALG_NONE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003032 priv->tx_key = 0;
David Kilroyd03032a2008-08-21 23:28:02 +01003033 priv->wpa_enabled = 0;
3034 priv->tkip_cm_active = 0;
3035 priv->key_mgmt = 0;
3036 priv->wpa_ie_len = 0;
3037 priv->wpa_ie = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003038
Linus Torvalds1da177e2005-04-16 15:20:36 -07003039 /* Make the hardware available, as long as it hasn't been
3040 * removed elsewhere (e.g. by PCMCIA hot unplug) */
3041 spin_lock_irq(&priv->lock);
3042 priv->hw_unavailable--;
3043 spin_unlock_irq(&priv->lock);
3044
3045 printk(KERN_DEBUG "%s: ready\n", dev->name);
3046
3047 out:
Linus Torvalds1da177e2005-04-16 15:20:36 -07003048 return err;
3049}
3050
Andrey Borzenkov89ea4092009-01-21 20:46:46 +03003051static const struct net_device_ops orinoco_netdev_ops = {
3052 .ndo_init = orinoco_init,
3053 .ndo_open = orinoco_open,
3054 .ndo_stop = orinoco_stop,
3055 .ndo_start_xmit = orinoco_xmit,
3056 .ndo_set_multicast_list = orinoco_set_multicast_list,
3057 .ndo_change_mtu = orinoco_change_mtu,
3058 .ndo_tx_timeout = orinoco_tx_timeout,
3059 .ndo_get_stats = orinoco_get_stats,
3060};
3061
David Kilroy3994d502008-08-21 23:27:54 +01003062struct net_device
3063*alloc_orinocodev(int sizeof_card,
3064 struct device *device,
3065 int (*hard_reset)(struct orinoco_private *),
3066 int (*stop_fw)(struct orinoco_private *, int))
Linus Torvalds1da177e2005-04-16 15:20:36 -07003067{
3068 struct net_device *dev;
3069 struct orinoco_private *priv;
3070
3071 dev = alloc_etherdev(sizeof(struct orinoco_private) + sizeof_card);
Andrey Borzenkove129a942009-01-21 21:55:29 +03003072 if (!dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003073 return NULL;
3074 priv = netdev_priv(dev);
3075 priv->ndev = dev;
3076 if (sizeof_card)
Christoph Hellwig84d8a2f2005-05-14 17:30:22 +02003077 priv->card = (void *)((unsigned long)priv
Linus Torvalds1da177e2005-04-16 15:20:36 -07003078 + sizeof(struct orinoco_private));
3079 else
3080 priv->card = NULL;
David Kilroy3994d502008-08-21 23:27:54 +01003081 priv->dev = device;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003082
3083 /* Setup / override net_device fields */
Andrey Borzenkov89ea4092009-01-21 20:46:46 +03003084 dev->netdev_ops = &orinoco_netdev_ops;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003085 dev->watchdog_timeo = HZ; /* 1 second timeout */
Christoph Hellwig1fab2e82005-06-19 01:27:40 +02003086 dev->ethtool_ops = &orinoco_ethtool_ops;
Andrey Borzenkove129a942009-01-21 21:55:29 +03003087 dev->wireless_handlers = &orinoco_handler_def;
Pavel Roskin343c6862005-09-09 18:43:02 -04003088#ifdef WIRELESS_SPY
3089 priv->wireless_data.spy_data = &priv->spy_data;
3090 dev->wireless_data = &priv->wireless_data;
3091#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -07003092 /* we use the default eth_mac_addr for setting the MAC addr */
3093
David Kilroy23edcc42008-08-21 23:28:05 +01003094 /* Reserve space in skb for the SNAP header */
3095 dev->hard_header_len += ENCAPS_OVERHEAD;
3096
Linus Torvalds1da177e2005-04-16 15:20:36 -07003097 /* Set up default callbacks */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003098 priv->hard_reset = hard_reset;
David Kilroy3994d502008-08-21 23:27:54 +01003099 priv->stop_fw = stop_fw;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003100
3101 spin_lock_init(&priv->lock);
3102 priv->open = 0;
3103 priv->hw_unavailable = 1; /* orinoco_init() must clear this
3104 * before anything else touches the
3105 * hardware */
David Howellsc4028952006-11-22 14:57:56 +00003106 INIT_WORK(&priv->reset_work, orinoco_reset);
3107 INIT_WORK(&priv->join_work, orinoco_join_ap);
3108 INIT_WORK(&priv->wevent_work, orinoco_send_wevents);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003109
David Kilroy31afcef2008-08-21 23:28:04 +01003110 INIT_LIST_HEAD(&priv->rx_list);
3111 tasklet_init(&priv->rx_tasklet, orinoco_rx_isr_tasklet,
3112 (unsigned long) dev);
3113
Linus Torvalds1da177e2005-04-16 15:20:36 -07003114 netif_carrier_off(dev);
3115 priv->last_linkstatus = 0xffff;
3116
David Kilroy2cea7b22008-11-22 10:37:26 +00003117 priv->cached_pri_fw = NULL;
Andrey Borzenkov4fb30782008-10-19 12:06:11 +04003118 priv->cached_fw = NULL;
3119
David Kilroy39d1ffe2008-11-22 10:37:28 +00003120 /* Register PM notifiers */
3121 priv->pm_notifier.notifier_call = orinoco_pm_notifier;
3122 register_pm_notifier(&priv->pm_notifier);
3123
Linus Torvalds1da177e2005-04-16 15:20:36 -07003124 return dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003125}
David Kilroy21312662009-02-04 23:05:47 +00003126EXPORT_SYMBOL(alloc_orinocodev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003127
3128void free_orinocodev(struct net_device *dev)
3129{
Christoph Hellwig95dd91f2005-06-19 01:27:56 +02003130 struct orinoco_private *priv = netdev_priv(dev);
David Kilroy20953ad2009-01-07 00:23:55 +00003131 struct orinoco_rx_data *rx_data, *temp;
Christoph Hellwig95dd91f2005-06-19 01:27:56 +02003132
David Kilroy20953ad2009-01-07 00:23:55 +00003133 /* If the tasklet is scheduled when we call tasklet_kill it
3134 * will run one final time. However the tasklet will only
3135 * drain priv->rx_list if the hw is still available. */
David Kilroy31afcef2008-08-21 23:28:04 +01003136 tasklet_kill(&priv->rx_tasklet);
David Kilroy74734312008-11-22 10:37:25 +00003137
David Kilroy20953ad2009-01-07 00:23:55 +00003138 /* Explicitly drain priv->rx_list */
3139 list_for_each_entry_safe(rx_data, temp, &priv->rx_list, list) {
3140 list_del(&rx_data->list);
3141
3142 dev_kfree_skb(rx_data->skb);
3143 kfree(rx_data->desc);
3144 kfree(rx_data);
3145 }
3146
David Kilroy39d1ffe2008-11-22 10:37:28 +00003147 unregister_pm_notifier(&priv->pm_notifier);
David Kilroy74734312008-11-22 10:37:25 +00003148 orinoco_uncache_fw(priv);
3149
David Kilroyd03032a2008-08-21 23:28:02 +01003150 priv->wpa_ie_len = 0;
3151 kfree(priv->wpa_ie);
David Kilroy23edcc42008-08-21 23:28:05 +01003152 orinoco_mic_free(priv);
Dan Williams1e3428e2007-10-10 23:56:25 -04003153 orinoco_bss_data_free(priv);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003154 free_netdev(dev);
3155}
David Kilroy21312662009-02-04 23:05:47 +00003156EXPORT_SYMBOL(free_orinocodev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003157
3158/********************************************************************/
3159/* Wireless extensions */
3160/********************************************************************/
3161
Jean Tourrilhes7e4e8d92006-10-10 14:45:44 -07003162/* Return : < 0 -> error code ; >= 0 -> length */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003163static int orinoco_hw_get_essid(struct orinoco_private *priv, int *active,
3164 char buf[IW_ESSID_MAX_SIZE+1])
3165{
3166 hermes_t *hw = &priv->hw;
3167 int err = 0;
3168 struct hermes_idstring essidbuf;
3169 char *p = (char *)(&essidbuf.val);
3170 int len;
3171 unsigned long flags;
3172
3173 if (orinoco_lock(priv, &flags) != 0)
3174 return -EBUSY;
3175
3176 if (strlen(priv->desired_essid) > 0) {
3177 /* We read the desired SSID from the hardware rather
3178 than from priv->desired_essid, just in case the
3179 firmware is allowed to change it on us. I'm not
3180 sure about this */
3181 /* My guess is that the OWNSSID should always be whatever
3182 * we set to the card, whereas CURRENT_SSID is the one that
3183 * may change... - Jean II */
3184 u16 rid;
3185
3186 *active = 1;
3187
3188 rid = (priv->port_type == 3) ? HERMES_RID_CNFOWNSSID :
3189 HERMES_RID_CNFDESIREDSSID;
David Kilroy6fe9deb2009-02-04 23:05:43 +00003190
Linus Torvalds1da177e2005-04-16 15:20:36 -07003191 err = hermes_read_ltv(hw, USER_BAP, rid, sizeof(essidbuf),
3192 NULL, &essidbuf);
3193 if (err)
3194 goto fail_unlock;
3195 } else {
3196 *active = 0;
3197
3198 err = hermes_read_ltv(hw, USER_BAP, HERMES_RID_CURRENTSSID,
3199 sizeof(essidbuf), NULL, &essidbuf);
3200 if (err)
3201 goto fail_unlock;
3202 }
3203
3204 len = le16_to_cpu(essidbuf.len);
Christoph Hellwig84d8a2f2005-05-14 17:30:22 +02003205 BUG_ON(len > IW_ESSID_MAX_SIZE);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003206
Jean Tourrilhes7e4e8d92006-10-10 14:45:44 -07003207 memset(buf, 0, IW_ESSID_MAX_SIZE);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003208 memcpy(buf, p, len);
Jean Tourrilhes7e4e8d92006-10-10 14:45:44 -07003209 err = len;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003210
3211 fail_unlock:
3212 orinoco_unlock(priv, &flags);
3213
David Kilroy6fe9deb2009-02-04 23:05:43 +00003214 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003215}
3216
David Kilroy9ee677c2008-12-23 14:03:38 +00003217static int orinoco_hw_get_freq(struct orinoco_private *priv)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003218{
David Kilroy6fe9deb2009-02-04 23:05:43 +00003219
Linus Torvalds1da177e2005-04-16 15:20:36 -07003220 hermes_t *hw = &priv->hw;
3221 int err = 0;
3222 u16 channel;
David Kilroy9ee677c2008-12-23 14:03:38 +00003223 int freq = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003224 unsigned long flags;
3225
3226 if (orinoco_lock(priv, &flags) != 0)
3227 return -EBUSY;
David Kilroy6fe9deb2009-02-04 23:05:43 +00003228
David Kilroyb2f30a02009-02-04 23:05:46 +00003229 err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_CURRENTCHANNEL,
3230 &channel);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003231 if (err)
3232 goto out;
3233
3234 /* Intersil firmware 1.3.5 returns 0 when the interface is down */
3235 if (channel == 0) {
3236 err = -EBUSY;
3237 goto out;
3238 }
3239
David Kilroya94e8422009-02-04 23:05:44 +00003240 if ((channel < 1) || (channel > NUM_CHANNELS)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07003241 printk(KERN_WARNING "%s: Channel out of range (%d)!\n",
3242 priv->ndev->name, channel);
3243 err = -EBUSY;
3244 goto out;
3245
3246 }
David Kilroy9ee677c2008-12-23 14:03:38 +00003247 freq = ieee80211_dsss_chan_to_freq(channel);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003248
3249 out:
3250 orinoco_unlock(priv, &flags);
3251
3252 if (err > 0)
3253 err = -EBUSY;
3254 return err ? err : freq;
3255}
3256
3257static int orinoco_hw_get_bitratelist(struct orinoco_private *priv,
3258 int *numrates, s32 *rates, int max)
3259{
3260 hermes_t *hw = &priv->hw;
3261 struct hermes_idstring list;
3262 unsigned char *p = (unsigned char *)&list.val;
3263 int err = 0;
3264 int num;
3265 int i;
3266 unsigned long flags;
3267
3268 if (orinoco_lock(priv, &flags) != 0)
3269 return -EBUSY;
3270
3271 err = hermes_read_ltv(hw, USER_BAP, HERMES_RID_SUPPORTEDDATARATES,
3272 sizeof(list), NULL, &list);
3273 orinoco_unlock(priv, &flags);
3274
3275 if (err)
3276 return err;
David Kilroy6fe9deb2009-02-04 23:05:43 +00003277
Linus Torvalds1da177e2005-04-16 15:20:36 -07003278 num = le16_to_cpu(list.len);
3279 *numrates = num;
3280 num = min(num, max);
3281
David Kilroy566f2d92009-02-04 23:05:45 +00003282 for (i = 0; i < num; i++)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003283 rates[i] = (p[i] & 0x7f) * 500000; /* convert to bps */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003284
3285 return 0;
3286}
3287
Christoph Hellwig620554e2005-06-19 01:27:33 +02003288static int orinoco_ioctl_getname(struct net_device *dev,
3289 struct iw_request_info *info,
3290 char *name,
3291 char *extra)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003292{
3293 struct orinoco_private *priv = netdev_priv(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003294 int numrates;
Christoph Hellwig620554e2005-06-19 01:27:33 +02003295 int err;
3296
3297 err = orinoco_hw_get_bitratelist(priv, &numrates, NULL, 0);
3298
3299 if (!err && (numrates > 2))
3300 strcpy(name, "IEEE 802.11b");
3301 else
3302 strcpy(name, "IEEE 802.11-DS");
3303
3304 return 0;
3305}
3306
Christoph Hellwig16739b02005-06-19 01:27:51 +02003307static int orinoco_ioctl_setwap(struct net_device *dev,
3308 struct iw_request_info *info,
3309 struct sockaddr *ap_addr,
3310 char *extra)
3311{
3312 struct orinoco_private *priv = netdev_priv(dev);
3313 int err = -EINPROGRESS; /* Call commit handler */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003314 unsigned long flags;
Christoph Hellwig16739b02005-06-19 01:27:51 +02003315 static const u8 off_addr[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
3316 static const u8 any_addr[] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
Linus Torvalds1da177e2005-04-16 15:20:36 -07003317
3318 if (orinoco_lock(priv, &flags) != 0)
3319 return -EBUSY;
3320
Christoph Hellwig16739b02005-06-19 01:27:51 +02003321 /* Enable automatic roaming - no sanity checks are needed */
3322 if (memcmp(&ap_addr->sa_data, off_addr, ETH_ALEN) == 0 ||
3323 memcmp(&ap_addr->sa_data, any_addr, ETH_ALEN) == 0) {
3324 priv->bssid_fixed = 0;
3325 memset(priv->desired_bssid, 0, ETH_ALEN);
3326
3327 /* "off" means keep existing connection */
3328 if (ap_addr->sa_data[0] == 0) {
3329 __orinoco_hw_set_wap(priv);
3330 err = 0;
3331 }
3332 goto out;
3333 }
3334
3335 if (priv->firmware_type == FIRMWARE_TYPE_AGERE) {
3336 printk(KERN_WARNING "%s: Lucent/Agere firmware doesn't "
3337 "support manual roaming\n",
3338 dev->name);
3339 err = -EOPNOTSUPP;
3340 goto out;
3341 }
3342
3343 if (priv->iw_mode != IW_MODE_INFRA) {
3344 printk(KERN_WARNING "%s: Manual roaming supported only in "
3345 "managed mode\n", dev->name);
3346 err = -EOPNOTSUPP;
3347 goto out;
3348 }
3349
3350 /* Intersil firmware hangs without Desired ESSID */
3351 if (priv->firmware_type == FIRMWARE_TYPE_INTERSIL &&
3352 strlen(priv->desired_essid) == 0) {
3353 printk(KERN_WARNING "%s: Desired ESSID must be set for "
3354 "manual roaming\n", dev->name);
3355 err = -EOPNOTSUPP;
3356 goto out;
3357 }
3358
3359 /* Finally, enable manual roaming */
3360 priv->bssid_fixed = 1;
3361 memcpy(priv->desired_bssid, &ap_addr->sa_data, ETH_ALEN);
3362
3363 out:
3364 orinoco_unlock(priv, &flags);
3365 return err;
3366}
3367
Christoph Hellwig620554e2005-06-19 01:27:33 +02003368static int orinoco_ioctl_getwap(struct net_device *dev,
3369 struct iw_request_info *info,
3370 struct sockaddr *ap_addr,
3371 char *extra)
3372{
3373 struct orinoco_private *priv = netdev_priv(dev);
3374
3375 hermes_t *hw = &priv->hw;
3376 int err = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003377 unsigned long flags;
3378
Linus Torvalds1da177e2005-04-16 15:20:36 -07003379 if (orinoco_lock(priv, &flags) != 0)
3380 return -EBUSY;
3381
Christoph Hellwig620554e2005-06-19 01:27:33 +02003382 ap_addr->sa_family = ARPHRD_ETHER;
3383 err = hermes_read_ltv(hw, USER_BAP, HERMES_RID_CURRENTBSSID,
3384 ETH_ALEN, NULL, ap_addr->sa_data);
3385
Linus Torvalds1da177e2005-04-16 15:20:36 -07003386 orinoco_unlock(priv, &flags);
3387
Christoph Hellwig620554e2005-06-19 01:27:33 +02003388 return err;
3389}
Linus Torvalds1da177e2005-04-16 15:20:36 -07003390
Christoph Hellwig620554e2005-06-19 01:27:33 +02003391static int orinoco_ioctl_setmode(struct net_device *dev,
3392 struct iw_request_info *info,
3393 u32 *mode,
3394 char *extra)
3395{
3396 struct orinoco_private *priv = netdev_priv(dev);
3397 int err = -EINPROGRESS; /* Call commit handler */
3398 unsigned long flags;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003399
Christoph Hellwig620554e2005-06-19 01:27:33 +02003400 if (priv->iw_mode == *mode)
3401 return 0;
3402
3403 if (orinoco_lock(priv, &flags) != 0)
3404 return -EBUSY;
3405
3406 switch (*mode) {
3407 case IW_MODE_ADHOC:
3408 if (!priv->has_ibss && !priv->has_port3)
3409 err = -EOPNOTSUPP;
3410 break;
3411
3412 case IW_MODE_INFRA:
3413 break;
3414
Christoph Hellwig98c4cae2005-06-19 01:28:06 +02003415 case IW_MODE_MONITOR:
3416 if (priv->broken_monitor && !force_monitor) {
3417 printk(KERN_WARNING "%s: Monitor mode support is "
3418 "buggy in this firmware, not enabling\n",
3419 dev->name);
3420 err = -EOPNOTSUPP;
3421 }
3422 break;
3423
Christoph Hellwig620554e2005-06-19 01:27:33 +02003424 default:
3425 err = -EOPNOTSUPP;
3426 break;
3427 }
3428
3429 if (err == -EINPROGRESS) {
3430 priv->iw_mode = *mode;
3431 set_port_type(priv);
3432 }
3433
3434 orinoco_unlock(priv, &flags);
3435
3436 return err;
3437}
3438
3439static int orinoco_ioctl_getmode(struct net_device *dev,
3440 struct iw_request_info *info,
3441 u32 *mode,
3442 char *extra)
3443{
3444 struct orinoco_private *priv = netdev_priv(dev);
3445
3446 *mode = priv->iw_mode;
3447 return 0;
3448}
3449
3450static int orinoco_ioctl_getiwrange(struct net_device *dev,
3451 struct iw_request_info *info,
3452 struct iw_point *rrq,
3453 char *extra)
3454{
3455 struct orinoco_private *priv = netdev_priv(dev);
3456 int err = 0;
3457 struct iw_range *range = (struct iw_range *) extra;
3458 int numrates;
3459 int i, k;
3460
Christoph Hellwig620554e2005-06-19 01:27:33 +02003461 rrq->length = sizeof(struct iw_range);
3462 memset(range, 0, sizeof(struct iw_range));
3463
3464 range->we_version_compiled = WIRELESS_EXT;
David Kilroyd03032a2008-08-21 23:28:02 +01003465 range->we_version_source = 22;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003466
3467 /* Set available channels/frequencies */
Christoph Hellwig620554e2005-06-19 01:27:33 +02003468 range->num_channels = NUM_CHANNELS;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003469 k = 0;
3470 for (i = 0; i < NUM_CHANNELS; i++) {
3471 if (priv->channel_mask & (1 << i)) {
Christoph Hellwig620554e2005-06-19 01:27:33 +02003472 range->freq[k].i = i + 1;
David Kilroy9ee677c2008-12-23 14:03:38 +00003473 range->freq[k].m = (ieee80211_dsss_chan_to_freq(i + 1) *
3474 100000);
Christoph Hellwig620554e2005-06-19 01:27:33 +02003475 range->freq[k].e = 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003476 k++;
3477 }
David Kilroy6fe9deb2009-02-04 23:05:43 +00003478
Linus Torvalds1da177e2005-04-16 15:20:36 -07003479 if (k >= IW_MAX_FREQUENCIES)
3480 break;
3481 }
Christoph Hellwig620554e2005-06-19 01:27:33 +02003482 range->num_frequency = k;
3483 range->sensitivity = 3;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003484
Christoph Hellwig620554e2005-06-19 01:27:33 +02003485 if (priv->has_wep) {
3486 range->max_encoding_tokens = ORINOCO_MAX_KEYS;
3487 range->encoding_size[0] = SMALL_KEY_SIZE;
3488 range->num_encoding_sizes = 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003489
Christoph Hellwig620554e2005-06-19 01:27:33 +02003490 if (priv->has_big_wep) {
3491 range->encoding_size[1] = LARGE_KEY_SIZE;
3492 range->num_encoding_sizes = 2;
3493 }
3494 }
3495
David Kilroyd03032a2008-08-21 23:28:02 +01003496 if (priv->has_wpa)
3497 range->enc_capa = IW_ENC_CAPA_WPA | IW_ENC_CAPA_CIPHER_TKIP;
3498
David Kilroya94e8422009-02-04 23:05:44 +00003499 if ((priv->iw_mode == IW_MODE_ADHOC) && (!SPY_NUMBER(priv))) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07003500 /* Quality stats meaningless in ad-hoc mode */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003501 } else {
Christoph Hellwig620554e2005-06-19 01:27:33 +02003502 range->max_qual.qual = 0x8b - 0x2f;
3503 range->max_qual.level = 0x2f - 0x95 - 1;
3504 range->max_qual.noise = 0x2f - 0x95 - 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003505 /* Need to get better values */
Christoph Hellwig620554e2005-06-19 01:27:33 +02003506 range->avg_qual.qual = 0x24;
3507 range->avg_qual.level = 0xC2;
3508 range->avg_qual.noise = 0x9E;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003509 }
3510
3511 err = orinoco_hw_get_bitratelist(priv, &numrates,
Christoph Hellwig620554e2005-06-19 01:27:33 +02003512 range->bitrate, IW_MAX_BITRATES);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003513 if (err)
3514 return err;
Christoph Hellwig620554e2005-06-19 01:27:33 +02003515 range->num_bitrates = numrates;
3516
Linus Torvalds1da177e2005-04-16 15:20:36 -07003517 /* Set an indication of the max TCP throughput in bit/s that we can
3518 * expect using this interface. May be use for QoS stuff...
3519 * Jean II */
Christoph Hellwig620554e2005-06-19 01:27:33 +02003520 if (numrates > 2)
3521 range->throughput = 5 * 1000 * 1000; /* ~5 Mb/s */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003522 else
Christoph Hellwig620554e2005-06-19 01:27:33 +02003523 range->throughput = 1.5 * 1000 * 1000; /* ~1.5 Mb/s */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003524
Christoph Hellwig620554e2005-06-19 01:27:33 +02003525 range->min_rts = 0;
3526 range->max_rts = 2347;
3527 range->min_frag = 256;
3528 range->max_frag = 2346;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003529
Christoph Hellwig620554e2005-06-19 01:27:33 +02003530 range->min_pmp = 0;
3531 range->max_pmp = 65535000;
3532 range->min_pmt = 0;
3533 range->max_pmt = 65535 * 1000; /* ??? */
3534 range->pmp_flags = IW_POWER_PERIOD;
3535 range->pmt_flags = IW_POWER_TIMEOUT;
David Kilroyb2f30a02009-02-04 23:05:46 +00003536 range->pm_capa = (IW_POWER_PERIOD | IW_POWER_TIMEOUT |
3537 IW_POWER_UNICAST_R);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003538
Christoph Hellwig620554e2005-06-19 01:27:33 +02003539 range->retry_capa = IW_RETRY_LIMIT | IW_RETRY_LIFETIME;
3540 range->retry_flags = IW_RETRY_LIMIT;
3541 range->r_time_flags = IW_RETRY_LIFETIME;
3542 range->min_retry = 0;
3543 range->max_retry = 65535; /* ??? */
3544 range->min_r_time = 0;
3545 range->max_r_time = 65535 * 1000; /* ??? */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003546
David Kilroy0753bba2008-08-21 23:27:46 +01003547 if (priv->firmware_type == FIRMWARE_TYPE_AGERE)
3548 range->scan_capa = IW_SCAN_CAPA_ESSID;
3549 else
3550 range->scan_capa = IW_SCAN_CAPA_NONE;
3551
Pavel Roskin343c6862005-09-09 18:43:02 -04003552 /* Event capability (kernel) */
3553 IW_EVENT_CAPA_SET_KERNEL(range->event_capa);
3554 /* Event capability (driver) */
3555 IW_EVENT_CAPA_SET(range->event_capa, SIOCGIWTHRSPY);
3556 IW_EVENT_CAPA_SET(range->event_capa, SIOCGIWAP);
3557 IW_EVENT_CAPA_SET(range->event_capa, SIOCGIWSCAN);
3558 IW_EVENT_CAPA_SET(range->event_capa, IWEVTXDROP);
3559
Linus Torvalds1da177e2005-04-16 15:20:36 -07003560 return 0;
3561}
3562
Christoph Hellwig620554e2005-06-19 01:27:33 +02003563static int orinoco_ioctl_setiwencode(struct net_device *dev,
3564 struct iw_request_info *info,
3565 struct iw_point *erq,
3566 char *keybuf)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003567{
3568 struct orinoco_private *priv = netdev_priv(dev);
3569 int index = (erq->flags & IW_ENCODE_INDEX) - 1;
3570 int setindex = priv->tx_key;
David Kilroy4ae6ee22008-08-21 23:27:59 +01003571 int encode_alg = priv->encode_alg;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003572 int restricted = priv->wep_restrict;
3573 u16 xlen = 0;
Christoph Hellwig620554e2005-06-19 01:27:33 +02003574 int err = -EINPROGRESS; /* Call commit handler */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003575 unsigned long flags;
3576
David Kilroya94e8422009-02-04 23:05:44 +00003577 if (!priv->has_wep)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003578 return -EOPNOTSUPP;
3579
3580 if (erq->pointer) {
3581 /* We actually have a key to set - check its length */
3582 if (erq->length > LARGE_KEY_SIZE)
3583 return -E2BIG;
3584
David Kilroya94e8422009-02-04 23:05:44 +00003585 if ((erq->length > SMALL_KEY_SIZE) && !priv->has_big_wep)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003586 return -E2BIG;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003587 }
3588
3589 if (orinoco_lock(priv, &flags) != 0)
3590 return -EBUSY;
3591
David Kilroyd03032a2008-08-21 23:28:02 +01003592 /* Clear any TKIP key we have */
3593 if ((priv->has_wpa) && (priv->encode_alg == IW_ENCODE_ALG_TKIP))
3594 (void) orinoco_clear_tkip_key(priv, setindex);
3595
Dan Williamsfe397d42006-07-14 11:41:47 -04003596 if (erq->length > 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07003597 if ((index < 0) || (index >= ORINOCO_MAX_KEYS))
3598 index = priv->tx_key;
3599
3600 /* Adjust key length to a supported value */
David Kilroy566f2d92009-02-04 23:05:45 +00003601 if (erq->length > SMALL_KEY_SIZE)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003602 xlen = LARGE_KEY_SIZE;
David Kilroy566f2d92009-02-04 23:05:45 +00003603 else if (erq->length > 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003604 xlen = SMALL_KEY_SIZE;
David Kilroy566f2d92009-02-04 23:05:45 +00003605 else
Linus Torvalds1da177e2005-04-16 15:20:36 -07003606 xlen = 0;
3607
3608 /* Switch on WEP if off */
David Kilroy4ae6ee22008-08-21 23:27:59 +01003609 if ((encode_alg != IW_ENCODE_ALG_WEP) && (xlen > 0)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07003610 setindex = index;
David Kilroy4ae6ee22008-08-21 23:27:59 +01003611 encode_alg = IW_ENCODE_ALG_WEP;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003612 }
3613 } else {
3614 /* Important note : if the user do "iwconfig eth0 enc off",
3615 * we will arrive there with an index of -1. This is valid
3616 * but need to be taken care off... Jean II */
3617 if ((index < 0) || (index >= ORINOCO_MAX_KEYS)) {
David Kilroya94e8422009-02-04 23:05:44 +00003618 if ((index != -1) || (erq->flags == 0)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07003619 err = -EINVAL;
3620 goto out;
3621 }
3622 } else {
3623 /* Set the index : Check that the key is valid */
David Kilroya94e8422009-02-04 23:05:44 +00003624 if (priv->keys[index].len == 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07003625 err = -EINVAL;
3626 goto out;
3627 }
3628 setindex = index;
3629 }
3630 }
3631
3632 if (erq->flags & IW_ENCODE_DISABLED)
David Kilroy4ae6ee22008-08-21 23:27:59 +01003633 encode_alg = IW_ENCODE_ALG_NONE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003634 if (erq->flags & IW_ENCODE_OPEN)
3635 restricted = 0;
3636 if (erq->flags & IW_ENCODE_RESTRICTED)
3637 restricted = 1;
3638
Dan Williamsfe397d42006-07-14 11:41:47 -04003639 if (erq->pointer && erq->length > 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07003640 priv->keys[index].len = cpu_to_le16(xlen);
3641 memset(priv->keys[index].data, 0,
3642 sizeof(priv->keys[index].data));
3643 memcpy(priv->keys[index].data, keybuf, erq->length);
3644 }
3645 priv->tx_key = setindex;
3646
3647 /* Try fast key change if connected and only keys are changed */
David Kilroy4ae6ee22008-08-21 23:27:59 +01003648 if ((priv->encode_alg == encode_alg) &&
3649 (priv->wep_restrict == restricted) &&
Linus Torvalds1da177e2005-04-16 15:20:36 -07003650 netif_carrier_ok(dev)) {
3651 err = __orinoco_hw_setup_wepkeys(priv);
3652 /* No need to commit if successful */
3653 goto out;
3654 }
3655
David Kilroy4ae6ee22008-08-21 23:27:59 +01003656 priv->encode_alg = encode_alg;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003657 priv->wep_restrict = restricted;
3658
3659 out:
3660 orinoco_unlock(priv, &flags);
3661
3662 return err;
3663}
3664
Christoph Hellwig620554e2005-06-19 01:27:33 +02003665static int orinoco_ioctl_getiwencode(struct net_device *dev,
3666 struct iw_request_info *info,
3667 struct iw_point *erq,
3668 char *keybuf)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003669{
3670 struct orinoco_private *priv = netdev_priv(dev);
3671 int index = (erq->flags & IW_ENCODE_INDEX) - 1;
3672 u16 xlen = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003673 unsigned long flags;
3674
David Kilroya94e8422009-02-04 23:05:44 +00003675 if (!priv->has_wep)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003676 return -EOPNOTSUPP;
3677
3678 if (orinoco_lock(priv, &flags) != 0)
3679 return -EBUSY;
3680
3681 if ((index < 0) || (index >= ORINOCO_MAX_KEYS))
3682 index = priv->tx_key;
3683
3684 erq->flags = 0;
David Kilroy4ae6ee22008-08-21 23:27:59 +01003685 if (!priv->encode_alg)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003686 erq->flags |= IW_ENCODE_DISABLED;
3687 erq->flags |= index + 1;
3688
3689 if (priv->wep_restrict)
3690 erq->flags |= IW_ENCODE_RESTRICTED;
3691 else
3692 erq->flags |= IW_ENCODE_OPEN;
3693
3694 xlen = le16_to_cpu(priv->keys[index].len);
3695
3696 erq->length = xlen;
3697
3698 memcpy(keybuf, priv->keys[index].data, ORINOCO_MAX_KEY_SIZE);
3699
3700 orinoco_unlock(priv, &flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003701 return 0;
3702}
3703
Christoph Hellwig620554e2005-06-19 01:27:33 +02003704static int orinoco_ioctl_setessid(struct net_device *dev,
3705 struct iw_request_info *info,
3706 struct iw_point *erq,
3707 char *essidbuf)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003708{
3709 struct orinoco_private *priv = netdev_priv(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003710 unsigned long flags;
3711
3712 /* Note : ESSID is ignored in Ad-Hoc demo mode, but we can set it
3713 * anyway... - Jean II */
3714
Christoph Hellwig620554e2005-06-19 01:27:33 +02003715 /* Hum... Should not use Wireless Extension constant (may change),
3716 * should use our own... - Jean II */
3717 if (erq->length > IW_ESSID_MAX_SIZE)
3718 return -E2BIG;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003719
3720 if (orinoco_lock(priv, &flags) != 0)
3721 return -EBUSY;
3722
Christoph Hellwig620554e2005-06-19 01:27:33 +02003723 /* NULL the string (for NULL termination & ESSID = ANY) - Jean II */
3724 memset(priv->desired_essid, 0, sizeof(priv->desired_essid));
3725
3726 /* If not ANY, get the new ESSID */
David Kilroy566f2d92009-02-04 23:05:45 +00003727 if (erq->flags)
Christoph Hellwig620554e2005-06-19 01:27:33 +02003728 memcpy(priv->desired_essid, essidbuf, erq->length);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003729
3730 orinoco_unlock(priv, &flags);
3731
Christoph Hellwig620554e2005-06-19 01:27:33 +02003732 return -EINPROGRESS; /* Call commit handler */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003733}
3734
Christoph Hellwig620554e2005-06-19 01:27:33 +02003735static int orinoco_ioctl_getessid(struct net_device *dev,
3736 struct iw_request_info *info,
3737 struct iw_point *erq,
3738 char *essidbuf)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003739{
3740 struct orinoco_private *priv = netdev_priv(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003741 int active;
3742 int err = 0;
3743 unsigned long flags;
3744
Linus Torvalds1da177e2005-04-16 15:20:36 -07003745 if (netif_running(dev)) {
3746 err = orinoco_hw_get_essid(priv, &active, essidbuf);
Jean Tourrilhes7e4e8d92006-10-10 14:45:44 -07003747 if (err < 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003748 return err;
Jean Tourrilhes7e4e8d92006-10-10 14:45:44 -07003749 erq->length = err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003750 } else {
3751 if (orinoco_lock(priv, &flags) != 0)
3752 return -EBUSY;
Jean Tourrilhes7e4e8d92006-10-10 14:45:44 -07003753 memcpy(essidbuf, priv->desired_essid, IW_ESSID_MAX_SIZE);
3754 erq->length = strlen(priv->desired_essid);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003755 orinoco_unlock(priv, &flags);
3756 }
3757
3758 erq->flags = 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003759
Linus Torvalds1da177e2005-04-16 15:20:36 -07003760 return 0;
3761}
3762
Christoph Hellwig620554e2005-06-19 01:27:33 +02003763static int orinoco_ioctl_setnick(struct net_device *dev,
3764 struct iw_request_info *info,
3765 struct iw_point *nrq,
3766 char *nickbuf)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003767{
3768 struct orinoco_private *priv = netdev_priv(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003769 unsigned long flags;
3770
3771 if (nrq->length > IW_ESSID_MAX_SIZE)
3772 return -E2BIG;
3773
Linus Torvalds1da177e2005-04-16 15:20:36 -07003774 if (orinoco_lock(priv, &flags) != 0)
3775 return -EBUSY;
3776
Christoph Hellwig620554e2005-06-19 01:27:33 +02003777 memset(priv->nick, 0, sizeof(priv->nick));
3778 memcpy(priv->nick, nickbuf, nrq->length);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003779
3780 orinoco_unlock(priv, &flags);
3781
Christoph Hellwig620554e2005-06-19 01:27:33 +02003782 return -EINPROGRESS; /* Call commit handler */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003783}
3784
Christoph Hellwig620554e2005-06-19 01:27:33 +02003785static int orinoco_ioctl_getnick(struct net_device *dev,
3786 struct iw_request_info *info,
3787 struct iw_point *nrq,
3788 char *nickbuf)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003789{
3790 struct orinoco_private *priv = netdev_priv(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003791 unsigned long flags;
3792
3793 if (orinoco_lock(priv, &flags) != 0)
3794 return -EBUSY;
3795
Jean Tourrilhes7e4e8d92006-10-10 14:45:44 -07003796 memcpy(nickbuf, priv->nick, IW_ESSID_MAX_SIZE);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003797 orinoco_unlock(priv, &flags);
3798
Jean Tourrilhes7e4e8d92006-10-10 14:45:44 -07003799 nrq->length = strlen(priv->nick);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003800
Linus Torvalds1da177e2005-04-16 15:20:36 -07003801 return 0;
3802}
3803
Christoph Hellwig620554e2005-06-19 01:27:33 +02003804static int orinoco_ioctl_setfreq(struct net_device *dev,
3805 struct iw_request_info *info,
3806 struct iw_freq *frq,
3807 char *extra)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003808{
3809 struct orinoco_private *priv = netdev_priv(dev);
3810 int chan = -1;
3811 unsigned long flags;
Christoph Hellwig620554e2005-06-19 01:27:33 +02003812 int err = -EINPROGRESS; /* Call commit handler */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003813
Christoph Hellwig98c4cae2005-06-19 01:28:06 +02003814 /* In infrastructure mode the AP sets the channel */
3815 if (priv->iw_mode == IW_MODE_INFRA)
3816 return -EBUSY;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003817
David Kilroya94e8422009-02-04 23:05:44 +00003818 if ((frq->e == 0) && (frq->m <= 1000)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07003819 /* Setting by channel number */
3820 chan = frq->m;
3821 } else {
David Kilroy9ee677c2008-12-23 14:03:38 +00003822 /* Setting by frequency */
3823 int denom = 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003824 int i;
3825
David Kilroy9ee677c2008-12-23 14:03:38 +00003826 /* Calculate denominator to rescale to MHz */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003827 for (i = 0; i < (6 - frq->e); i++)
David Kilroy9ee677c2008-12-23 14:03:38 +00003828 denom *= 10;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003829
David Kilroy9ee677c2008-12-23 14:03:38 +00003830 chan = ieee80211_freq_to_dsss_chan(frq->m / denom);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003831 }
3832
David Kilroya94e8422009-02-04 23:05:44 +00003833 if ((chan < 1) || (chan > NUM_CHANNELS) ||
3834 !(priv->channel_mask & (1 << (chan-1))))
Linus Torvalds1da177e2005-04-16 15:20:36 -07003835 return -EINVAL;
3836
3837 if (orinoco_lock(priv, &flags) != 0)
3838 return -EBUSY;
Christoph Hellwig98c4cae2005-06-19 01:28:06 +02003839
Linus Torvalds1da177e2005-04-16 15:20:36 -07003840 priv->channel = chan;
Christoph Hellwig98c4cae2005-06-19 01:28:06 +02003841 if (priv->iw_mode == IW_MODE_MONITOR) {
3842 /* Fast channel change - no commit if successful */
3843 hermes_t *hw = &priv->hw;
3844 err = hermes_docmd_wait(hw, HERMES_CMD_TEST |
3845 HERMES_TEST_SET_CHANNEL,
3846 chan, NULL);
3847 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07003848 orinoco_unlock(priv, &flags);
3849
Christoph Hellwig620554e2005-06-19 01:27:33 +02003850 return err;
3851}
3852
3853static int orinoco_ioctl_getfreq(struct net_device *dev,
3854 struct iw_request_info *info,
3855 struct iw_freq *frq,
3856 char *extra)
3857{
3858 struct orinoco_private *priv = netdev_priv(dev);
3859 int tmp;
3860
3861 /* Locking done in there */
3862 tmp = orinoco_hw_get_freq(priv);
David Kilroy566f2d92009-02-04 23:05:45 +00003863 if (tmp < 0)
Christoph Hellwig620554e2005-06-19 01:27:33 +02003864 return tmp;
Christoph Hellwig620554e2005-06-19 01:27:33 +02003865
David Kilroy9ee677c2008-12-23 14:03:38 +00003866 frq->m = tmp * 100000;
Christoph Hellwig620554e2005-06-19 01:27:33 +02003867 frq->e = 1;
3868
Linus Torvalds1da177e2005-04-16 15:20:36 -07003869 return 0;
3870}
3871
Christoph Hellwig620554e2005-06-19 01:27:33 +02003872static int orinoco_ioctl_getsens(struct net_device *dev,
3873 struct iw_request_info *info,
3874 struct iw_param *srq,
3875 char *extra)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003876{
3877 struct orinoco_private *priv = netdev_priv(dev);
3878 hermes_t *hw = &priv->hw;
3879 u16 val;
3880 int err;
3881 unsigned long flags;
3882
3883 if (!priv->has_sensitivity)
3884 return -EOPNOTSUPP;
3885
3886 if (orinoco_lock(priv, &flags) != 0)
3887 return -EBUSY;
3888 err = hermes_read_wordrec(hw, USER_BAP,
3889 HERMES_RID_CNFSYSTEMSCALE, &val);
3890 orinoco_unlock(priv, &flags);
3891
3892 if (err)
3893 return err;
3894
3895 srq->value = val;
3896 srq->fixed = 0; /* auto */
3897
3898 return 0;
3899}
3900
Christoph Hellwig620554e2005-06-19 01:27:33 +02003901static int orinoco_ioctl_setsens(struct net_device *dev,
3902 struct iw_request_info *info,
3903 struct iw_param *srq,
3904 char *extra)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003905{
3906 struct orinoco_private *priv = netdev_priv(dev);
3907 int val = srq->value;
3908 unsigned long flags;
3909
3910 if (!priv->has_sensitivity)
3911 return -EOPNOTSUPP;
3912
3913 if ((val < 1) || (val > 3))
3914 return -EINVAL;
David Kilroy6fe9deb2009-02-04 23:05:43 +00003915
Linus Torvalds1da177e2005-04-16 15:20:36 -07003916 if (orinoco_lock(priv, &flags) != 0)
3917 return -EBUSY;
3918 priv->ap_density = val;
3919 orinoco_unlock(priv, &flags);
3920
Christoph Hellwig620554e2005-06-19 01:27:33 +02003921 return -EINPROGRESS; /* Call commit handler */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003922}
3923
Christoph Hellwig620554e2005-06-19 01:27:33 +02003924static int orinoco_ioctl_setrts(struct net_device *dev,
3925 struct iw_request_info *info,
3926 struct iw_param *rrq,
3927 char *extra)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003928{
3929 struct orinoco_private *priv = netdev_priv(dev);
3930 int val = rrq->value;
3931 unsigned long flags;
3932
3933 if (rrq->disabled)
3934 val = 2347;
3935
David Kilroya94e8422009-02-04 23:05:44 +00003936 if ((val < 0) || (val > 2347))
Linus Torvalds1da177e2005-04-16 15:20:36 -07003937 return -EINVAL;
3938
3939 if (orinoco_lock(priv, &flags) != 0)
3940 return -EBUSY;
3941
3942 priv->rts_thresh = val;
3943 orinoco_unlock(priv, &flags);
3944
Christoph Hellwig620554e2005-06-19 01:27:33 +02003945 return -EINPROGRESS; /* Call commit handler */
3946}
3947
3948static int orinoco_ioctl_getrts(struct net_device *dev,
3949 struct iw_request_info *info,
3950 struct iw_param *rrq,
3951 char *extra)
3952{
3953 struct orinoco_private *priv = netdev_priv(dev);
3954
3955 rrq->value = priv->rts_thresh;
3956 rrq->disabled = (rrq->value == 2347);
3957 rrq->fixed = 1;
3958
Linus Torvalds1da177e2005-04-16 15:20:36 -07003959 return 0;
3960}
3961
Christoph Hellwig620554e2005-06-19 01:27:33 +02003962static int orinoco_ioctl_setfrag(struct net_device *dev,
3963 struct iw_request_info *info,
3964 struct iw_param *frq,
3965 char *extra)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003966{
3967 struct orinoco_private *priv = netdev_priv(dev);
Christoph Hellwig620554e2005-06-19 01:27:33 +02003968 int err = -EINPROGRESS; /* Call commit handler */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003969 unsigned long flags;
3970
3971 if (orinoco_lock(priv, &flags) != 0)
3972 return -EBUSY;
3973
3974 if (priv->has_mwo) {
3975 if (frq->disabled)
3976 priv->mwo_robust = 0;
3977 else {
3978 if (frq->fixed)
David Kilroyb2f30a02009-02-04 23:05:46 +00003979 printk(KERN_WARNING "%s: Fixed fragmentation "
3980 "is not supported on this firmware. "
3981 "Using MWO robust instead.\n",
3982 dev->name);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003983 priv->mwo_robust = 1;
3984 }
3985 } else {
3986 if (frq->disabled)
3987 priv->frag_thresh = 2346;
3988 else {
David Kilroya94e8422009-02-04 23:05:44 +00003989 if ((frq->value < 256) || (frq->value > 2346))
Linus Torvalds1da177e2005-04-16 15:20:36 -07003990 err = -EINVAL;
3991 else
David Kilroyb2f30a02009-02-04 23:05:46 +00003992 /* must be even */
3993 priv->frag_thresh = frq->value & ~0x1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003994 }
3995 }
3996
3997 orinoco_unlock(priv, &flags);
3998
3999 return err;
4000}
4001
Christoph Hellwig620554e2005-06-19 01:27:33 +02004002static int orinoco_ioctl_getfrag(struct net_device *dev,
4003 struct iw_request_info *info,
4004 struct iw_param *frq,
4005 char *extra)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004006{
4007 struct orinoco_private *priv = netdev_priv(dev);
4008 hermes_t *hw = &priv->hw;
Christoph Hellwig620554e2005-06-19 01:27:33 +02004009 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004010 u16 val;
4011 unsigned long flags;
4012
4013 if (orinoco_lock(priv, &flags) != 0)
4014 return -EBUSY;
David Kilroy6fe9deb2009-02-04 23:05:43 +00004015
Linus Torvalds1da177e2005-04-16 15:20:36 -07004016 if (priv->has_mwo) {
4017 err = hermes_read_wordrec(hw, USER_BAP,
4018 HERMES_RID_CNFMWOROBUST_AGERE,
4019 &val);
4020 if (err)
4021 val = 0;
4022
4023 frq->value = val ? 2347 : 0;
David Kilroya94e8422009-02-04 23:05:44 +00004024 frq->disabled = !val;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004025 frq->fixed = 0;
4026 } else {
David Kilroyb2f30a02009-02-04 23:05:46 +00004027 err = hermes_read_wordrec(hw, USER_BAP,
4028 HERMES_RID_CNFFRAGMENTATIONTHRESHOLD,
Linus Torvalds1da177e2005-04-16 15:20:36 -07004029 &val);
4030 if (err)
4031 val = 0;
4032
4033 frq->value = val;
4034 frq->disabled = (val >= 2346);
4035 frq->fixed = 1;
4036 }
4037
4038 orinoco_unlock(priv, &flags);
David Kilroy6fe9deb2009-02-04 23:05:43 +00004039
Linus Torvalds1da177e2005-04-16 15:20:36 -07004040 return err;
4041}
4042
Christoph Hellwig620554e2005-06-19 01:27:33 +02004043static int orinoco_ioctl_setrate(struct net_device *dev,
4044 struct iw_request_info *info,
4045 struct iw_param *rrq,
4046 char *extra)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004047{
4048 struct orinoco_private *priv = netdev_priv(dev);
David Kilroycfeb1db2009-02-04 23:05:53 +00004049 int ratemode;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004050 int bitrate; /* 100s of kilobits */
Linus Torvalds1da177e2005-04-16 15:20:36 -07004051 unsigned long flags;
David Kilroy6fe9deb2009-02-04 23:05:43 +00004052
Linus Torvalds1da177e2005-04-16 15:20:36 -07004053 /* As the user space doesn't know our highest rate, it uses -1
4054 * to ask us to set the highest rate. Test it using "iwconfig
4055 * ethX rate auto" - Jean II */
4056 if (rrq->value == -1)
4057 bitrate = 110;
4058 else {
4059 if (rrq->value % 100000)
4060 return -EINVAL;
4061 bitrate = rrq->value / 100000;
4062 }
4063
David Kilroycfeb1db2009-02-04 23:05:53 +00004064 ratemode = orinoco_get_bitratemode(bitrate, !rrq->fixed);
David Kilroy6fe9deb2009-02-04 23:05:43 +00004065
Linus Torvalds1da177e2005-04-16 15:20:36 -07004066 if (ratemode == -1)
4067 return -EINVAL;
4068
4069 if (orinoco_lock(priv, &flags) != 0)
4070 return -EBUSY;
4071 priv->bitratemode = ratemode;
4072 orinoco_unlock(priv, &flags);
4073
Christoph Hellwig620554e2005-06-19 01:27:33 +02004074 return -EINPROGRESS;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004075}
4076
Christoph Hellwig620554e2005-06-19 01:27:33 +02004077static int orinoco_ioctl_getrate(struct net_device *dev,
4078 struct iw_request_info *info,
4079 struct iw_param *rrq,
4080 char *extra)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004081{
4082 struct orinoco_private *priv = netdev_priv(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004083 int err = 0;
David Kilroycfeb1db2009-02-04 23:05:53 +00004084 int bitrate, automatic;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004085 unsigned long flags;
4086
4087 if (orinoco_lock(priv, &flags) != 0)
4088 return -EBUSY;
4089
David Kilroycfeb1db2009-02-04 23:05:53 +00004090 orinoco_get_ratemode_cfg(priv->bitratemode, &bitrate, &automatic);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004091
4092 /* If the interface is running we try to find more about the
4093 current mode */
David Kilroycfeb1db2009-02-04 23:05:53 +00004094 if (netif_running(dev))
4095 err = orinoco_hw_get_act_bitrate(priv, &bitrate);
David Kilroy6fe9deb2009-02-04 23:05:43 +00004096
Linus Torvalds1da177e2005-04-16 15:20:36 -07004097 orinoco_unlock(priv, &flags);
4098
David Kilroycfeb1db2009-02-04 23:05:53 +00004099 rrq->value = bitrate;
4100 rrq->fixed = !automatic;
4101 rrq->disabled = 0;
4102
Linus Torvalds1da177e2005-04-16 15:20:36 -07004103 return err;
4104}
4105
Christoph Hellwig620554e2005-06-19 01:27:33 +02004106static int orinoco_ioctl_setpower(struct net_device *dev,
4107 struct iw_request_info *info,
4108 struct iw_param *prq,
4109 char *extra)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004110{
4111 struct orinoco_private *priv = netdev_priv(dev);
Christoph Hellwig620554e2005-06-19 01:27:33 +02004112 int err = -EINPROGRESS; /* Call commit handler */
Linus Torvalds1da177e2005-04-16 15:20:36 -07004113 unsigned long flags;
4114
4115 if (orinoco_lock(priv, &flags) != 0)
4116 return -EBUSY;
4117
4118 if (prq->disabled) {
4119 priv->pm_on = 0;
4120 } else {
4121 switch (prq->flags & IW_POWER_MODE) {
4122 case IW_POWER_UNICAST_R:
4123 priv->pm_mcast = 0;
4124 priv->pm_on = 1;
4125 break;
4126 case IW_POWER_ALL_R:
4127 priv->pm_mcast = 1;
4128 priv->pm_on = 1;
4129 break;
4130 case IW_POWER_ON:
4131 /* No flags : but we may have a value - Jean II */
4132 break;
4133 default:
4134 err = -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004135 goto out;
Pavel Roskinc08ad1e2005-11-29 02:59:27 -05004136 }
David Kilroy6fe9deb2009-02-04 23:05:43 +00004137
Linus Torvalds1da177e2005-04-16 15:20:36 -07004138 if (prq->flags & IW_POWER_TIMEOUT) {
4139 priv->pm_on = 1;
4140 priv->pm_timeout = prq->value / 1000;
4141 }
4142 if (prq->flags & IW_POWER_PERIOD) {
4143 priv->pm_on = 1;
4144 priv->pm_period = prq->value / 1000;
4145 }
4146 /* It's valid to not have a value if we are just toggling
4147 * the flags... Jean II */
David Kilroya94e8422009-02-04 23:05:44 +00004148 if (!priv->pm_on) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07004149 err = -EINVAL;
4150 goto out;
David Kilroy6fe9deb2009-02-04 23:05:43 +00004151 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07004152 }
4153
4154 out:
4155 orinoco_unlock(priv, &flags);
4156
4157 return err;
4158}
4159
Christoph Hellwig620554e2005-06-19 01:27:33 +02004160static int orinoco_ioctl_getpower(struct net_device *dev,
4161 struct iw_request_info *info,
4162 struct iw_param *prq,
4163 char *extra)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004164{
4165 struct orinoco_private *priv = netdev_priv(dev);
4166 hermes_t *hw = &priv->hw;
4167 int err = 0;
4168 u16 enable, period, timeout, mcast;
4169 unsigned long flags;
4170
4171 if (orinoco_lock(priv, &flags) != 0)
4172 return -EBUSY;
David Kilroy6fe9deb2009-02-04 23:05:43 +00004173
David Kilroyb2f30a02009-02-04 23:05:46 +00004174 err = hermes_read_wordrec(hw, USER_BAP,
4175 HERMES_RID_CNFPMENABLED, &enable);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004176 if (err)
4177 goto out;
4178
4179 err = hermes_read_wordrec(hw, USER_BAP,
4180 HERMES_RID_CNFMAXSLEEPDURATION, &period);
4181 if (err)
4182 goto out;
4183
David Kilroyb2f30a02009-02-04 23:05:46 +00004184 err = hermes_read_wordrec(hw, USER_BAP,
4185 HERMES_RID_CNFPMHOLDOVERDURATION, &timeout);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004186 if (err)
4187 goto out;
4188
David Kilroyb2f30a02009-02-04 23:05:46 +00004189 err = hermes_read_wordrec(hw, USER_BAP,
4190 HERMES_RID_CNFMULTICASTRECEIVE, &mcast);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004191 if (err)
4192 goto out;
4193
4194 prq->disabled = !enable;
4195 /* Note : by default, display the period */
4196 if ((prq->flags & IW_POWER_TYPE) == IW_POWER_TIMEOUT) {
4197 prq->flags = IW_POWER_TIMEOUT;
4198 prq->value = timeout * 1000;
4199 } else {
4200 prq->flags = IW_POWER_PERIOD;
4201 prq->value = period * 1000;
4202 }
4203 if (mcast)
4204 prq->flags |= IW_POWER_ALL_R;
4205 else
4206 prq->flags |= IW_POWER_UNICAST_R;
4207
4208 out:
4209 orinoco_unlock(priv, &flags);
4210
4211 return err;
4212}
4213
David Kilroyd03032a2008-08-21 23:28:02 +01004214static int orinoco_ioctl_set_encodeext(struct net_device *dev,
4215 struct iw_request_info *info,
4216 union iwreq_data *wrqu,
4217 char *extra)
4218{
4219 struct orinoco_private *priv = netdev_priv(dev);
4220 struct iw_point *encoding = &wrqu->encoding;
4221 struct iw_encode_ext *ext = (struct iw_encode_ext *)extra;
4222 int idx, alg = ext->alg, set_key = 1;
4223 unsigned long flags;
4224 int err = -EINVAL;
4225 u16 key_len;
4226
4227 if (orinoco_lock(priv, &flags) != 0)
4228 return -EBUSY;
4229
4230 /* Determine and validate the key index */
4231 idx = encoding->flags & IW_ENCODE_INDEX;
4232 if (idx) {
Johannes Berg2c7060022008-10-30 22:09:54 +01004233 if ((idx < 1) || (idx > 4))
David Kilroyd03032a2008-08-21 23:28:02 +01004234 goto out;
4235 idx--;
4236 } else
4237 idx = priv->tx_key;
4238
4239 if (encoding->flags & IW_ENCODE_DISABLED)
David Kilroy6fe9deb2009-02-04 23:05:43 +00004240 alg = IW_ENCODE_ALG_NONE;
David Kilroyd03032a2008-08-21 23:28:02 +01004241
4242 if (priv->has_wpa && (alg != IW_ENCODE_ALG_TKIP)) {
4243 /* Clear any TKIP TX key we had */
4244 (void) orinoco_clear_tkip_key(priv, priv->tx_key);
4245 }
4246
4247 if (ext->ext_flags & IW_ENCODE_EXT_SET_TX_KEY) {
4248 priv->tx_key = idx;
4249 set_key = ((alg == IW_ENCODE_ALG_TKIP) ||
4250 (ext->key_len > 0)) ? 1 : 0;
4251 }
4252
4253 if (set_key) {
4254 /* Set the requested key first */
4255 switch (alg) {
4256 case IW_ENCODE_ALG_NONE:
4257 priv->encode_alg = alg;
4258 priv->keys[idx].len = 0;
4259 break;
4260
4261 case IW_ENCODE_ALG_WEP:
4262 if (ext->key_len > SMALL_KEY_SIZE)
4263 key_len = LARGE_KEY_SIZE;
4264 else if (ext->key_len > 0)
4265 key_len = SMALL_KEY_SIZE;
4266 else
4267 goto out;
4268
4269 priv->encode_alg = alg;
4270 priv->keys[idx].len = cpu_to_le16(key_len);
4271
4272 key_len = min(ext->key_len, key_len);
4273
4274 memset(priv->keys[idx].data, 0, ORINOCO_MAX_KEY_SIZE);
4275 memcpy(priv->keys[idx].data, ext->key, key_len);
4276 break;
4277
4278 case IW_ENCODE_ALG_TKIP:
4279 {
4280 hermes_t *hw = &priv->hw;
4281 u8 *tkip_iv = NULL;
4282
4283 if (!priv->has_wpa ||
4284 (ext->key_len > sizeof(priv->tkip_key[0])))
4285 goto out;
4286
4287 priv->encode_alg = alg;
4288 memset(&priv->tkip_key[idx], 0,
4289 sizeof(priv->tkip_key[idx]));
4290 memcpy(&priv->tkip_key[idx], ext->key, ext->key_len);
4291
4292 if (ext->ext_flags & IW_ENCODE_EXT_RX_SEQ_VALID)
4293 tkip_iv = &ext->rx_seq[0];
4294
4295 err = __orinoco_hw_set_tkip_key(hw, idx,
4296 ext->ext_flags & IW_ENCODE_EXT_SET_TX_KEY,
4297 (u8 *) &priv->tkip_key[idx],
4298 tkip_iv, NULL);
4299 if (err)
4300 printk(KERN_ERR "%s: Error %d setting TKIP key"
4301 "\n", dev->name, err);
4302
4303 goto out;
4304 }
4305 default:
4306 goto out;
4307 }
4308 }
4309 err = -EINPROGRESS;
4310 out:
4311 orinoco_unlock(priv, &flags);
4312
4313 return err;
4314}
4315
4316static int orinoco_ioctl_get_encodeext(struct net_device *dev,
4317 struct iw_request_info *info,
4318 union iwreq_data *wrqu,
4319 char *extra)
4320{
4321 struct orinoco_private *priv = netdev_priv(dev);
4322 struct iw_point *encoding = &wrqu->encoding;
4323 struct iw_encode_ext *ext = (struct iw_encode_ext *)extra;
4324 int idx, max_key_len;
4325 unsigned long flags;
4326 int err;
4327
4328 if (orinoco_lock(priv, &flags) != 0)
4329 return -EBUSY;
4330
4331 err = -EINVAL;
4332 max_key_len = encoding->length - sizeof(*ext);
4333 if (max_key_len < 0)
4334 goto out;
4335
4336 idx = encoding->flags & IW_ENCODE_INDEX;
4337 if (idx) {
Johannes Berg2c7060022008-10-30 22:09:54 +01004338 if ((idx < 1) || (idx > 4))
David Kilroyd03032a2008-08-21 23:28:02 +01004339 goto out;
4340 idx--;
4341 } else
4342 idx = priv->tx_key;
4343
4344 encoding->flags = idx + 1;
4345 memset(ext, 0, sizeof(*ext));
4346
4347 ext->alg = priv->encode_alg;
4348 switch (priv->encode_alg) {
4349 case IW_ENCODE_ALG_NONE:
4350 ext->key_len = 0;
4351 encoding->flags |= IW_ENCODE_DISABLED;
4352 break;
4353 case IW_ENCODE_ALG_WEP:
David Kilroy75d31cf2008-09-12 22:28:18 +01004354 ext->key_len = min_t(u16, le16_to_cpu(priv->keys[idx].len),
4355 max_key_len);
David Kilroyd03032a2008-08-21 23:28:02 +01004356 memcpy(ext->key, priv->keys[idx].data, ext->key_len);
4357 encoding->flags |= IW_ENCODE_ENABLED;
4358 break;
4359 case IW_ENCODE_ALG_TKIP:
David Kilroy75d31cf2008-09-12 22:28:18 +01004360 ext->key_len = min_t(u16, sizeof(struct orinoco_tkip_key),
4361 max_key_len);
David Kilroyd03032a2008-08-21 23:28:02 +01004362 memcpy(ext->key, &priv->tkip_key[idx], ext->key_len);
4363 encoding->flags |= IW_ENCODE_ENABLED;
4364 break;
4365 }
4366
4367 err = 0;
4368 out:
4369 orinoco_unlock(priv, &flags);
4370
4371 return err;
4372}
4373
4374static int orinoco_ioctl_set_auth(struct net_device *dev,
4375 struct iw_request_info *info,
4376 union iwreq_data *wrqu, char *extra)
4377{
4378 struct orinoco_private *priv = netdev_priv(dev);
4379 hermes_t *hw = &priv->hw;
4380 struct iw_param *param = &wrqu->param;
4381 unsigned long flags;
4382 int ret = -EINPROGRESS;
4383
4384 if (orinoco_lock(priv, &flags) != 0)
4385 return -EBUSY;
4386
4387 switch (param->flags & IW_AUTH_INDEX) {
4388 case IW_AUTH_WPA_VERSION:
4389 case IW_AUTH_CIPHER_PAIRWISE:
4390 case IW_AUTH_CIPHER_GROUP:
4391 case IW_AUTH_RX_UNENCRYPTED_EAPOL:
4392 case IW_AUTH_PRIVACY_INVOKED:
4393 case IW_AUTH_DROP_UNENCRYPTED:
4394 /*
4395 * orinoco does not use these parameters
4396 */
4397 break;
4398
4399 case IW_AUTH_KEY_MGMT:
4400 /* wl_lkm implies value 2 == PSK for Hermes I
4401 * which ties in with WEXT
4402 * no other hints tho :(
4403 */
4404 priv->key_mgmt = param->value;
4405 break;
4406
4407 case IW_AUTH_TKIP_COUNTERMEASURES:
4408 /* When countermeasures are enabled, shut down the
4409 * card; when disabled, re-enable the card. This must
4410 * take effect immediately.
4411 *
4412 * TODO: Make sure that the EAPOL message is getting
4413 * out before card disabled
4414 */
4415 if (param->value) {
4416 priv->tkip_cm_active = 1;
4417 ret = hermes_enable_port(hw, 0);
4418 } else {
4419 priv->tkip_cm_active = 0;
4420 ret = hermes_disable_port(hw, 0);
4421 }
4422 break;
4423
4424 case IW_AUTH_80211_AUTH_ALG:
4425 if (param->value & IW_AUTH_ALG_SHARED_KEY)
4426 priv->wep_restrict = 1;
4427 else if (param->value & IW_AUTH_ALG_OPEN_SYSTEM)
4428 priv->wep_restrict = 0;
4429 else
4430 ret = -EINVAL;
4431 break;
4432
4433 case IW_AUTH_WPA_ENABLED:
4434 if (priv->has_wpa) {
4435 priv->wpa_enabled = param->value ? 1 : 0;
4436 } else {
4437 if (param->value)
4438 ret = -EOPNOTSUPP;
4439 /* else silently accept disable of WPA */
4440 priv->wpa_enabled = 0;
4441 }
4442 break;
4443
4444 default:
4445 ret = -EOPNOTSUPP;
4446 }
4447
4448 orinoco_unlock(priv, &flags);
4449 return ret;
4450}
4451
4452static int orinoco_ioctl_get_auth(struct net_device *dev,
4453 struct iw_request_info *info,
4454 union iwreq_data *wrqu, char *extra)
4455{
4456 struct orinoco_private *priv = netdev_priv(dev);
4457 struct iw_param *param = &wrqu->param;
4458 unsigned long flags;
4459 int ret = 0;
4460
4461 if (orinoco_lock(priv, &flags) != 0)
4462 return -EBUSY;
4463
4464 switch (param->flags & IW_AUTH_INDEX) {
4465 case IW_AUTH_KEY_MGMT:
4466 param->value = priv->key_mgmt;
4467 break;
4468
4469 case IW_AUTH_TKIP_COUNTERMEASURES:
4470 param->value = priv->tkip_cm_active;
4471 break;
4472
4473 case IW_AUTH_80211_AUTH_ALG:
4474 if (priv->wep_restrict)
4475 param->value = IW_AUTH_ALG_SHARED_KEY;
4476 else
4477 param->value = IW_AUTH_ALG_OPEN_SYSTEM;
4478 break;
4479
4480 case IW_AUTH_WPA_ENABLED:
4481 param->value = priv->wpa_enabled;
4482 break;
4483
4484 default:
4485 ret = -EOPNOTSUPP;
4486 }
4487
4488 orinoco_unlock(priv, &flags);
4489 return ret;
4490}
4491
4492static int orinoco_ioctl_set_genie(struct net_device *dev,
4493 struct iw_request_info *info,
4494 union iwreq_data *wrqu, char *extra)
4495{
4496 struct orinoco_private *priv = netdev_priv(dev);
4497 u8 *buf;
4498 unsigned long flags;
David Kilroyd03032a2008-08-21 23:28:02 +01004499
Johannes Berg2c7060022008-10-30 22:09:54 +01004500 /* cut off at IEEE80211_MAX_DATA_LEN */
4501 if ((wrqu->data.length > IEEE80211_MAX_DATA_LEN) ||
David Kilroyd03032a2008-08-21 23:28:02 +01004502 (wrqu->data.length && (extra == NULL)))
4503 return -EINVAL;
4504
David Kilroyd03032a2008-08-21 23:28:02 +01004505 if (wrqu->data.length) {
4506 buf = kmalloc(wrqu->data.length, GFP_KERNEL);
Andrey Borzenkov7fe99c42009-01-20 20:26:46 +03004507 if (buf == NULL)
4508 return -ENOMEM;
David Kilroyd03032a2008-08-21 23:28:02 +01004509
4510 memcpy(buf, extra, wrqu->data.length);
Andrey Borzenkov7fe99c42009-01-20 20:26:46 +03004511 } else
4512 buf = NULL;
4513
4514 if (orinoco_lock(priv, &flags) != 0) {
4515 kfree(buf);
4516 return -EBUSY;
David Kilroyd03032a2008-08-21 23:28:02 +01004517 }
4518
Andrey Borzenkov7fe99c42009-01-20 20:26:46 +03004519 kfree(priv->wpa_ie);
4520 priv->wpa_ie = buf;
4521 priv->wpa_ie_len = wrqu->data.length;
4522
David Kilroyd03032a2008-08-21 23:28:02 +01004523 if (priv->wpa_ie) {
4524 /* Looks like wl_lkm wants to check the auth alg, and
4525 * somehow pass it to the firmware.
4526 * Instead it just calls the key mgmt rid
4527 * - we do this in set auth.
4528 */
4529 }
4530
David Kilroyd03032a2008-08-21 23:28:02 +01004531 orinoco_unlock(priv, &flags);
Andrey Borzenkov7fe99c42009-01-20 20:26:46 +03004532 return 0;
David Kilroyd03032a2008-08-21 23:28:02 +01004533}
4534
4535static int orinoco_ioctl_get_genie(struct net_device *dev,
4536 struct iw_request_info *info,
4537 union iwreq_data *wrqu, char *extra)
4538{
4539 struct orinoco_private *priv = netdev_priv(dev);
4540 unsigned long flags;
4541 int err = 0;
4542
4543 if (orinoco_lock(priv, &flags) != 0)
4544 return -EBUSY;
4545
4546 if ((priv->wpa_ie_len == 0) || (priv->wpa_ie == NULL)) {
4547 wrqu->data.length = 0;
4548 goto out;
4549 }
4550
4551 if (wrqu->data.length < priv->wpa_ie_len) {
4552 err = -E2BIG;
4553 goto out;
4554 }
4555
4556 wrqu->data.length = priv->wpa_ie_len;
4557 memcpy(extra, priv->wpa_ie, priv->wpa_ie_len);
4558
4559out:
4560 orinoco_unlock(priv, &flags);
4561 return err;
4562}
4563
4564static int orinoco_ioctl_set_mlme(struct net_device *dev,
4565 struct iw_request_info *info,
4566 union iwreq_data *wrqu, char *extra)
4567{
4568 struct orinoco_private *priv = netdev_priv(dev);
4569 hermes_t *hw = &priv->hw;
4570 struct iw_mlme *mlme = (struct iw_mlme *)extra;
4571 unsigned long flags;
4572 int ret = 0;
4573
4574 if (orinoco_lock(priv, &flags) != 0)
4575 return -EBUSY;
4576
4577 switch (mlme->cmd) {
4578 case IW_MLME_DEAUTH:
4579 /* silently ignore */
4580 break;
4581
4582 case IW_MLME_DISASSOC:
4583 {
4584 struct {
4585 u8 addr[ETH_ALEN];
4586 __le16 reason_code;
4587 } __attribute__ ((packed)) buf;
4588
4589 memcpy(buf.addr, mlme->addr.sa_data, ETH_ALEN);
4590 buf.reason_code = cpu_to_le16(mlme->reason_code);
4591 ret = HERMES_WRITE_RECORD(hw, USER_BAP,
4592 HERMES_RID_CNFDISASSOCIATE,
4593 &buf);
4594 break;
4595 }
4596 default:
4597 ret = -EOPNOTSUPP;
4598 }
4599
4600 orinoco_unlock(priv, &flags);
4601 return ret;
4602}
4603
Christoph Hellwig620554e2005-06-19 01:27:33 +02004604static int orinoco_ioctl_getretry(struct net_device *dev,
4605 struct iw_request_info *info,
4606 struct iw_param *rrq,
4607 char *extra)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004608{
4609 struct orinoco_private *priv = netdev_priv(dev);
4610 hermes_t *hw = &priv->hw;
4611 int err = 0;
4612 u16 short_limit, long_limit, lifetime;
4613 unsigned long flags;
4614
4615 if (orinoco_lock(priv, &flags) != 0)
4616 return -EBUSY;
David Kilroy6fe9deb2009-02-04 23:05:43 +00004617
Linus Torvalds1da177e2005-04-16 15:20:36 -07004618 err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_SHORTRETRYLIMIT,
4619 &short_limit);
4620 if (err)
4621 goto out;
4622
4623 err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_LONGRETRYLIMIT,
4624 &long_limit);
4625 if (err)
4626 goto out;
4627
4628 err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_MAXTRANSMITLIFETIME,
4629 &lifetime);
4630 if (err)
4631 goto out;
4632
4633 rrq->disabled = 0; /* Can't be disabled */
4634
4635 /* Note : by default, display the retry number */
4636 if ((rrq->flags & IW_RETRY_TYPE) == IW_RETRY_LIFETIME) {
4637 rrq->flags = IW_RETRY_LIFETIME;
4638 rrq->value = lifetime * 1000; /* ??? */
4639 } else {
4640 /* By default, display the min number */
Jean Tourrilheseeec9f12006-08-29 18:02:31 -07004641 if ((rrq->flags & IW_RETRY_LONG)) {
4642 rrq->flags = IW_RETRY_LIMIT | IW_RETRY_LONG;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004643 rrq->value = long_limit;
4644 } else {
4645 rrq->flags = IW_RETRY_LIMIT;
4646 rrq->value = short_limit;
David Kilroya94e8422009-02-04 23:05:44 +00004647 if (short_limit != long_limit)
Jean Tourrilheseeec9f12006-08-29 18:02:31 -07004648 rrq->flags |= IW_RETRY_SHORT;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004649 }
4650 }
4651
4652 out:
4653 orinoco_unlock(priv, &flags);
4654
4655 return err;
4656}
4657
Christoph Hellwig620554e2005-06-19 01:27:33 +02004658static int orinoco_ioctl_reset(struct net_device *dev,
4659 struct iw_request_info *info,
4660 void *wrqu,
4661 char *extra)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004662{
4663 struct orinoco_private *priv = netdev_priv(dev);
Christoph Hellwig620554e2005-06-19 01:27:33 +02004664
David Kilroya94e8422009-02-04 23:05:44 +00004665 if (!capable(CAP_NET_ADMIN))
Christoph Hellwig620554e2005-06-19 01:27:33 +02004666 return -EPERM;
4667
4668 if (info->cmd == (SIOCIWFIRSTPRIV + 0x1)) {
4669 printk(KERN_DEBUG "%s: Forcing reset!\n", dev->name);
4670
4671 /* Firmware reset */
David Howellsc4028952006-11-22 14:57:56 +00004672 orinoco_reset(&priv->reset_work);
Christoph Hellwig620554e2005-06-19 01:27:33 +02004673 } else {
4674 printk(KERN_DEBUG "%s: Force scheduling reset!\n", dev->name);
4675
4676 schedule_work(&priv->reset_work);
4677 }
4678
4679 return 0;
4680}
4681
4682static int orinoco_ioctl_setibssport(struct net_device *dev,
4683 struct iw_request_info *info,
4684 void *wrqu,
4685 char *extra)
4686
4687{
4688 struct orinoco_private *priv = netdev_priv(dev);
David Kilroya94e8422009-02-04 23:05:44 +00004689 int val = *((int *) extra);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004690 unsigned long flags;
4691
4692 if (orinoco_lock(priv, &flags) != 0)
4693 return -EBUSY;
4694
4695 priv->ibss_port = val ;
4696
4697 /* Actually update the mode we are using */
4698 set_port_type(priv);
4699
4700 orinoco_unlock(priv, &flags);
Christoph Hellwig620554e2005-06-19 01:27:33 +02004701 return -EINPROGRESS; /* Call commit handler */
Linus Torvalds1da177e2005-04-16 15:20:36 -07004702}
4703
Christoph Hellwig620554e2005-06-19 01:27:33 +02004704static int orinoco_ioctl_getibssport(struct net_device *dev,
4705 struct iw_request_info *info,
4706 void *wrqu,
4707 char *extra)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004708{
4709 struct orinoco_private *priv = netdev_priv(dev);
Christoph Hellwig620554e2005-06-19 01:27:33 +02004710 int *val = (int *) extra;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004711
4712 *val = priv->ibss_port;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004713 return 0;
4714}
4715
Christoph Hellwig620554e2005-06-19 01:27:33 +02004716static int orinoco_ioctl_setport3(struct net_device *dev,
4717 struct iw_request_info *info,
4718 void *wrqu,
4719 char *extra)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004720{
4721 struct orinoco_private *priv = netdev_priv(dev);
David Kilroya94e8422009-02-04 23:05:44 +00004722 int val = *((int *) extra);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004723 int err = 0;
4724 unsigned long flags;
4725
4726 if (orinoco_lock(priv, &flags) != 0)
4727 return -EBUSY;
4728
4729 switch (val) {
4730 case 0: /* Try to do IEEE ad-hoc mode */
David Kilroya94e8422009-02-04 23:05:44 +00004731 if (!priv->has_ibss) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07004732 err = -EINVAL;
4733 break;
4734 }
4735 priv->prefer_port3 = 0;
David Kilroy6fe9deb2009-02-04 23:05:43 +00004736
Linus Torvalds1da177e2005-04-16 15:20:36 -07004737 break;
4738
4739 case 1: /* Try to do Lucent proprietary ad-hoc mode */
David Kilroya94e8422009-02-04 23:05:44 +00004740 if (!priv->has_port3) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07004741 err = -EINVAL;
4742 break;
4743 }
4744 priv->prefer_port3 = 1;
4745 break;
4746
4747 default:
4748 err = -EINVAL;
4749 }
4750
David Kilroya94e8422009-02-04 23:05:44 +00004751 if (!err) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07004752 /* Actually update the mode we are using */
4753 set_port_type(priv);
Christoph Hellwig620554e2005-06-19 01:27:33 +02004754 err = -EINPROGRESS;
4755 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07004756
4757 orinoco_unlock(priv, &flags);
4758
4759 return err;
4760}
4761
Christoph Hellwig620554e2005-06-19 01:27:33 +02004762static int orinoco_ioctl_getport3(struct net_device *dev,
4763 struct iw_request_info *info,
4764 void *wrqu,
4765 char *extra)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004766{
4767 struct orinoco_private *priv = netdev_priv(dev);
Christoph Hellwig620554e2005-06-19 01:27:33 +02004768 int *val = (int *) extra;
4769
4770 *val = priv->prefer_port3;
4771 return 0;
4772}
4773
4774static int orinoco_ioctl_setpreamble(struct net_device *dev,
4775 struct iw_request_info *info,
4776 void *wrqu,
4777 char *extra)
4778{
4779 struct orinoco_private *priv = netdev_priv(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004780 unsigned long flags;
Christoph Hellwig620554e2005-06-19 01:27:33 +02004781 int val;
4782
David Kilroya94e8422009-02-04 23:05:44 +00004783 if (!priv->has_preamble)
Christoph Hellwig620554e2005-06-19 01:27:33 +02004784 return -EOPNOTSUPP;
4785
4786 /* 802.11b has recently defined some short preamble.
4787 * Basically, the Phy header has been reduced in size.
4788 * This increase performance, especially at high rates
4789 * (the preamble is transmitted at 1Mb/s), unfortunately
4790 * this give compatibility troubles... - Jean II */
David Kilroya94e8422009-02-04 23:05:44 +00004791 val = *((int *) extra);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004792
4793 if (orinoco_lock(priv, &flags) != 0)
4794 return -EBUSY;
4795
Christoph Hellwig620554e2005-06-19 01:27:33 +02004796 if (val)
4797 priv->preamble = 1;
4798 else
4799 priv->preamble = 0;
4800
Linus Torvalds1da177e2005-04-16 15:20:36 -07004801 orinoco_unlock(priv, &flags);
Christoph Hellwig620554e2005-06-19 01:27:33 +02004802
4803 return -EINPROGRESS; /* Call commit handler */
4804}
4805
4806static int orinoco_ioctl_getpreamble(struct net_device *dev,
4807 struct iw_request_info *info,
4808 void *wrqu,
4809 char *extra)
4810{
4811 struct orinoco_private *priv = netdev_priv(dev);
4812 int *val = (int *) extra;
4813
David Kilroya94e8422009-02-04 23:05:44 +00004814 if (!priv->has_preamble)
Christoph Hellwig620554e2005-06-19 01:27:33 +02004815 return -EOPNOTSUPP;
4816
4817 *val = priv->preamble;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004818 return 0;
4819}
4820
Christoph Hellwig620554e2005-06-19 01:27:33 +02004821/* ioctl interface to hermes_read_ltv()
4822 * To use with iwpriv, pass the RID as the token argument, e.g.
4823 * iwpriv get_rid [0xfc00]
4824 * At least Wireless Tools 25 is required to use iwpriv.
4825 * For Wireless Tools 25 and 26 append "dummy" are the end. */
4826static int orinoco_ioctl_getrid(struct net_device *dev,
4827 struct iw_request_info *info,
4828 struct iw_point *data,
4829 char *extra)
4830{
4831 struct orinoco_private *priv = netdev_priv(dev);
4832 hermes_t *hw = &priv->hw;
4833 int rid = data->flags;
4834 u16 length;
4835 int err;
4836 unsigned long flags;
4837
4838 /* It's a "get" function, but we don't want users to access the
4839 * WEP key and other raw firmware data */
David Kilroya94e8422009-02-04 23:05:44 +00004840 if (!capable(CAP_NET_ADMIN))
Christoph Hellwig620554e2005-06-19 01:27:33 +02004841 return -EPERM;
4842
4843 if (rid < 0xfc00 || rid > 0xffff)
4844 return -EINVAL;
4845
4846 if (orinoco_lock(priv, &flags) != 0)
4847 return -EBUSY;
4848
4849 err = hermes_read_ltv(hw, USER_BAP, rid, MAX_RID_LEN, &length,
4850 extra);
4851 if (err)
4852 goto out;
4853
4854 data->length = min_t(u16, HERMES_RECLEN_TO_BYTES(length),
4855 MAX_RID_LEN);
4856
4857 out:
4858 orinoco_unlock(priv, &flags);
4859 return err;
4860}
4861
David Kilroy17a1a882008-08-21 23:27:47 +01004862/* Trigger a scan (look for other cells in the vicinity) */
Christoph Hellwig95dd91f2005-06-19 01:27:56 +02004863static int orinoco_ioctl_setscan(struct net_device *dev,
4864 struct iw_request_info *info,
David Kilroy9930cce2008-09-13 12:22:05 +01004865 struct iw_point *srq,
Christoph Hellwig95dd91f2005-06-19 01:27:56 +02004866 char *extra)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004867{
4868 struct orinoco_private *priv = netdev_priv(dev);
Christoph Hellwig95dd91f2005-06-19 01:27:56 +02004869 hermes_t *hw = &priv->hw;
David Kilroy0753bba2008-08-21 23:27:46 +01004870 struct iw_scan_req *si = (struct iw_scan_req *) extra;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004871 int err = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004872 unsigned long flags;
4873
Christoph Hellwig95dd91f2005-06-19 01:27:56 +02004874 /* Note : you may have realised that, as this is a SET operation,
Alexey Dobriyan7f927fc2006-03-28 01:56:53 -08004875 * this is privileged and therefore a normal user can't
Christoph Hellwig95dd91f2005-06-19 01:27:56 +02004876 * perform scanning.
4877 * This is not an error, while the device perform scanning,
4878 * traffic doesn't flow, so it's a perfect DoS...
4879 * Jean II */
Linus Torvalds1da177e2005-04-16 15:20:36 -07004880
Christoph Hellwig95dd91f2005-06-19 01:27:56 +02004881 if (orinoco_lock(priv, &flags) != 0)
4882 return -EBUSY;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004883
Christoph Hellwig95dd91f2005-06-19 01:27:56 +02004884 /* Scanning with port 0 disabled would fail */
4885 if (!netif_running(dev)) {
4886 err = -ENETDOWN;
4887 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004888 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07004889
Christoph Hellwig95dd91f2005-06-19 01:27:56 +02004890 /* In monitor mode, the scan results are always empty.
4891 * Probe responses are passed to the driver as received
4892 * frames and could be processed in software. */
4893 if (priv->iw_mode == IW_MODE_MONITOR) {
4894 err = -EOPNOTSUPP;
4895 goto out;
4896 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07004897
Christoph Hellwig95dd91f2005-06-19 01:27:56 +02004898 /* Note : because we don't lock out the irq handler, the way
4899 * we access scan variables in priv is critical.
4900 * o scan_inprogress : not touched by irq handler
4901 * o scan_mode : not touched by irq handler
Christoph Hellwig95dd91f2005-06-19 01:27:56 +02004902 * Before modifying anything on those variables, please think hard !
4903 * Jean II */
4904
Christoph Hellwig95dd91f2005-06-19 01:27:56 +02004905 /* Save flags */
4906 priv->scan_mode = srq->flags;
4907
4908 /* Always trigger scanning, even if it's in progress.
4909 * This way, if the info frame get lost, we will recover somewhat
4910 * gracefully - Jean II */
4911
4912 if (priv->has_hostscan) {
4913 switch (priv->firmware_type) {
4914 case FIRMWARE_TYPE_SYMBOL:
4915 err = hermes_write_wordrec(hw, USER_BAP,
David Kilroyb2f30a02009-02-04 23:05:46 +00004916 HERMES_RID_CNFHOSTSCAN_SYMBOL,
4917 HERMES_HOSTSCAN_SYMBOL_ONCE |
4918 HERMES_HOSTSCAN_SYMBOL_BCAST);
Christoph Hellwig95dd91f2005-06-19 01:27:56 +02004919 break;
4920 case FIRMWARE_TYPE_INTERSIL: {
Pavel Roskind133ae42005-09-23 04:18:06 -04004921 __le16 req[3];
Christoph Hellwig95dd91f2005-06-19 01:27:56 +02004922
4923 req[0] = cpu_to_le16(0x3fff); /* All channels */
4924 req[1] = cpu_to_le16(0x0001); /* rate 1 Mbps */
4925 req[2] = 0; /* Any ESSID */
4926 err = HERMES_WRITE_RECORD(hw, USER_BAP,
4927 HERMES_RID_CNFHOSTSCAN, &req);
4928 }
4929 break;
4930 case FIRMWARE_TYPE_AGERE:
David Kilroy0753bba2008-08-21 23:27:46 +01004931 if (priv->scan_mode & IW_SCAN_THIS_ESSID) {
4932 struct hermes_idstring idbuf;
4933 size_t len = min(sizeof(idbuf.val),
4934 (size_t) si->essid_len);
4935 idbuf.len = cpu_to_le16(len);
4936 memcpy(idbuf.val, si->essid, len);
4937
4938 err = hermes_write_ltv(hw, USER_BAP,
4939 HERMES_RID_CNFSCANSSID_AGERE,
4940 HERMES_BYTES_TO_RECLEN(len + 2),
4941 &idbuf);
4942 } else
4943 err = hermes_write_wordrec(hw, USER_BAP,
Christoph Hellwig95dd91f2005-06-19 01:27:56 +02004944 HERMES_RID_CNFSCANSSID_AGERE,
4945 0); /* Any ESSID */
4946 if (err)
4947 break;
4948
David Kilroy01632fa2008-08-21 23:27:58 +01004949 if (priv->has_ext_scan) {
4950 /* Clear scan results at the start of
4951 * an extended scan */
4952 orinoco_clear_scan_results(priv,
4953 msecs_to_jiffies(15000));
4954
4955 /* TODO: Is this available on older firmware?
4956 * Can we use it to scan specific channels
4957 * for IW_SCAN_THIS_FREQ? */
4958 err = hermes_write_wordrec(hw, USER_BAP,
4959 HERMES_RID_CNFSCANCHANNELS2GHZ,
4960 0x7FFF);
4961 if (err)
4962 goto out;
4963
4964 err = hermes_inquire(hw,
4965 HERMES_INQ_CHANNELINFO);
4966 } else
4967 err = hermes_inquire(hw, HERMES_INQ_SCAN);
Christoph Hellwig95dd91f2005-06-19 01:27:56 +02004968 break;
4969 }
4970 } else
4971 err = hermes_inquire(hw, HERMES_INQ_SCAN);
4972
4973 /* One more client */
David Kilroya94e8422009-02-04 23:05:44 +00004974 if (!err)
Christoph Hellwig95dd91f2005-06-19 01:27:56 +02004975 priv->scan_inprogress = 1;
4976
4977 out:
4978 orinoco_unlock(priv, &flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004979 return err;
4980}
4981
Dan Williams1e3428e2007-10-10 23:56:25 -04004982#define MAX_CUSTOM_LEN 64
4983
Christoph Hellwig95dd91f2005-06-19 01:27:56 +02004984/* Translate scan data returned from the card to a card independant
David Kilroy17a1a882008-08-21 23:27:47 +01004985 * format that the Wireless Tools will understand - Jean II */
Dan Williams1e3428e2007-10-10 23:56:25 -04004986static inline char *orinoco_translate_scan(struct net_device *dev,
David S. Millerccc58052008-06-16 18:50:49 -07004987 struct iw_request_info *info,
Dan Williams1e3428e2007-10-10 23:56:25 -04004988 char *current_ev,
4989 char *end_buf,
4990 union hermes_scan_info *bss,
Pavel Roskindfe1baf2008-11-10 09:25:53 -05004991 unsigned long last_scanned)
Christoph Hellwig95dd91f2005-06-19 01:27:56 +02004992{
4993 struct orinoco_private *priv = netdev_priv(dev);
Christoph Hellwig95dd91f2005-06-19 01:27:56 +02004994 u16 capabilities;
4995 u16 channel;
4996 struct iw_event iwe; /* Temporary buffer */
Dan Williams1e3428e2007-10-10 23:56:25 -04004997 char custom[MAX_CUSTOM_LEN];
Christoph Hellwig95dd91f2005-06-19 01:27:56 +02004998
David Kilroy17a1a882008-08-21 23:27:47 +01004999 memset(&iwe, 0, sizeof(iwe));
5000
Dan Williams1e3428e2007-10-10 23:56:25 -04005001 /* First entry *MUST* be the AP MAC address */
5002 iwe.cmd = SIOCGIWAP;
5003 iwe.u.ap_addr.sa_family = ARPHRD_ETHER;
5004 memcpy(iwe.u.ap_addr.sa_data, bss->a.bssid, ETH_ALEN);
David S. Millerccc58052008-06-16 18:50:49 -07005005 current_ev = iwe_stream_add_event(info, current_ev, end_buf,
5006 &iwe, IW_EV_ADDR_LEN);
Dan Williams1e3428e2007-10-10 23:56:25 -04005007
5008 /* Other entries will be displayed in the order we give them */
5009
5010 /* Add the ESSID */
5011 iwe.u.data.length = le16_to_cpu(bss->a.essid_len);
5012 if (iwe.u.data.length > 32)
5013 iwe.u.data.length = 32;
5014 iwe.cmd = SIOCGIWESSID;
5015 iwe.u.data.flags = 1;
David S. Millerccc58052008-06-16 18:50:49 -07005016 current_ev = iwe_stream_add_point(info, current_ev, end_buf,
5017 &iwe, bss->a.essid);
Dan Williams1e3428e2007-10-10 23:56:25 -04005018
5019 /* Add mode */
5020 iwe.cmd = SIOCGIWMODE;
5021 capabilities = le16_to_cpu(bss->a.capabilities);
David Kilroy17a1a882008-08-21 23:27:47 +01005022 if (capabilities & (WLAN_CAPABILITY_ESS | WLAN_CAPABILITY_IBSS)) {
5023 if (capabilities & WLAN_CAPABILITY_ESS)
Dan Williams1e3428e2007-10-10 23:56:25 -04005024 iwe.u.mode = IW_MODE_MASTER;
Christoph Hellwig95dd91f2005-06-19 01:27:56 +02005025 else
Dan Williams1e3428e2007-10-10 23:56:25 -04005026 iwe.u.mode = IW_MODE_ADHOC;
David S. Millerccc58052008-06-16 18:50:49 -07005027 current_ev = iwe_stream_add_event(info, current_ev, end_buf,
5028 &iwe, IW_EV_UINT_LEN);
Christoph Hellwig95dd91f2005-06-19 01:27:56 +02005029 }
5030
Dan Williams1e3428e2007-10-10 23:56:25 -04005031 channel = bss->s.channel;
5032 if ((channel >= 1) && (channel <= NUM_CHANNELS)) {
David Kilroy17a1a882008-08-21 23:27:47 +01005033 /* Add channel and frequency */
Dan Williams1e3428e2007-10-10 23:56:25 -04005034 iwe.cmd = SIOCGIWFREQ;
David Kilroy17a1a882008-08-21 23:27:47 +01005035 iwe.u.freq.m = channel;
5036 iwe.u.freq.e = 0;
5037 current_ev = iwe_stream_add_event(info, current_ev, end_buf,
5038 &iwe, IW_EV_FREQ_LEN);
5039
David Kilroy9ee677c2008-12-23 14:03:38 +00005040 iwe.u.freq.m = ieee80211_dsss_chan_to_freq(channel) * 100000;
Dan Williams1e3428e2007-10-10 23:56:25 -04005041 iwe.u.freq.e = 1;
David S. Millerccc58052008-06-16 18:50:49 -07005042 current_ev = iwe_stream_add_event(info, current_ev, end_buf,
Dan Williams1e3428e2007-10-10 23:56:25 -04005043 &iwe, IW_EV_FREQ_LEN);
Christoph Hellwig95dd91f2005-06-19 01:27:56 +02005044 }
5045
David Kilroy17a1a882008-08-21 23:27:47 +01005046 /* Add quality statistics. level and noise in dB. No link quality */
Dan Williams1e3428e2007-10-10 23:56:25 -04005047 iwe.cmd = IWEVQUAL;
David Kilroy17a1a882008-08-21 23:27:47 +01005048 iwe.u.qual.updated = IW_QUAL_DBM | IW_QUAL_QUAL_INVALID;
Dan Williams1e3428e2007-10-10 23:56:25 -04005049 iwe.u.qual.level = (__u8) le16_to_cpu(bss->a.level) - 0x95;
5050 iwe.u.qual.noise = (__u8) le16_to_cpu(bss->a.noise) - 0x95;
5051 /* Wireless tools prior to 27.pre22 will show link quality
5052 * anyway, so we provide a reasonable value. */
5053 if (iwe.u.qual.level > iwe.u.qual.noise)
5054 iwe.u.qual.qual = iwe.u.qual.level - iwe.u.qual.noise;
5055 else
5056 iwe.u.qual.qual = 0;
David S. Millerccc58052008-06-16 18:50:49 -07005057 current_ev = iwe_stream_add_event(info, current_ev, end_buf,
5058 &iwe, IW_EV_QUAL_LEN);
Christoph Hellwig95dd91f2005-06-19 01:27:56 +02005059
Dan Williams1e3428e2007-10-10 23:56:25 -04005060 /* Add encryption capability */
5061 iwe.cmd = SIOCGIWENCODE;
David Kilroy17a1a882008-08-21 23:27:47 +01005062 if (capabilities & WLAN_CAPABILITY_PRIVACY)
Dan Williams1e3428e2007-10-10 23:56:25 -04005063 iwe.u.data.flags = IW_ENCODE_ENABLED | IW_ENCODE_NOKEY;
5064 else
5065 iwe.u.data.flags = IW_ENCODE_DISABLED;
5066 iwe.u.data.length = 0;
David S. Millerccc58052008-06-16 18:50:49 -07005067 current_ev = iwe_stream_add_point(info, current_ev, end_buf,
David Kilroy17a1a882008-08-21 23:27:47 +01005068 &iwe, NULL);
Christoph Hellwig95dd91f2005-06-19 01:27:56 +02005069
Dan Williams1e3428e2007-10-10 23:56:25 -04005070 /* Bit rate is not available in Lucent/Agere firmwares */
5071 if (priv->firmware_type != FIRMWARE_TYPE_AGERE) {
David S. Millerccc58052008-06-16 18:50:49 -07005072 char *current_val = current_ev + iwe_stream_lcp_len(info);
Dan Williams1e3428e2007-10-10 23:56:25 -04005073 int i;
5074 int step;
Christoph Hellwig95dd91f2005-06-19 01:27:56 +02005075
Dan Williams1e3428e2007-10-10 23:56:25 -04005076 if (priv->firmware_type == FIRMWARE_TYPE_SYMBOL)
5077 step = 2;
Christoph Hellwig95dd91f2005-06-19 01:27:56 +02005078 else
Dan Williams1e3428e2007-10-10 23:56:25 -04005079 step = 1;
Christoph Hellwig95dd91f2005-06-19 01:27:56 +02005080
Dan Williams1e3428e2007-10-10 23:56:25 -04005081 iwe.cmd = SIOCGIWRATE;
5082 /* Those two flags are ignored... */
5083 iwe.u.bitrate.fixed = iwe.u.bitrate.disabled = 0;
5084 /* Max 10 values */
5085 for (i = 0; i < 10; i += step) {
5086 /* NULL terminated */
5087 if (bss->p.rates[i] == 0x0)
5088 break;
5089 /* Bit rate given in 500 kb/s units (+ 0x80) */
David Kilroy17a1a882008-08-21 23:27:47 +01005090 iwe.u.bitrate.value =
5091 ((bss->p.rates[i] & 0x7f) * 500000);
David S. Millerccc58052008-06-16 18:50:49 -07005092 current_val = iwe_stream_add_value(info, current_ev,
5093 current_val,
Dan Williams1e3428e2007-10-10 23:56:25 -04005094 end_buf, &iwe,
5095 IW_EV_PARAM_LEN);
Christoph Hellwig95dd91f2005-06-19 01:27:56 +02005096 }
Dan Williams1e3428e2007-10-10 23:56:25 -04005097 /* Check if we added any event */
David S. Millerccc58052008-06-16 18:50:49 -07005098 if ((current_val - current_ev) > iwe_stream_lcp_len(info))
Dan Williams1e3428e2007-10-10 23:56:25 -04005099 current_ev = current_val;
Christoph Hellwig95dd91f2005-06-19 01:27:56 +02005100 }
Dan Williams1e3428e2007-10-10 23:56:25 -04005101
David Kilroy17a1a882008-08-21 23:27:47 +01005102 /* Beacon interval */
5103 iwe.cmd = IWEVCUSTOM;
5104 iwe.u.data.length = snprintf(custom, MAX_CUSTOM_LEN,
5105 "bcn_int=%d",
5106 le16_to_cpu(bss->a.beacon_interv));
5107 if (iwe.u.data.length)
5108 current_ev = iwe_stream_add_point(info, current_ev, end_buf,
5109 &iwe, custom);
5110
5111 /* Capabilites */
5112 iwe.cmd = IWEVCUSTOM;
5113 iwe.u.data.length = snprintf(custom, MAX_CUSTOM_LEN,
5114 "capab=0x%04x",
5115 capabilities);
5116 if (iwe.u.data.length)
5117 current_ev = iwe_stream_add_point(info, current_ev, end_buf,
5118 &iwe, custom);
5119
5120 /* Add EXTRA: Age to display seconds since last beacon/probe response
5121 * for given network. */
5122 iwe.cmd = IWEVCUSTOM;
5123 iwe.u.data.length = snprintf(custom, MAX_CUSTOM_LEN,
5124 " Last beacon: %dms ago",
5125 jiffies_to_msecs(jiffies - last_scanned));
5126 if (iwe.u.data.length)
5127 current_ev = iwe_stream_add_point(info, current_ev, end_buf,
5128 &iwe, custom);
5129
Dan Williams1e3428e2007-10-10 23:56:25 -04005130 return current_ev;
Christoph Hellwig95dd91f2005-06-19 01:27:56 +02005131}
5132
David Kilroy01632fa2008-08-21 23:27:58 +01005133static inline char *orinoco_translate_ext_scan(struct net_device *dev,
5134 struct iw_request_info *info,
5135 char *current_ev,
5136 char *end_buf,
5137 struct agere_ext_scan_info *bss,
Pavel Roskindfe1baf2008-11-10 09:25:53 -05005138 unsigned long last_scanned)
David Kilroy01632fa2008-08-21 23:27:58 +01005139{
5140 u16 capabilities;
5141 u16 channel;
5142 struct iw_event iwe; /* Temporary buffer */
5143 char custom[MAX_CUSTOM_LEN];
5144 u8 *ie;
5145
5146 memset(&iwe, 0, sizeof(iwe));
5147
5148 /* First entry *MUST* be the AP MAC address */
5149 iwe.cmd = SIOCGIWAP;
5150 iwe.u.ap_addr.sa_family = ARPHRD_ETHER;
5151 memcpy(iwe.u.ap_addr.sa_data, bss->bssid, ETH_ALEN);
5152 current_ev = iwe_stream_add_event(info, current_ev, end_buf,
5153 &iwe, IW_EV_ADDR_LEN);
5154
5155 /* Other entries will be displayed in the order we give them */
5156
5157 /* Add the ESSID */
5158 ie = bss->data;
5159 iwe.u.data.length = ie[1];
5160 if (iwe.u.data.length) {
5161 if (iwe.u.data.length > 32)
5162 iwe.u.data.length = 32;
5163 iwe.cmd = SIOCGIWESSID;
5164 iwe.u.data.flags = 1;
5165 current_ev = iwe_stream_add_point(info, current_ev, end_buf,
5166 &iwe, &ie[2]);
5167 }
5168
5169 /* Add mode */
5170 capabilities = le16_to_cpu(bss->capabilities);
5171 if (capabilities & (WLAN_CAPABILITY_ESS | WLAN_CAPABILITY_IBSS)) {
5172 iwe.cmd = SIOCGIWMODE;
5173 if (capabilities & WLAN_CAPABILITY_ESS)
5174 iwe.u.mode = IW_MODE_MASTER;
5175 else
5176 iwe.u.mode = IW_MODE_ADHOC;
5177 current_ev = iwe_stream_add_event(info, current_ev, end_buf,
5178 &iwe, IW_EV_UINT_LEN);
5179 }
5180
Johannes Berg2c7060022008-10-30 22:09:54 +01005181 ie = orinoco_get_ie(bss->data, sizeof(bss->data), WLAN_EID_DS_PARAMS);
David Kilroy01632fa2008-08-21 23:27:58 +01005182 channel = ie ? ie[2] : 0;
5183 if ((channel >= 1) && (channel <= NUM_CHANNELS)) {
5184 /* Add channel and frequency */
5185 iwe.cmd = SIOCGIWFREQ;
5186 iwe.u.freq.m = channel;
5187 iwe.u.freq.e = 0;
5188 current_ev = iwe_stream_add_event(info, current_ev, end_buf,
5189 &iwe, IW_EV_FREQ_LEN);
5190
David Kilroy9ee677c2008-12-23 14:03:38 +00005191 iwe.u.freq.m = ieee80211_dsss_chan_to_freq(channel) * 100000;
David Kilroy01632fa2008-08-21 23:27:58 +01005192 iwe.u.freq.e = 1;
5193 current_ev = iwe_stream_add_event(info, current_ev, end_buf,
5194 &iwe, IW_EV_FREQ_LEN);
5195 }
5196
5197 /* Add quality statistics. level and noise in dB. No link quality */
5198 iwe.cmd = IWEVQUAL;
5199 iwe.u.qual.updated = IW_QUAL_DBM | IW_QUAL_QUAL_INVALID;
5200 iwe.u.qual.level = bss->level - 0x95;
5201 iwe.u.qual.noise = bss->noise - 0x95;
5202 /* Wireless tools prior to 27.pre22 will show link quality
5203 * anyway, so we provide a reasonable value. */
5204 if (iwe.u.qual.level > iwe.u.qual.noise)
5205 iwe.u.qual.qual = iwe.u.qual.level - iwe.u.qual.noise;
5206 else
5207 iwe.u.qual.qual = 0;
5208 current_ev = iwe_stream_add_event(info, current_ev, end_buf,
5209 &iwe, IW_EV_QUAL_LEN);
5210
5211 /* Add encryption capability */
5212 iwe.cmd = SIOCGIWENCODE;
5213 if (capabilities & WLAN_CAPABILITY_PRIVACY)
5214 iwe.u.data.flags = IW_ENCODE_ENABLED | IW_ENCODE_NOKEY;
5215 else
5216 iwe.u.data.flags = IW_ENCODE_DISABLED;
5217 iwe.u.data.length = 0;
5218 current_ev = iwe_stream_add_point(info, current_ev, end_buf,
5219 &iwe, NULL);
5220
5221 /* WPA IE */
5222 ie = orinoco_get_wpa_ie(bss->data, sizeof(bss->data));
5223 if (ie) {
5224 iwe.cmd = IWEVGENIE;
5225 iwe.u.data.length = ie[1] + 2;
5226 current_ev = iwe_stream_add_point(info, current_ev, end_buf,
5227 &iwe, ie);
5228 }
5229
5230 /* RSN IE */
Johannes Berg2c7060022008-10-30 22:09:54 +01005231 ie = orinoco_get_ie(bss->data, sizeof(bss->data), WLAN_EID_RSN);
David Kilroy01632fa2008-08-21 23:27:58 +01005232 if (ie) {
5233 iwe.cmd = IWEVGENIE;
5234 iwe.u.data.length = ie[1] + 2;
5235 current_ev = iwe_stream_add_point(info, current_ev, end_buf,
5236 &iwe, ie);
5237 }
5238
Johannes Berg2c7060022008-10-30 22:09:54 +01005239 ie = orinoco_get_ie(bss->data, sizeof(bss->data), WLAN_EID_SUPP_RATES);
David Kilroy01632fa2008-08-21 23:27:58 +01005240 if (ie) {
5241 char *p = current_ev + iwe_stream_lcp_len(info);
5242 int i;
5243
5244 iwe.cmd = SIOCGIWRATE;
5245 /* Those two flags are ignored... */
5246 iwe.u.bitrate.fixed = iwe.u.bitrate.disabled = 0;
5247
5248 for (i = 2; i < (ie[1] + 2); i++) {
5249 iwe.u.bitrate.value = ((ie[i] & 0x7F) * 500000);
5250 p = iwe_stream_add_value(info, current_ev, p, end_buf,
5251 &iwe, IW_EV_PARAM_LEN);
5252 }
5253 /* Check if we added any event */
5254 if (p > (current_ev + iwe_stream_lcp_len(info)))
5255 current_ev = p;
5256 }
5257
5258 /* Timestamp */
5259 iwe.cmd = IWEVCUSTOM;
David Kilroy75d31cf2008-09-12 22:28:18 +01005260 iwe.u.data.length =
5261 snprintf(custom, MAX_CUSTOM_LEN, "tsf=%016llx",
5262 (unsigned long long) le64_to_cpu(bss->timestamp));
David Kilroy01632fa2008-08-21 23:27:58 +01005263 if (iwe.u.data.length)
5264 current_ev = iwe_stream_add_point(info, current_ev, end_buf,
5265 &iwe, custom);
5266
5267 /* Beacon interval */
5268 iwe.cmd = IWEVCUSTOM;
5269 iwe.u.data.length = snprintf(custom, MAX_CUSTOM_LEN,
5270 "bcn_int=%d",
5271 le16_to_cpu(bss->beacon_interval));
5272 if (iwe.u.data.length)
5273 current_ev = iwe_stream_add_point(info, current_ev, end_buf,
5274 &iwe, custom);
5275
5276 /* Capabilites */
5277 iwe.cmd = IWEVCUSTOM;
5278 iwe.u.data.length = snprintf(custom, MAX_CUSTOM_LEN,
5279 "capab=0x%04x",
5280 capabilities);
5281 if (iwe.u.data.length)
5282 current_ev = iwe_stream_add_point(info, current_ev, end_buf,
5283 &iwe, custom);
5284
5285 /* Add EXTRA: Age to display seconds since last beacon/probe response
5286 * for given network. */
5287 iwe.cmd = IWEVCUSTOM;
5288 iwe.u.data.length = snprintf(custom, MAX_CUSTOM_LEN,
5289 " Last beacon: %dms ago",
5290 jiffies_to_msecs(jiffies - last_scanned));
5291 if (iwe.u.data.length)
5292 current_ev = iwe_stream_add_point(info, current_ev, end_buf,
5293 &iwe, custom);
5294
5295 return current_ev;
5296}
5297
Christoph Hellwig95dd91f2005-06-19 01:27:56 +02005298/* Return results of a scan */
5299static int orinoco_ioctl_getscan(struct net_device *dev,
5300 struct iw_request_info *info,
5301 struct iw_point *srq,
5302 char *extra)
5303{
5304 struct orinoco_private *priv = netdev_priv(dev);
5305 int err = 0;
5306 unsigned long flags;
Dan Williams1e3428e2007-10-10 23:56:25 -04005307 char *current_ev = extra;
Christoph Hellwig95dd91f2005-06-19 01:27:56 +02005308
5309 if (orinoco_lock(priv, &flags) != 0)
5310 return -EBUSY;
5311
Dan Williams1e3428e2007-10-10 23:56:25 -04005312 if (priv->scan_inprogress) {
5313 /* Important note : we don't want to block the caller
5314 * until results are ready for various reasons.
5315 * First, managing wait queues is complex and racy.
5316 * Second, we grab some rtnetlink lock before comming
5317 * here (in dev_ioctl()).
5318 * Third, we generate an Wireless Event, so the
5319 * caller can wait itself on that - Jean II */
5320 err = -EAGAIN;
5321 goto out;
Christoph Hellwig95dd91f2005-06-19 01:27:56 +02005322 }
Dan Williams1e3428e2007-10-10 23:56:25 -04005323
David Kilroy01632fa2008-08-21 23:27:58 +01005324 if (priv->has_ext_scan) {
5325 struct xbss_element *bss;
Dan Williams1e3428e2007-10-10 23:56:25 -04005326
David Kilroy01632fa2008-08-21 23:27:58 +01005327 list_for_each_entry(bss, &priv->bss_list, list) {
5328 /* Translate this entry to WE format */
5329 current_ev =
5330 orinoco_translate_ext_scan(dev, info,
5331 current_ev,
5332 extra + srq->length,
5333 &bss->bss,
5334 bss->last_scanned);
5335
5336 /* Check if there is space for one more entry */
5337 if ((extra + srq->length - current_ev)
5338 <= IW_EV_ADDR_LEN) {
5339 /* Ask user space to try again with a
5340 * bigger buffer */
5341 err = -E2BIG;
5342 goto out;
5343 }
5344 }
5345
5346 } else {
5347 struct bss_element *bss;
5348
5349 list_for_each_entry(bss, &priv->bss_list, list) {
5350 /* Translate this entry to WE format */
5351 current_ev = orinoco_translate_scan(dev, info,
5352 current_ev,
5353 extra + srq->length,
5354 &bss->bss,
5355 bss->last_scanned);
5356
5357 /* Check if there is space for one more entry */
5358 if ((extra + srq->length - current_ev)
5359 <= IW_EV_ADDR_LEN) {
5360 /* Ask user space to try again with a
5361 * bigger buffer */
5362 err = -E2BIG;
5363 goto out;
5364 }
Dan Williams1e3428e2007-10-10 23:56:25 -04005365 }
5366 }
5367
5368 srq->length = (current_ev - extra);
5369 srq->flags = (__u16) priv->scan_mode;
5370
5371out:
Christoph Hellwig95dd91f2005-06-19 01:27:56 +02005372 orinoco_unlock(priv, &flags);
5373 return err;
5374}
5375
Christoph Hellwig620554e2005-06-19 01:27:33 +02005376/* Commit handler, called after set operations */
5377static int orinoco_ioctl_commit(struct net_device *dev,
5378 struct iw_request_info *info,
5379 void *wrqu,
5380 char *extra)
Linus Torvalds1da177e2005-04-16 15:20:36 -07005381{
5382 struct orinoco_private *priv = netdev_priv(dev);
Christoph Hellwig620554e2005-06-19 01:27:33 +02005383 struct hermes *hw = &priv->hw;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005384 unsigned long flags;
Christoph Hellwig620554e2005-06-19 01:27:33 +02005385 int err = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005386
Christoph Hellwig620554e2005-06-19 01:27:33 +02005387 if (!priv->open)
5388 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005389
Christoph Hellwig620554e2005-06-19 01:27:33 +02005390 if (priv->broken_disableport) {
David Howellsc4028952006-11-22 14:57:56 +00005391 orinoco_reset(&priv->reset_work);
Christoph Hellwig620554e2005-06-19 01:27:33 +02005392 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005393 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07005394
Christoph Hellwig620554e2005-06-19 01:27:33 +02005395 if (orinoco_lock(priv, &flags) != 0)
5396 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005397
Christoph Hellwig620554e2005-06-19 01:27:33 +02005398 err = hermes_disable_port(hw, 0);
5399 if (err) {
5400 printk(KERN_WARNING "%s: Unable to disable port "
5401 "while reconfiguring card\n", dev->name);
5402 priv->broken_disableport = 1;
5403 goto out;
5404 }
5405
5406 err = __orinoco_program_rids(dev);
5407 if (err) {
5408 printk(KERN_WARNING "%s: Unable to reconfigure card\n",
5409 dev->name);
5410 goto out;
5411 }
5412
5413 err = hermes_enable_port(hw, 0);
5414 if (err) {
5415 printk(KERN_WARNING "%s: Unable to enable port while reconfiguring card\n",
5416 dev->name);
5417 goto out;
5418 }
5419
5420 out:
5421 if (err) {
5422 printk(KERN_WARNING "%s: Resetting instead...\n", dev->name);
5423 schedule_work(&priv->reset_work);
5424 err = 0;
5425 }
5426
5427 orinoco_unlock(priv, &flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005428 return err;
5429}
5430
Christoph Hellwig620554e2005-06-19 01:27:33 +02005431static const struct iw_priv_args orinoco_privtab[] = {
5432 { SIOCIWFIRSTPRIV + 0x0, 0, 0, "force_reset" },
5433 { SIOCIWFIRSTPRIV + 0x1, 0, 0, "card_reset" },
5434 { SIOCIWFIRSTPRIV + 0x2, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
5435 0, "set_port3" },
5436 { SIOCIWFIRSTPRIV + 0x3, 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
5437 "get_port3" },
5438 { SIOCIWFIRSTPRIV + 0x4, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
5439 0, "set_preamble" },
5440 { SIOCIWFIRSTPRIV + 0x5, 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
5441 "get_preamble" },
5442 { SIOCIWFIRSTPRIV + 0x6, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
5443 0, "set_ibssport" },
5444 { SIOCIWFIRSTPRIV + 0x7, 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
5445 "get_ibssport" },
5446 { SIOCIWFIRSTPRIV + 0x9, 0, IW_PRIV_TYPE_BYTE | MAX_RID_LEN,
5447 "get_rid" },
5448};
5449
5450
5451/*
5452 * Structures to export the Wireless Handlers
5453 */
5454
David Kilroy409644a2008-08-21 23:28:01 +01005455#define STD_IW_HANDLER(id, func) \
5456 [IW_IOCTL_IDX(id)] = (iw_handler) func
Christoph Hellwig620554e2005-06-19 01:27:33 +02005457static const iw_handler orinoco_handler[] = {
David Kilroy409644a2008-08-21 23:28:01 +01005458 STD_IW_HANDLER(SIOCSIWCOMMIT, orinoco_ioctl_commit),
5459 STD_IW_HANDLER(SIOCGIWNAME, orinoco_ioctl_getname),
5460 STD_IW_HANDLER(SIOCSIWFREQ, orinoco_ioctl_setfreq),
5461 STD_IW_HANDLER(SIOCGIWFREQ, orinoco_ioctl_getfreq),
5462 STD_IW_HANDLER(SIOCSIWMODE, orinoco_ioctl_setmode),
5463 STD_IW_HANDLER(SIOCGIWMODE, orinoco_ioctl_getmode),
5464 STD_IW_HANDLER(SIOCSIWSENS, orinoco_ioctl_setsens),
5465 STD_IW_HANDLER(SIOCGIWSENS, orinoco_ioctl_getsens),
5466 STD_IW_HANDLER(SIOCGIWRANGE, orinoco_ioctl_getiwrange),
5467 STD_IW_HANDLER(SIOCSIWSPY, iw_handler_set_spy),
5468 STD_IW_HANDLER(SIOCGIWSPY, iw_handler_get_spy),
5469 STD_IW_HANDLER(SIOCSIWTHRSPY, iw_handler_set_thrspy),
5470 STD_IW_HANDLER(SIOCGIWTHRSPY, iw_handler_get_thrspy),
5471 STD_IW_HANDLER(SIOCSIWAP, orinoco_ioctl_setwap),
5472 STD_IW_HANDLER(SIOCGIWAP, orinoco_ioctl_getwap),
5473 STD_IW_HANDLER(SIOCSIWSCAN, orinoco_ioctl_setscan),
5474 STD_IW_HANDLER(SIOCGIWSCAN, orinoco_ioctl_getscan),
5475 STD_IW_HANDLER(SIOCSIWESSID, orinoco_ioctl_setessid),
5476 STD_IW_HANDLER(SIOCGIWESSID, orinoco_ioctl_getessid),
5477 STD_IW_HANDLER(SIOCSIWNICKN, orinoco_ioctl_setnick),
5478 STD_IW_HANDLER(SIOCGIWNICKN, orinoco_ioctl_getnick),
5479 STD_IW_HANDLER(SIOCSIWRATE, orinoco_ioctl_setrate),
5480 STD_IW_HANDLER(SIOCGIWRATE, orinoco_ioctl_getrate),
5481 STD_IW_HANDLER(SIOCSIWRTS, orinoco_ioctl_setrts),
5482 STD_IW_HANDLER(SIOCGIWRTS, orinoco_ioctl_getrts),
5483 STD_IW_HANDLER(SIOCSIWFRAG, orinoco_ioctl_setfrag),
5484 STD_IW_HANDLER(SIOCGIWFRAG, orinoco_ioctl_getfrag),
5485 STD_IW_HANDLER(SIOCGIWRETRY, orinoco_ioctl_getretry),
5486 STD_IW_HANDLER(SIOCSIWENCODE, orinoco_ioctl_setiwencode),
5487 STD_IW_HANDLER(SIOCGIWENCODE, orinoco_ioctl_getiwencode),
5488 STD_IW_HANDLER(SIOCSIWPOWER, orinoco_ioctl_setpower),
5489 STD_IW_HANDLER(SIOCGIWPOWER, orinoco_ioctl_getpower),
David Kilroyd03032a2008-08-21 23:28:02 +01005490 STD_IW_HANDLER(SIOCSIWGENIE, orinoco_ioctl_set_genie),
5491 STD_IW_HANDLER(SIOCGIWGENIE, orinoco_ioctl_get_genie),
5492 STD_IW_HANDLER(SIOCSIWMLME, orinoco_ioctl_set_mlme),
5493 STD_IW_HANDLER(SIOCSIWAUTH, orinoco_ioctl_set_auth),
5494 STD_IW_HANDLER(SIOCGIWAUTH, orinoco_ioctl_get_auth),
5495 STD_IW_HANDLER(SIOCSIWENCODEEXT, orinoco_ioctl_set_encodeext),
5496 STD_IW_HANDLER(SIOCGIWENCODEEXT, orinoco_ioctl_get_encodeext),
Christoph Hellwig620554e2005-06-19 01:27:33 +02005497};
5498
5499
5500/*
5501 Added typecasting since we no longer use iwreq_data -- Moustafa
5502 */
5503static const iw_handler orinoco_private_handler[] = {
Peter Hagervall6b9b97c2005-07-27 01:14:46 -07005504 [0] = (iw_handler) orinoco_ioctl_reset,
5505 [1] = (iw_handler) orinoco_ioctl_reset,
5506 [2] = (iw_handler) orinoco_ioctl_setport3,
5507 [3] = (iw_handler) orinoco_ioctl_getport3,
5508 [4] = (iw_handler) orinoco_ioctl_setpreamble,
5509 [5] = (iw_handler) orinoco_ioctl_getpreamble,
5510 [6] = (iw_handler) orinoco_ioctl_setibssport,
5511 [7] = (iw_handler) orinoco_ioctl_getibssport,
5512 [9] = (iw_handler) orinoco_ioctl_getrid,
Christoph Hellwig620554e2005-06-19 01:27:33 +02005513};
5514
5515static const struct iw_handler_def orinoco_handler_def = {
5516 .num_standard = ARRAY_SIZE(orinoco_handler),
5517 .num_private = ARRAY_SIZE(orinoco_private_handler),
5518 .num_private_args = ARRAY_SIZE(orinoco_privtab),
5519 .standard = orinoco_handler,
5520 .private = orinoco_private_handler,
5521 .private_args = orinoco_privtab,
Pavel Roskin343c6862005-09-09 18:43:02 -04005522 .get_wireless_stats = orinoco_get_wireless_stats,
Christoph Hellwig620554e2005-06-19 01:27:33 +02005523};
Linus Torvalds1da177e2005-04-16 15:20:36 -07005524
Christoph Hellwig1fab2e82005-06-19 01:27:40 +02005525static void orinoco_get_drvinfo(struct net_device *dev,
5526 struct ethtool_drvinfo *info)
5527{
5528 struct orinoco_private *priv = netdev_priv(dev);
5529
5530 strncpy(info->driver, DRIVER_NAME, sizeof(info->driver) - 1);
5531 strncpy(info->version, DRIVER_VERSION, sizeof(info->version) - 1);
5532 strncpy(info->fw_version, priv->fw_name, sizeof(info->fw_version) - 1);
Greg Kroah-Hartman43cb76d2002-04-09 12:14:34 -07005533 if (dev->dev.parent)
Kay Sieversfb28ad32008-11-10 13:55:14 -08005534 strncpy(info->bus_info, dev_name(dev->dev.parent),
Christoph Hellwig1fab2e82005-06-19 01:27:40 +02005535 sizeof(info->bus_info) - 1);
5536 else
5537 snprintf(info->bus_info, sizeof(info->bus_info) - 1,
5538 "PCMCIA %p", priv->hw.iobase);
5539}
5540
Jeff Garzik7282d492006-09-13 14:30:00 -04005541static const struct ethtool_ops orinoco_ethtool_ops = {
Christoph Hellwig1fab2e82005-06-19 01:27:40 +02005542 .get_drvinfo = orinoco_get_drvinfo,
5543 .get_link = ethtool_op_get_link,
5544};
Linus Torvalds1da177e2005-04-16 15:20:36 -07005545
5546/********************************************************************/
Linus Torvalds1da177e2005-04-16 15:20:36 -07005547/* Module initialization */
5548/********************************************************************/
5549
Linus Torvalds1da177e2005-04-16 15:20:36 -07005550/* Can't be declared "const" or the whole __initdata section will
5551 * become const */
5552static char version[] __initdata = DRIVER_NAME " " DRIVER_VERSION
5553 " (David Gibson <hermes@gibson.dropbear.id.au>, "
5554 "Pavel Roskin <proski@gnu.org>, et al)";
5555
5556static int __init init_orinoco(void)
5557{
5558 printk(KERN_DEBUG "%s\n", version);
5559 return 0;
5560}
5561
5562static void __exit exit_orinoco(void)
5563{
5564}
5565
5566module_init(init_orinoco);
5567module_exit(exit_orinoco);