blob: f49cabd7087783a1188b3664fc0b23faea8459ea [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 Kilroy01632fa2008-08-21 23:27:58 +0100285static inline u8 *orinoco_get_ie(u8 *data, size_t len,
Johannes Berg2c7060022008-10-30 22:09:54 +0100286 enum ieee80211_eid eid)
David Kilroy01632fa2008-08-21 23:27:58 +0100287{
288 u8 *p = data;
289 while ((p + 2) < (data + len)) {
290 if (p[0] == eid)
291 return p;
292 p += p[1] + 2;
293 }
294 return NULL;
295}
296
297#define WPA_OUI_TYPE "\x00\x50\xF2\x01"
298#define WPA_SELECTOR_LEN 4
299static inline u8 *orinoco_get_wpa_ie(u8 *data, size_t len)
300{
301 u8 *p = data;
302 while ((p + 2 + WPA_SELECTOR_LEN) < (data + len)) {
Johannes Berg2c7060022008-10-30 22:09:54 +0100303 if ((p[0] == WLAN_EID_GENERIC) &&
David Kilroy01632fa2008-08-21 23:27:58 +0100304 (memcmp(&p[2], WPA_OUI_TYPE, WPA_SELECTOR_LEN) == 0))
305 return p;
306 p += p[1] + 2;
307 }
308 return NULL;
Dan Williams1e3428e2007-10-10 23:56:25 -0400309}
310
David Kilroy3994d502008-08-21 23:27:54 +0100311
312/********************************************************************/
Linus Torvalds1da177e2005-04-16 15:20:36 -0700313/* Device methods */
314/********************************************************************/
315
316static int orinoco_open(struct net_device *dev)
317{
318 struct orinoco_private *priv = netdev_priv(dev);
319 unsigned long flags;
320 int err;
321
322 if (orinoco_lock(priv, &flags) != 0)
323 return -EBUSY;
324
325 err = __orinoco_up(dev);
326
David Kilroya94e8422009-02-04 23:05:44 +0000327 if (!err)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700328 priv->open = 1;
329
330 orinoco_unlock(priv, &flags);
331
332 return err;
333}
334
Christoph Hellwigad8f4512005-05-14 17:30:17 +0200335static int orinoco_stop(struct net_device *dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700336{
337 struct orinoco_private *priv = netdev_priv(dev);
338 int err = 0;
339
340 /* We mustn't use orinoco_lock() here, because we need to be
341 able to close the interface even if hw_unavailable is set
342 (e.g. as we're released after a PC Card removal) */
343 spin_lock_irq(&priv->lock);
344
345 priv->open = 0;
346
347 err = __orinoco_down(dev);
348
349 spin_unlock_irq(&priv->lock);
350
351 return err;
352}
353
354static struct net_device_stats *orinoco_get_stats(struct net_device *dev)
355{
356 struct orinoco_private *priv = netdev_priv(dev);
David Kilroy6fe9deb2009-02-04 23:05:43 +0000357
Linus Torvalds1da177e2005-04-16 15:20:36 -0700358 return &priv->stats;
359}
360
361static struct iw_statistics *orinoco_get_wireless_stats(struct net_device *dev)
362{
363 struct orinoco_private *priv = netdev_priv(dev);
364 hermes_t *hw = &priv->hw;
365 struct iw_statistics *wstats = &priv->wstats;
David Gibsone67d9d92005-05-12 20:01:22 -0400366 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700367 unsigned long flags;
368
David Kilroya94e8422009-02-04 23:05:44 +0000369 if (!netif_device_present(dev)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700370 printk(KERN_WARNING "%s: get_wireless_stats() called while device not present\n",
371 dev->name);
372 return NULL; /* FIXME: Can we do better than this? */
373 }
374
David Gibsone67d9d92005-05-12 20:01:22 -0400375 /* If busy, return the old stats. Returning NULL may cause
376 * the interface to disappear from /proc/net/wireless */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700377 if (orinoco_lock(priv, &flags) != 0)
David Gibsone67d9d92005-05-12 20:01:22 -0400378 return wstats;
379
380 /* We can't really wait for the tallies inquiry command to
381 * complete, so we just use the previous results and trigger
382 * a new tallies inquiry command for next time - Jean II */
383 /* FIXME: Really we should wait for the inquiry to come back -
384 * as it is the stats we give don't make a whole lot of sense.
385 * Unfortunately, it's not clear how to do that within the
386 * wireless extensions framework: I think we're in user
387 * context, but a lock seems to be held by the time we get in
388 * here so we're not safe to sleep here. */
389 hermes_inquire(hw, HERMES_INQ_TALLIES);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700390
391 if (priv->iw_mode == IW_MODE_ADHOC) {
392 memset(&wstats->qual, 0, sizeof(wstats->qual));
393 /* If a spy address is defined, we report stats of the
394 * first spy address - Jean II */
395 if (SPY_NUMBER(priv)) {
Pavel Roskin343c6862005-09-09 18:43:02 -0400396 wstats->qual.qual = priv->spy_data.spy_stat[0].qual;
397 wstats->qual.level = priv->spy_data.spy_stat[0].level;
398 wstats->qual.noise = priv->spy_data.spy_stat[0].noise;
David Kilroyb2f30a02009-02-04 23:05:46 +0000399 wstats->qual.updated =
400 priv->spy_data.spy_stat[0].updated;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700401 }
402 } else {
403 struct {
Pavel Roskina208c4e2006-04-07 04:10:26 -0400404 __le16 qual, signal, noise, unused;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700405 } __attribute__ ((packed)) cq;
406
407 err = HERMES_READ_RECORD(hw, USER_BAP,
408 HERMES_RID_COMMSQUALITY, &cq);
David Gibsone67d9d92005-05-12 20:01:22 -0400409
410 if (!err) {
411 wstats->qual.qual = (int)le16_to_cpu(cq.qual);
412 wstats->qual.level = (int)le16_to_cpu(cq.signal) - 0x95;
413 wstats->qual.noise = (int)le16_to_cpu(cq.noise) - 0x95;
David Kilroyb2f30a02009-02-04 23:05:46 +0000414 wstats->qual.updated =
415 IW_QUAL_ALL_UPDATED | IW_QUAL_DBM;
David Gibsone67d9d92005-05-12 20:01:22 -0400416 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700417 }
418
Linus Torvalds1da177e2005-04-16 15:20:36 -0700419 orinoco_unlock(priv, &flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700420 return wstats;
421}
422
423static void orinoco_set_multicast_list(struct net_device *dev)
424{
425 struct orinoco_private *priv = netdev_priv(dev);
426 unsigned long flags;
427
428 if (orinoco_lock(priv, &flags) != 0) {
429 printk(KERN_DEBUG "%s: orinoco_set_multicast_list() "
430 "called when hw_unavailable\n", dev->name);
431 return;
432 }
433
434 __orinoco_set_multicast_list(dev);
435 orinoco_unlock(priv, &flags);
436}
437
438static int orinoco_change_mtu(struct net_device *dev, int new_mtu)
439{
440 struct orinoco_private *priv = netdev_priv(dev);
441
David Kilroya94e8422009-02-04 23:05:44 +0000442 if ((new_mtu < ORINOCO_MIN_MTU) || (new_mtu > ORINOCO_MAX_MTU))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700443 return -EINVAL;
444
Johannes Berg2c7060022008-10-30 22:09:54 +0100445 /* MTU + encapsulation + header length */
David Kilroya94e8422009-02-04 23:05:44 +0000446 if ((new_mtu + ENCAPS_OVERHEAD + sizeof(struct ieee80211_hdr)) >
447 (priv->nicbuf_size - ETH_HLEN))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700448 return -EINVAL;
449
450 dev->mtu = new_mtu;
451
452 return 0;
453}
454
455/********************************************************************/
456/* Tx path */
457/********************************************************************/
458
459static int orinoco_xmit(struct sk_buff *skb, struct net_device *dev)
460{
461 struct orinoco_private *priv = netdev_priv(dev);
462 struct net_device_stats *stats = &priv->stats;
463 hermes_t *hw = &priv->hw;
464 int err = 0;
465 u16 txfid = priv->txfid;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700466 struct ethhdr *eh;
David Kilroy6eecad72008-08-21 23:27:56 +0100467 int tx_control;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700468 unsigned long flags;
469
David Kilroya94e8422009-02-04 23:05:44 +0000470 if (!netif_running(dev)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700471 printk(KERN_ERR "%s: Tx on stopped device!\n",
472 dev->name);
Pavel Roskinb34b8672006-04-07 04:10:36 -0400473 return NETDEV_TX_BUSY;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700474 }
David Kilroy6fe9deb2009-02-04 23:05:43 +0000475
Linus Torvalds1da177e2005-04-16 15:20:36 -0700476 if (netif_queue_stopped(dev)) {
David Kilroy6fe9deb2009-02-04 23:05:43 +0000477 printk(KERN_DEBUG "%s: Tx while transmitter busy!\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700478 dev->name);
Pavel Roskinb34b8672006-04-07 04:10:36 -0400479 return NETDEV_TX_BUSY;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700480 }
David Kilroy6fe9deb2009-02-04 23:05:43 +0000481
Linus Torvalds1da177e2005-04-16 15:20:36 -0700482 if (orinoco_lock(priv, &flags) != 0) {
483 printk(KERN_ERR "%s: orinoco_xmit() called while hw_unavailable\n",
484 dev->name);
Pavel Roskinb34b8672006-04-07 04:10:36 -0400485 return NETDEV_TX_BUSY;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700486 }
487
David Kilroya94e8422009-02-04 23:05:44 +0000488 if (!netif_carrier_ok(dev) || (priv->iw_mode == IW_MODE_MONITOR)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700489 /* Oops, the firmware hasn't established a connection,
David Kilroy6fe9deb2009-02-04 23:05:43 +0000490 silently drop the packet (this seems to be the
491 safest approach). */
Pavel Roskin470e2aa2006-04-07 04:10:43 -0400492 goto drop;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700493 }
494
Pavel Roskin8d5be082006-04-07 04:10:41 -0400495 /* Check packet length */
Pavel Roskina28dc812006-04-07 04:10:45 -0400496 if (skb->len < ETH_HLEN)
Pavel Roskin470e2aa2006-04-07 04:10:43 -0400497 goto drop;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700498
David Kilroy6eecad72008-08-21 23:27:56 +0100499 tx_control = HERMES_TXCTRL_TX_OK | HERMES_TXCTRL_TX_EX;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700500
David Kilroy23edcc42008-08-21 23:28:05 +0100501 if (priv->encode_alg == IW_ENCODE_ALG_TKIP)
502 tx_control |= (priv->tx_key << HERMES_MIC_KEY_ID_SHIFT) |
503 HERMES_TXCTRL_MIC;
504
David Kilroy6eecad72008-08-21 23:27:56 +0100505 if (priv->has_alt_txcntl) {
506 /* WPA enabled firmwares have tx_cntl at the end of
507 * the 802.11 header. So write zeroed descriptor and
508 * 802.11 header at the same time
509 */
510 char desc[HERMES_802_3_OFFSET];
511 __le16 *txcntl = (__le16 *) &desc[HERMES_TXCNTL2_OFFSET];
512
513 memset(&desc, 0, sizeof(desc));
514
515 *txcntl = cpu_to_le16(tx_control);
516 err = hermes_bap_pwrite(hw, USER_BAP, &desc, sizeof(desc),
517 txfid, 0);
518 if (err) {
519 if (net_ratelimit())
520 printk(KERN_ERR "%s: Error %d writing Tx "
521 "descriptor to BAP\n", dev->name, err);
522 goto busy;
523 }
524 } else {
525 struct hermes_tx_descriptor desc;
526
527 memset(&desc, 0, sizeof(desc));
528
529 desc.tx_control = cpu_to_le16(tx_control);
530 err = hermes_bap_pwrite(hw, USER_BAP, &desc, sizeof(desc),
531 txfid, 0);
532 if (err) {
533 if (net_ratelimit())
534 printk(KERN_ERR "%s: Error %d writing Tx "
535 "descriptor to BAP\n", dev->name, err);
536 goto busy;
537 }
538
539 /* Clear the 802.11 header and data length fields - some
540 * firmwares (e.g. Lucent/Agere 8.xx) appear to get confused
541 * if this isn't done. */
542 hermes_clear_words(hw, HERMES_DATA0,
543 HERMES_802_3_OFFSET - HERMES_802_11_OFFSET);
544 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700545
David Kilroy23edcc42008-08-21 23:28:05 +0100546 eh = (struct ethhdr *)skb->data;
547
Linus Torvalds1da177e2005-04-16 15:20:36 -0700548 /* Encapsulate Ethernet-II frames */
549 if (ntohs(eh->h_proto) > ETH_DATA_LEN) { /* Ethernet-II frame */
Pavel Roskina28dc812006-04-07 04:10:45 -0400550 struct header_struct {
551 struct ethhdr eth; /* 802.3 header */
552 u8 encap[6]; /* 802.2 header */
553 } __attribute__ ((packed)) hdr;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700554
Pavel Roskina28dc812006-04-07 04:10:45 -0400555 /* Strip destination and source from the data */
556 skb_pull(skb, 2 * ETH_ALEN);
Pavel Roskina28dc812006-04-07 04:10:45 -0400557
558 /* And move them to a separate header */
559 memcpy(&hdr.eth, eh, 2 * ETH_ALEN);
560 hdr.eth.h_proto = htons(sizeof(encaps_hdr) + skb->len);
561 memcpy(hdr.encap, encaps_hdr, sizeof(encaps_hdr));
562
David Kilroy23edcc42008-08-21 23:28:05 +0100563 /* Insert the SNAP header */
564 if (skb_headroom(skb) < sizeof(hdr)) {
565 printk(KERN_ERR
566 "%s: Not enough headroom for 802.2 headers %d\n",
567 dev->name, skb_headroom(skb));
568 goto drop;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700569 }
David Kilroy23edcc42008-08-21 23:28:05 +0100570 eh = (struct ethhdr *) skb_push(skb, sizeof(hdr));
571 memcpy(eh, &hdr, sizeof(hdr));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700572 }
573
Pavel Roskina28dc812006-04-07 04:10:45 -0400574 err = hermes_bap_pwrite(hw, USER_BAP, skb->data, skb->len,
David Kilroy23edcc42008-08-21 23:28:05 +0100575 txfid, HERMES_802_3_OFFSET);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700576 if (err) {
577 printk(KERN_ERR "%s: Error %d writing packet to BAP\n",
578 dev->name, err);
Pavel Roskin470e2aa2006-04-07 04:10:43 -0400579 goto busy;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700580 }
581
David Kilroy23edcc42008-08-21 23:28:05 +0100582 /* Calculate Michael MIC */
583 if (priv->encode_alg == IW_ENCODE_ALG_TKIP) {
584 u8 mic_buf[MICHAEL_MIC_LEN + 1];
585 u8 *mic;
586 size_t offset;
587 size_t len;
588
589 if (skb->len % 2) {
590 /* MIC start is on an odd boundary */
591 mic_buf[0] = skb->data[skb->len - 1];
592 mic = &mic_buf[1];
593 offset = skb->len - 1;
594 len = MICHAEL_MIC_LEN + 1;
595 } else {
596 mic = &mic_buf[0];
597 offset = skb->len;
598 len = MICHAEL_MIC_LEN;
599 }
600
David Kilroy4adb4742009-02-04 23:05:51 +0000601 orinoco_mic(priv->tx_tfm_mic,
David Kilroy23edcc42008-08-21 23:28:05 +0100602 priv->tkip_key[priv->tx_key].tx_mic,
603 eh->h_dest, eh->h_source, 0 /* priority */,
604 skb->data + ETH_HLEN, skb->len - ETH_HLEN, mic);
605
606 /* Write the MIC */
607 err = hermes_bap_pwrite(hw, USER_BAP, &mic_buf[0], len,
608 txfid, HERMES_802_3_OFFSET + offset);
609 if (err) {
610 printk(KERN_ERR "%s: Error %d writing MIC to BAP\n",
611 dev->name, err);
612 goto busy;
613 }
614 }
615
Linus Torvalds1da177e2005-04-16 15:20:36 -0700616 /* Finally, we actually initiate the send */
617 netif_stop_queue(dev);
618
619 err = hermes_docmd_wait(hw, HERMES_CMD_TX | HERMES_CMD_RECL,
620 txfid, NULL);
621 if (err) {
622 netif_start_queue(dev);
Andrew Mortonc367c212005-10-19 21:23:44 -0700623 if (net_ratelimit())
624 printk(KERN_ERR "%s: Error %d transmitting packet\n",
625 dev->name, err);
Pavel Roskin470e2aa2006-04-07 04:10:43 -0400626 goto busy;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700627 }
628
629 dev->trans_start = jiffies;
David Kilroy23edcc42008-08-21 23:28:05 +0100630 stats->tx_bytes += HERMES_802_3_OFFSET + skb->len;
Pavel Roskin470e2aa2006-04-07 04:10:43 -0400631 goto ok;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700632
Pavel Roskin470e2aa2006-04-07 04:10:43 -0400633 drop:
634 stats->tx_errors++;
635 stats->tx_dropped++;
636
637 ok:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700638 orinoco_unlock(priv, &flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700639 dev_kfree_skb(skb);
Pavel Roskinb34b8672006-04-07 04:10:36 -0400640 return NETDEV_TX_OK;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700641
Pavel Roskin470e2aa2006-04-07 04:10:43 -0400642 busy:
Jiri Benc2c1bd262006-04-07 04:10:47 -0400643 if (err == -EIO)
644 schedule_work(&priv->reset_work);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700645 orinoco_unlock(priv, &flags);
Pavel Roskinb34b8672006-04-07 04:10:36 -0400646 return NETDEV_TX_BUSY;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700647}
648
649static void __orinoco_ev_alloc(struct net_device *dev, hermes_t *hw)
650{
651 struct orinoco_private *priv = netdev_priv(dev);
652 u16 fid = hermes_read_regn(hw, ALLOCFID);
653
654 if (fid != priv->txfid) {
655 if (fid != DUMMY_FID)
656 printk(KERN_WARNING "%s: Allocate event on unexpected fid (%04X)\n",
657 dev->name, fid);
658 return;
659 }
660
661 hermes_write_regn(hw, ALLOCFID, DUMMY_FID);
662}
663
664static void __orinoco_ev_tx(struct net_device *dev, hermes_t *hw)
665{
666 struct orinoco_private *priv = netdev_priv(dev);
667 struct net_device_stats *stats = &priv->stats;
668
669 stats->tx_packets++;
670
671 netif_wake_queue(dev);
672
673 hermes_write_regn(hw, TXCOMPLFID, DUMMY_FID);
674}
675
676static void __orinoco_ev_txexc(struct net_device *dev, hermes_t *hw)
677{
678 struct orinoco_private *priv = netdev_priv(dev);
679 struct net_device_stats *stats = &priv->stats;
680 u16 fid = hermes_read_regn(hw, TXCOMPLFID);
Pavel Roskind133ae42005-09-23 04:18:06 -0400681 u16 status;
Pavel Roskin30c2d3b2006-04-07 04:10:34 -0400682 struct hermes_txexc_data hdr;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700683 int err = 0;
684
685 if (fid == DUMMY_FID)
686 return; /* Nothing's really happened */
687
Pavel Roskin48ca7032005-09-23 04:18:06 -0400688 /* Read part of the frame header - we need status and addr1 */
Christoph Hellwig95dd91f2005-06-19 01:27:56 +0200689 err = hermes_bap_pread(hw, IRQ_BAP, &hdr,
Pavel Roskin30c2d3b2006-04-07 04:10:34 -0400690 sizeof(struct hermes_txexc_data),
Christoph Hellwig95dd91f2005-06-19 01:27:56 +0200691 fid, 0);
692
693 hermes_write_regn(hw, TXCOMPLFID, DUMMY_FID);
694 stats->tx_errors++;
695
Linus Torvalds1da177e2005-04-16 15:20:36 -0700696 if (err) {
697 printk(KERN_WARNING "%s: Unable to read descriptor on Tx error "
698 "(FID=%04X error %d)\n",
699 dev->name, fid, err);
Christoph Hellwig95dd91f2005-06-19 01:27:56 +0200700 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700701 }
David Kilroy6fe9deb2009-02-04 23:05:43 +0000702
Christoph Hellwig95dd91f2005-06-19 01:27:56 +0200703 DEBUG(1, "%s: Tx error, err %d (FID=%04X)\n", dev->name,
704 err, fid);
David Kilroy6fe9deb2009-02-04 23:05:43 +0000705
Christoph Hellwig95dd91f2005-06-19 01:27:56 +0200706 /* We produce a TXDROP event only for retry or lifetime
707 * exceeded, because that's the only status that really mean
708 * that this particular node went away.
709 * Other errors means that *we* screwed up. - Jean II */
Pavel Roskin30c2d3b2006-04-07 04:10:34 -0400710 status = le16_to_cpu(hdr.desc.status);
Pavel Roskind133ae42005-09-23 04:18:06 -0400711 if (status & (HERMES_TXSTAT_RETRYERR | HERMES_TXSTAT_AGEDERR)) {
Christoph Hellwig95dd91f2005-06-19 01:27:56 +0200712 union iwreq_data wrqu;
713
714 /* Copy 802.11 dest address.
715 * We use the 802.11 header because the frame may
716 * not be 802.3 or may be mangled...
717 * In Ad-Hoc mode, it will be the node address.
718 * In managed mode, it will be most likely the AP addr
719 * User space will figure out how to convert it to
720 * whatever it needs (IP address or else).
721 * - Jean II */
722 memcpy(wrqu.addr.sa_data, hdr.addr1, ETH_ALEN);
723 wrqu.addr.sa_family = ARPHRD_ETHER;
724
725 /* Send event to user space */
726 wireless_send_event(dev, IWEVTXDROP, &wrqu, NULL);
727 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700728
729 netif_wake_queue(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700730}
731
732static void orinoco_tx_timeout(struct net_device *dev)
733{
734 struct orinoco_private *priv = netdev_priv(dev);
735 struct net_device_stats *stats = &priv->stats;
736 struct hermes *hw = &priv->hw;
737
738 printk(KERN_WARNING "%s: Tx timeout! "
739 "ALLOCFID=%04x, TXCOMPLFID=%04x, EVSTAT=%04x\n",
740 dev->name, hermes_read_regn(hw, ALLOCFID),
741 hermes_read_regn(hw, TXCOMPLFID), hermes_read_regn(hw, EVSTAT));
742
743 stats->tx_errors++;
744
745 schedule_work(&priv->reset_work);
746}
747
748/********************************************************************/
749/* Rx path (data frames) */
750/********************************************************************/
751
752/* Does the frame have a SNAP header indicating it should be
753 * de-encapsulated to Ethernet-II? */
754static inline int is_ethersnap(void *_hdr)
755{
756 u8 *hdr = _hdr;
757
758 /* We de-encapsulate all packets which, a) have SNAP headers
759 * (i.e. SSAP=DSAP=0xaa and CTRL=0x3 in the 802.2 LLC header
760 * and where b) the OUI of the SNAP header is 00:00:00 or
761 * 00:00:f8 - we need both because different APs appear to use
762 * different OUIs for some reason */
763 return (memcmp(hdr, &encaps_hdr, 5) == 0)
David Kilroya94e8422009-02-04 23:05:44 +0000764 && ((hdr[5] == 0x00) || (hdr[5] == 0xf8));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700765}
766
767static inline void orinoco_spy_gather(struct net_device *dev, u_char *mac,
768 int level, int noise)
769{
Pavel Roskin343c6862005-09-09 18:43:02 -0400770 struct iw_quality wstats;
771 wstats.level = level - 0x95;
772 wstats.noise = noise - 0x95;
773 wstats.qual = (level > noise) ? (level - noise) : 0;
Andrey Borzenkovf941f852008-11-15 17:15:09 +0300774 wstats.updated = IW_QUAL_ALL_UPDATED | IW_QUAL_DBM;
Pavel Roskin343c6862005-09-09 18:43:02 -0400775 /* Update spy records */
776 wireless_spy_update(dev, mac, &wstats);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700777}
778
779static void orinoco_stat_gather(struct net_device *dev,
780 struct sk_buff *skb,
781 struct hermes_rx_descriptor *desc)
782{
783 struct orinoco_private *priv = netdev_priv(dev);
784
785 /* Using spy support with lots of Rx packets, like in an
786 * infrastructure (AP), will really slow down everything, because
787 * the MAC address must be compared to each entry of the spy list.
788 * If the user really asks for it (set some address in the
789 * spy list), we do it, but he will pay the price.
790 * Note that to get here, you need both WIRELESS_SPY
791 * compiled in AND some addresses in the list !!!
792 */
793 /* Note : gcc will optimise the whole section away if
794 * WIRELESS_SPY is not defined... - Jean II */
795 if (SPY_NUMBER(priv)) {
Arnaldo Carvalho de Melo98e399f2007-03-19 15:33:04 -0700796 orinoco_spy_gather(dev, skb_mac_header(skb) + ETH_ALEN,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700797 desc->signal, desc->silence);
798 }
799}
800
Christoph Hellwig98c4cae2005-06-19 01:28:06 +0200801/*
802 * orinoco_rx_monitor - handle received monitor frames.
803 *
804 * Arguments:
805 * dev network device
806 * rxfid received FID
807 * desc rx descriptor of the frame
808 *
809 * Call context: interrupt
810 */
811static void orinoco_rx_monitor(struct net_device *dev, u16 rxfid,
812 struct hermes_rx_descriptor *desc)
813{
814 u32 hdrlen = 30; /* return full header by default */
815 u32 datalen = 0;
816 u16 fc;
817 int err;
818 int len;
819 struct sk_buff *skb;
820 struct orinoco_private *priv = netdev_priv(dev);
821 struct net_device_stats *stats = &priv->stats;
822 hermes_t *hw = &priv->hw;
823
824 len = le16_to_cpu(desc->data_len);
825
826 /* Determine the size of the header and the data */
827 fc = le16_to_cpu(desc->frame_ctl);
828 switch (fc & IEEE80211_FCTL_FTYPE) {
829 case IEEE80211_FTYPE_DATA:
830 if ((fc & IEEE80211_FCTL_TODS)
831 && (fc & IEEE80211_FCTL_FROMDS))
832 hdrlen = 30;
833 else
834 hdrlen = 24;
835 datalen = len;
836 break;
837 case IEEE80211_FTYPE_MGMT:
838 hdrlen = 24;
839 datalen = len;
840 break;
841 case IEEE80211_FTYPE_CTL:
842 switch (fc & IEEE80211_FCTL_STYPE) {
843 case IEEE80211_STYPE_PSPOLL:
844 case IEEE80211_STYPE_RTS:
845 case IEEE80211_STYPE_CFEND:
846 case IEEE80211_STYPE_CFENDACK:
847 hdrlen = 16;
848 break;
849 case IEEE80211_STYPE_CTS:
850 case IEEE80211_STYPE_ACK:
851 hdrlen = 10;
852 break;
853 }
854 break;
855 default:
856 /* Unknown frame type */
857 break;
858 }
859
860 /* sanity check the length */
Johannes Berg2c7060022008-10-30 22:09:54 +0100861 if (datalen > IEEE80211_MAX_DATA_LEN + 12) {
Christoph Hellwig98c4cae2005-06-19 01:28:06 +0200862 printk(KERN_DEBUG "%s: oversized monitor frame, "
863 "data length = %d\n", dev->name, datalen);
Christoph Hellwig98c4cae2005-06-19 01:28:06 +0200864 stats->rx_length_errors++;
865 goto update_stats;
866 }
867
868 skb = dev_alloc_skb(hdrlen + datalen);
869 if (!skb) {
870 printk(KERN_WARNING "%s: Cannot allocate skb for monitor frame\n",
871 dev->name);
Florin Malitabb6e0932006-05-22 22:35:30 -0700872 goto update_stats;
Christoph Hellwig98c4cae2005-06-19 01:28:06 +0200873 }
874
875 /* Copy the 802.11 header to the skb */
876 memcpy(skb_put(skb, hdrlen), &(desc->frame_ctl), hdrlen);
Arnaldo Carvalho de Melo459a98e2007-03-19 15:30:44 -0700877 skb_reset_mac_header(skb);
Christoph Hellwig98c4cae2005-06-19 01:28:06 +0200878
879 /* If any, copy the data from the card to the skb */
880 if (datalen > 0) {
881 err = hermes_bap_pread(hw, IRQ_BAP, skb_put(skb, datalen),
882 ALIGN(datalen, 2), rxfid,
883 HERMES_802_2_OFFSET);
884 if (err) {
885 printk(KERN_ERR "%s: error %d reading monitor frame\n",
886 dev->name, err);
887 goto drop;
888 }
889 }
890
891 skb->dev = dev;
892 skb->ip_summed = CHECKSUM_NONE;
893 skb->pkt_type = PACKET_OTHERHOST;
Harvey Harrisonc1b4aa32009-01-29 13:26:44 -0800894 skb->protocol = cpu_to_be16(ETH_P_802_2);
David Kilroy6fe9deb2009-02-04 23:05:43 +0000895
Christoph Hellwig98c4cae2005-06-19 01:28:06 +0200896 stats->rx_packets++;
897 stats->rx_bytes += skb->len;
898
899 netif_rx(skb);
900 return;
901
902 drop:
903 dev_kfree_skb_irq(skb);
904 update_stats:
905 stats->rx_errors++;
906 stats->rx_dropped++;
907}
908
David Kilroy23edcc42008-08-21 23:28:05 +0100909/* Get tsc from the firmware */
910static int orinoco_hw_get_tkip_iv(struct orinoco_private *priv, int key,
911 u8 *tsc)
912{
913 hermes_t *hw = &priv->hw;
914 int err = 0;
915 u8 tsc_arr[4][IW_ENCODE_SEQ_MAX_SIZE];
916
917 if ((key < 0) || (key > 4))
918 return -EINVAL;
919
920 err = hermes_read_ltv(hw, USER_BAP, HERMES_RID_CURRENT_TKIP_IV,
921 sizeof(tsc_arr), NULL, &tsc_arr);
922 if (!err)
923 memcpy(tsc, &tsc_arr[key][0], sizeof(tsc_arr[0]));
924
925 return err;
926}
927
Linus Torvalds1da177e2005-04-16 15:20:36 -0700928static void __orinoco_ev_rx(struct net_device *dev, hermes_t *hw)
929{
930 struct orinoco_private *priv = netdev_priv(dev);
931 struct net_device_stats *stats = &priv->stats;
932 struct iw_statistics *wstats = &priv->wstats;
933 struct sk_buff *skb = NULL;
David Kilroy31afcef2008-08-21 23:28:04 +0100934 u16 rxfid, status;
Christoph Hellwig8f2abf42005-06-19 01:28:02 +0200935 int length;
David Kilroy31afcef2008-08-21 23:28:04 +0100936 struct hermes_rx_descriptor *desc;
937 struct orinoco_rx_data *rx_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700938 int err;
939
David Kilroy31afcef2008-08-21 23:28:04 +0100940 desc = kmalloc(sizeof(*desc), GFP_ATOMIC);
941 if (!desc) {
942 printk(KERN_WARNING
943 "%s: Can't allocate space for RX descriptor\n",
944 dev->name);
945 goto update_stats;
946 }
947
Linus Torvalds1da177e2005-04-16 15:20:36 -0700948 rxfid = hermes_read_regn(hw, RXFID);
949
David Kilroy31afcef2008-08-21 23:28:04 +0100950 err = hermes_bap_pread(hw, IRQ_BAP, desc, sizeof(*desc),
Linus Torvalds1da177e2005-04-16 15:20:36 -0700951 rxfid, 0);
952 if (err) {
953 printk(KERN_ERR "%s: error %d reading Rx descriptor. "
954 "Frame dropped.\n", dev->name, err);
Christoph Hellwig98c4cae2005-06-19 01:28:06 +0200955 goto update_stats;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700956 }
957
David Kilroy31afcef2008-08-21 23:28:04 +0100958 status = le16_to_cpu(desc->status);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700959
Christoph Hellwig98c4cae2005-06-19 01:28:06 +0200960 if (status & HERMES_RXSTAT_BADCRC) {
961 DEBUG(1, "%s: Bad CRC on Rx. Frame dropped.\n",
962 dev->name);
963 stats->rx_crc_errors++;
964 goto update_stats;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700965 }
966
Christoph Hellwig98c4cae2005-06-19 01:28:06 +0200967 /* Handle frames in monitor mode */
968 if (priv->iw_mode == IW_MODE_MONITOR) {
David Kilroy31afcef2008-08-21 23:28:04 +0100969 orinoco_rx_monitor(dev, rxfid, desc);
970 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700971 }
972
Christoph Hellwig98c4cae2005-06-19 01:28:06 +0200973 if (status & HERMES_RXSTAT_UNDECRYPTABLE) {
974 DEBUG(1, "%s: Undecryptable frame on Rx. Frame dropped.\n",
975 dev->name);
976 wstats->discard.code++;
977 goto update_stats;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700978 }
979
David Kilroy31afcef2008-08-21 23:28:04 +0100980 length = le16_to_cpu(desc->data_len);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700981
Linus Torvalds1da177e2005-04-16 15:20:36 -0700982 /* Sanity checks */
983 if (length < 3) { /* No for even an 802.2 LLC header */
984 /* At least on Symbol firmware with PCF we get quite a
David Kilroy6fe9deb2009-02-04 23:05:43 +0000985 lot of these legitimately - Poll frames with no
986 data. */
David Kilroy31afcef2008-08-21 23:28:04 +0100987 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700988 }
Johannes Berg2c7060022008-10-30 22:09:54 +0100989 if (length > IEEE80211_MAX_DATA_LEN) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700990 printk(KERN_WARNING "%s: Oversized frame received (%d bytes)\n",
991 dev->name, length);
992 stats->rx_length_errors++;
Christoph Hellwig98c4cae2005-06-19 01:28:06 +0200993 goto update_stats;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700994 }
995
David Kilroy23edcc42008-08-21 23:28:05 +0100996 /* Payload size does not include Michael MIC. Increase payload
997 * size to read it together with the data. */
998 if (status & HERMES_RXSTAT_MIC)
999 length += MICHAEL_MIC_LEN;
1000
Linus Torvalds1da177e2005-04-16 15:20:36 -07001001 /* We need space for the packet data itself, plus an ethernet
1002 header, plus 2 bytes so we can align the IP header on a
1003 32bit boundary, plus 1 byte so we can read in odd length
1004 packets from the card, which has an IO granularity of 16
David Kilroy6fe9deb2009-02-04 23:05:43 +00001005 bits */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001006 skb = dev_alloc_skb(length+ETH_HLEN+2+1);
1007 if (!skb) {
1008 printk(KERN_WARNING "%s: Can't allocate skb for Rx\n",
1009 dev->name);
Christoph Hellwig98c4cae2005-06-19 01:28:06 +02001010 goto update_stats;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001011 }
1012
Christoph Hellwig8f2abf42005-06-19 01:28:02 +02001013 /* We'll prepend the header, so reserve space for it. The worst
1014 case is no decapsulation, when 802.3 header is prepended and
1015 nothing is removed. 2 is for aligning the IP header. */
1016 skb_reserve(skb, ETH_HLEN + 2);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001017
Christoph Hellwig8f2abf42005-06-19 01:28:02 +02001018 err = hermes_bap_pread(hw, IRQ_BAP, skb_put(skb, length),
1019 ALIGN(length, 2), rxfid,
1020 HERMES_802_2_OFFSET);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001021 if (err) {
1022 printk(KERN_ERR "%s: error %d reading frame. "
1023 "Frame dropped.\n", dev->name, err);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001024 goto drop;
1025 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001026
David Kilroy31afcef2008-08-21 23:28:04 +01001027 /* Add desc and skb to rx queue */
1028 rx_data = kzalloc(sizeof(*rx_data), GFP_ATOMIC);
1029 if (!rx_data) {
1030 printk(KERN_WARNING "%s: Can't allocate RX packet\n",
1031 dev->name);
1032 goto drop;
1033 }
1034 rx_data->desc = desc;
1035 rx_data->skb = skb;
1036 list_add_tail(&rx_data->list, &priv->rx_list);
1037 tasklet_schedule(&priv->rx_tasklet);
1038
1039 return;
1040
1041drop:
1042 dev_kfree_skb_irq(skb);
1043update_stats:
1044 stats->rx_errors++;
1045 stats->rx_dropped++;
1046out:
1047 kfree(desc);
1048}
1049
1050static void orinoco_rx(struct net_device *dev,
1051 struct hermes_rx_descriptor *desc,
1052 struct sk_buff *skb)
1053{
1054 struct orinoco_private *priv = netdev_priv(dev);
1055 struct net_device_stats *stats = &priv->stats;
1056 u16 status, fc;
1057 int length;
1058 struct ethhdr *hdr;
1059
1060 status = le16_to_cpu(desc->status);
1061 length = le16_to_cpu(desc->data_len);
1062 fc = le16_to_cpu(desc->frame_ctl);
1063
David Kilroy23edcc42008-08-21 23:28:05 +01001064 /* Calculate and check MIC */
1065 if (status & HERMES_RXSTAT_MIC) {
1066 int key_id = ((status & HERMES_RXSTAT_MIC_KEY_ID) >>
1067 HERMES_MIC_KEY_ID_SHIFT);
1068 u8 mic[MICHAEL_MIC_LEN];
1069 u8 *rxmic;
1070 u8 *src = (fc & IEEE80211_FCTL_FROMDS) ?
1071 desc->addr3 : desc->addr2;
1072
1073 /* Extract Michael MIC from payload */
1074 rxmic = skb->data + skb->len - MICHAEL_MIC_LEN;
1075
1076 skb_trim(skb, skb->len - MICHAEL_MIC_LEN);
1077 length -= MICHAEL_MIC_LEN;
1078
David Kilroy4adb4742009-02-04 23:05:51 +00001079 orinoco_mic(priv->rx_tfm_mic,
David Kilroy23edcc42008-08-21 23:28:05 +01001080 priv->tkip_key[key_id].rx_mic,
1081 desc->addr1,
1082 src,
1083 0, /* priority or QoS? */
1084 skb->data,
1085 skb->len,
1086 &mic[0]);
1087
1088 if (memcmp(mic, rxmic,
1089 MICHAEL_MIC_LEN)) {
1090 union iwreq_data wrqu;
1091 struct iw_michaelmicfailure wxmic;
David Kilroy23edcc42008-08-21 23:28:05 +01001092
1093 printk(KERN_WARNING "%s: "
Johannes Berge1749612008-10-27 15:59:26 -07001094 "Invalid Michael MIC in data frame from %pM, "
David Kilroy23edcc42008-08-21 23:28:05 +01001095 "using key %i\n",
Johannes Berge1749612008-10-27 15:59:26 -07001096 dev->name, src, key_id);
David Kilroy23edcc42008-08-21 23:28:05 +01001097
1098 /* TODO: update stats */
1099
1100 /* Notify userspace */
1101 memset(&wxmic, 0, sizeof(wxmic));
1102 wxmic.flags = key_id & IW_MICFAILURE_KEY_ID;
1103 wxmic.flags |= (desc->addr1[0] & 1) ?
1104 IW_MICFAILURE_GROUP : IW_MICFAILURE_PAIRWISE;
1105 wxmic.src_addr.sa_family = ARPHRD_ETHER;
1106 memcpy(wxmic.src_addr.sa_data, src, ETH_ALEN);
1107
1108 (void) orinoco_hw_get_tkip_iv(priv, key_id,
1109 &wxmic.tsc[0]);
1110
1111 memset(&wrqu, 0, sizeof(wrqu));
1112 wrqu.data.length = sizeof(wxmic);
1113 wireless_send_event(dev, IWEVMICHAELMICFAILURE, &wrqu,
1114 (char *) &wxmic);
1115
1116 goto drop;
1117 }
1118 }
1119
Linus Torvalds1da177e2005-04-16 15:20:36 -07001120 /* Handle decapsulation
1121 * In most cases, the firmware tell us about SNAP frames.
1122 * For some reason, the SNAP frames sent by LinkSys APs
1123 * are not properly recognised by most firmwares.
1124 * So, check ourselves */
Christoph Hellwig8f2abf42005-06-19 01:28:02 +02001125 if (length >= ENCAPS_OVERHEAD &&
1126 (((status & HERMES_RXSTAT_MSGTYPE) == HERMES_RXSTAT_1042) ||
1127 ((status & HERMES_RXSTAT_MSGTYPE) == HERMES_RXSTAT_TUNNEL) ||
1128 is_ethersnap(skb->data))) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001129 /* These indicate a SNAP within 802.2 LLC within
1130 802.11 frame which we'll need to de-encapsulate to
1131 the original EthernetII frame. */
David Kilroyb2f30a02009-02-04 23:05:46 +00001132 hdr = (struct ethhdr *)skb_push(skb,
1133 ETH_HLEN - ENCAPS_OVERHEAD);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001134 } else {
Christoph Hellwig8f2abf42005-06-19 01:28:02 +02001135 /* 802.3 frame - prepend 802.3 header as is */
1136 hdr = (struct ethhdr *)skb_push(skb, ETH_HLEN);
1137 hdr->h_proto = htons(length);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001138 }
David Kilroy31afcef2008-08-21 23:28:04 +01001139 memcpy(hdr->h_dest, desc->addr1, ETH_ALEN);
Christoph Hellwig8f2abf42005-06-19 01:28:02 +02001140 if (fc & IEEE80211_FCTL_FROMDS)
David Kilroy31afcef2008-08-21 23:28:04 +01001141 memcpy(hdr->h_source, desc->addr3, ETH_ALEN);
Christoph Hellwig8f2abf42005-06-19 01:28:02 +02001142 else
David Kilroy31afcef2008-08-21 23:28:04 +01001143 memcpy(hdr->h_source, desc->addr2, ETH_ALEN);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001144
Linus Torvalds1da177e2005-04-16 15:20:36 -07001145 skb->protocol = eth_type_trans(skb, dev);
1146 skb->ip_summed = CHECKSUM_NONE;
Christoph Hellwig8f2abf42005-06-19 01:28:02 +02001147 if (fc & IEEE80211_FCTL_TODS)
1148 skb->pkt_type = PACKET_OTHERHOST;
David Kilroy6fe9deb2009-02-04 23:05:43 +00001149
Linus Torvalds1da177e2005-04-16 15:20:36 -07001150 /* Process the wireless stats if needed */
David Kilroy31afcef2008-08-21 23:28:04 +01001151 orinoco_stat_gather(dev, skb, desc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001152
1153 /* Pass the packet to the networking stack */
1154 netif_rx(skb);
1155 stats->rx_packets++;
1156 stats->rx_bytes += length;
1157
1158 return;
David Kilroy23edcc42008-08-21 23:28:05 +01001159
1160 drop:
1161 dev_kfree_skb(skb);
1162 stats->rx_errors++;
1163 stats->rx_dropped++;
David Kilroy31afcef2008-08-21 23:28:04 +01001164}
Linus Torvalds1da177e2005-04-16 15:20:36 -07001165
David Kilroy31afcef2008-08-21 23:28:04 +01001166static void orinoco_rx_isr_tasklet(unsigned long data)
1167{
1168 struct net_device *dev = (struct net_device *) data;
1169 struct orinoco_private *priv = netdev_priv(dev);
1170 struct orinoco_rx_data *rx_data, *temp;
1171 struct hermes_rx_descriptor *desc;
1172 struct sk_buff *skb;
David Kilroy20953ad2009-01-07 00:23:55 +00001173 unsigned long flags;
1174
1175 /* orinoco_rx requires the driver lock, and we also need to
1176 * protect priv->rx_list, so just hold the lock over the
1177 * lot.
1178 *
1179 * If orinoco_lock fails, we've unplugged the card. In this
1180 * case just abort. */
1181 if (orinoco_lock(priv, &flags) != 0)
1182 return;
David Kilroy31afcef2008-08-21 23:28:04 +01001183
1184 /* extract desc and skb from queue */
1185 list_for_each_entry_safe(rx_data, temp, &priv->rx_list, list) {
1186 desc = rx_data->desc;
1187 skb = rx_data->skb;
1188 list_del(&rx_data->list);
1189 kfree(rx_data);
1190
1191 orinoco_rx(dev, desc, skb);
1192
1193 kfree(desc);
1194 }
David Kilroy20953ad2009-01-07 00:23:55 +00001195
1196 orinoco_unlock(priv, &flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001197}
1198
1199/********************************************************************/
1200/* Rx path (info frames) */
1201/********************************************************************/
1202
1203static void print_linkstatus(struct net_device *dev, u16 status)
1204{
David Kilroy21312662009-02-04 23:05:47 +00001205 char *s;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001206
1207 if (suppress_linkstatus)
1208 return;
1209
1210 switch (status) {
1211 case HERMES_LINKSTATUS_NOT_CONNECTED:
1212 s = "Not Connected";
1213 break;
1214 case HERMES_LINKSTATUS_CONNECTED:
1215 s = "Connected";
1216 break;
1217 case HERMES_LINKSTATUS_DISCONNECTED:
1218 s = "Disconnected";
1219 break;
1220 case HERMES_LINKSTATUS_AP_CHANGE:
1221 s = "AP Changed";
1222 break;
1223 case HERMES_LINKSTATUS_AP_OUT_OF_RANGE:
1224 s = "AP Out of Range";
1225 break;
1226 case HERMES_LINKSTATUS_AP_IN_RANGE:
1227 s = "AP In Range";
1228 break;
1229 case HERMES_LINKSTATUS_ASSOC_FAILED:
1230 s = "Association Failed";
1231 break;
1232 default:
1233 s = "UNKNOWN";
1234 }
David Kilroy6fe9deb2009-02-04 23:05:43 +00001235
Pavel Roskin11eaea42009-01-18 23:20:58 -05001236 printk(KERN_DEBUG "%s: New link status: %s (%04x)\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -07001237 dev->name, s, status);
1238}
1239
Christoph Hellwig16739b02005-06-19 01:27:51 +02001240/* Search scan results for requested BSSID, join it if found */
David Howellsc4028952006-11-22 14:57:56 +00001241static void orinoco_join_ap(struct work_struct *work)
Christoph Hellwig16739b02005-06-19 01:27:51 +02001242{
David Howellsc4028952006-11-22 14:57:56 +00001243 struct orinoco_private *priv =
1244 container_of(work, struct orinoco_private, join_work);
1245 struct net_device *dev = priv->ndev;
Christoph Hellwig16739b02005-06-19 01:27:51 +02001246 struct hermes *hw = &priv->hw;
1247 int err;
1248 unsigned long flags;
1249 struct join_req {
1250 u8 bssid[ETH_ALEN];
Pavel Roskind133ae42005-09-23 04:18:06 -04001251 __le16 channel;
Christoph Hellwig16739b02005-06-19 01:27:51 +02001252 } __attribute__ ((packed)) req;
1253 const int atom_len = offsetof(struct prism2_scan_apinfo, atim);
Pavel Roskinc89cc222005-09-01 20:06:06 -04001254 struct prism2_scan_apinfo *atom = NULL;
Christoph Hellwig16739b02005-06-19 01:27:51 +02001255 int offset = 4;
Pavel Roskinc89cc222005-09-01 20:06:06 -04001256 int found = 0;
Christoph Hellwig16739b02005-06-19 01:27:51 +02001257 u8 *buf;
1258 u16 len;
1259
1260 /* Allocate buffer for scan results */
1261 buf = kmalloc(MAX_SCAN_LEN, GFP_KERNEL);
David Kilroya94e8422009-02-04 23:05:44 +00001262 if (!buf)
Christoph Hellwig16739b02005-06-19 01:27:51 +02001263 return;
1264
1265 if (orinoco_lock(priv, &flags) != 0)
Pavel Roskinf3cb4cc2005-09-23 04:18:06 -04001266 goto fail_lock;
Christoph Hellwig16739b02005-06-19 01:27:51 +02001267
1268 /* Sanity checks in case user changed something in the meantime */
David Kilroya94e8422009-02-04 23:05:44 +00001269 if (!priv->bssid_fixed)
Christoph Hellwig16739b02005-06-19 01:27:51 +02001270 goto out;
1271
1272 if (strlen(priv->desired_essid) == 0)
1273 goto out;
1274
1275 /* Read scan results from the firmware */
1276 err = hermes_read_ltv(hw, USER_BAP,
1277 HERMES_RID_SCANRESULTSTABLE,
1278 MAX_SCAN_LEN, &len, buf);
1279 if (err) {
1280 printk(KERN_ERR "%s: Cannot read scan results\n",
1281 dev->name);
1282 goto out;
1283 }
1284
1285 len = HERMES_RECLEN_TO_BYTES(len);
1286
1287 /* Go through the scan results looking for the channel of the AP
1288 * we were requested to join */
1289 for (; offset + atom_len <= len; offset += atom_len) {
1290 atom = (struct prism2_scan_apinfo *) (buf + offset);
Pavel Roskinc89cc222005-09-01 20:06:06 -04001291 if (memcmp(&atom->bssid, priv->desired_bssid, ETH_ALEN) == 0) {
1292 found = 1;
1293 break;
1294 }
Christoph Hellwig16739b02005-06-19 01:27:51 +02001295 }
1296
David Kilroya94e8422009-02-04 23:05:44 +00001297 if (!found) {
Pavel Roskinc89cc222005-09-01 20:06:06 -04001298 DEBUG(1, "%s: Requested AP not found in scan results\n",
1299 dev->name);
1300 goto out;
1301 }
Christoph Hellwig16739b02005-06-19 01:27:51 +02001302
Christoph Hellwig16739b02005-06-19 01:27:51 +02001303 memcpy(req.bssid, priv->desired_bssid, ETH_ALEN);
1304 req.channel = atom->channel; /* both are little-endian */
1305 err = HERMES_WRITE_RECORD(hw, USER_BAP, HERMES_RID_CNFJOINREQUEST,
1306 &req);
1307 if (err)
1308 printk(KERN_ERR "%s: Error issuing join request\n", dev->name);
1309
1310 out:
Christoph Hellwig16739b02005-06-19 01:27:51 +02001311 orinoco_unlock(priv, &flags);
Pavel Roskinf3cb4cc2005-09-23 04:18:06 -04001312
1313 fail_lock:
1314 kfree(buf);
Christoph Hellwig16739b02005-06-19 01:27:51 +02001315}
1316
Christoph Hellwig95dd91f2005-06-19 01:27:56 +02001317/* Send new BSSID to userspace */
David Kilroy6cd90b12008-08-21 23:28:00 +01001318static void orinoco_send_bssid_wevent(struct orinoco_private *priv)
Christoph Hellwig95dd91f2005-06-19 01:27:56 +02001319{
David Howellsc4028952006-11-22 14:57:56 +00001320 struct net_device *dev = priv->ndev;
Christoph Hellwig95dd91f2005-06-19 01:27:56 +02001321 struct hermes *hw = &priv->hw;
1322 union iwreq_data wrqu;
1323 int err;
Christoph Hellwig95dd91f2005-06-19 01:27:56 +02001324
David Kilroy499b7022008-12-09 21:46:29 +00001325 err = hermes_read_ltv(hw, USER_BAP, HERMES_RID_CURRENTBSSID,
Christoph Hellwig95dd91f2005-06-19 01:27:56 +02001326 ETH_ALEN, NULL, wrqu.ap_addr.sa_data);
1327 if (err != 0)
David Kilroy6cd90b12008-08-21 23:28:00 +01001328 return;
Christoph Hellwig95dd91f2005-06-19 01:27:56 +02001329
1330 wrqu.ap_addr.sa_family = ARPHRD_ETHER;
1331
1332 /* Send event to user space */
1333 wireless_send_event(dev, SIOCGIWAP, &wrqu, NULL);
Christoph Hellwig95dd91f2005-06-19 01:27:56 +02001334}
1335
David Kilroy06009fd2008-08-21 23:28:03 +01001336static void orinoco_send_assocreqie_wevent(struct orinoco_private *priv)
1337{
1338 struct net_device *dev = priv->ndev;
1339 struct hermes *hw = &priv->hw;
1340 union iwreq_data wrqu;
1341 int err;
1342 u8 buf[88];
1343 u8 *ie;
1344
1345 if (!priv->has_wpa)
1346 return;
1347
David Kilroy499b7022008-12-09 21:46:29 +00001348 err = hermes_read_ltv(hw, USER_BAP, HERMES_RID_CURRENT_ASSOC_REQ_INFO,
David Kilroy06009fd2008-08-21 23:28:03 +01001349 sizeof(buf), NULL, &buf);
1350 if (err != 0)
1351 return;
1352
1353 ie = orinoco_get_wpa_ie(buf, sizeof(buf));
1354 if (ie) {
1355 int rem = sizeof(buf) - (ie - &buf[0]);
1356 wrqu.data.length = ie[1] + 2;
1357 if (wrqu.data.length > rem)
1358 wrqu.data.length = rem;
1359
1360 if (wrqu.data.length)
1361 /* Send event to user space */
1362 wireless_send_event(dev, IWEVASSOCREQIE, &wrqu, ie);
1363 }
1364}
1365
1366static void orinoco_send_assocrespie_wevent(struct orinoco_private *priv)
1367{
1368 struct net_device *dev = priv->ndev;
1369 struct hermes *hw = &priv->hw;
1370 union iwreq_data wrqu;
1371 int err;
1372 u8 buf[88]; /* TODO: verify max size or IW_GENERIC_IE_MAX */
1373 u8 *ie;
1374
1375 if (!priv->has_wpa)
1376 return;
1377
David Kilroy499b7022008-12-09 21:46:29 +00001378 err = hermes_read_ltv(hw, USER_BAP, HERMES_RID_CURRENT_ASSOC_RESP_INFO,
David Kilroy06009fd2008-08-21 23:28:03 +01001379 sizeof(buf), NULL, &buf);
1380 if (err != 0)
1381 return;
1382
1383 ie = orinoco_get_wpa_ie(buf, sizeof(buf));
1384 if (ie) {
1385 int rem = sizeof(buf) - (ie - &buf[0]);
1386 wrqu.data.length = ie[1] + 2;
1387 if (wrqu.data.length > rem)
1388 wrqu.data.length = rem;
1389
1390 if (wrqu.data.length)
1391 /* Send event to user space */
1392 wireless_send_event(dev, IWEVASSOCRESPIE, &wrqu, ie);
1393 }
1394}
1395
David Kilroy6cd90b12008-08-21 23:28:00 +01001396static void orinoco_send_wevents(struct work_struct *work)
1397{
1398 struct orinoco_private *priv =
1399 container_of(work, struct orinoco_private, wevent_work);
1400 unsigned long flags;
1401
1402 if (orinoco_lock(priv, &flags) != 0)
1403 return;
1404
David Kilroy06009fd2008-08-21 23:28:03 +01001405 orinoco_send_assocreqie_wevent(priv);
1406 orinoco_send_assocrespie_wevent(priv);
David Kilroy6cd90b12008-08-21 23:28:00 +01001407 orinoco_send_bssid_wevent(priv);
1408
1409 orinoco_unlock(priv, &flags);
1410}
Dan Williams1e3428e2007-10-10 23:56:25 -04001411
Linus Torvalds1da177e2005-04-16 15:20:36 -07001412static void __orinoco_ev_info(struct net_device *dev, hermes_t *hw)
1413{
1414 struct orinoco_private *priv = netdev_priv(dev);
1415 u16 infofid;
1416 struct {
Pavel Roskind133ae42005-09-23 04:18:06 -04001417 __le16 len;
1418 __le16 type;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001419 } __attribute__ ((packed)) info;
1420 int len, type;
1421 int err;
1422
1423 /* This is an answer to an INQUIRE command that we did earlier,
1424 * or an information "event" generated by the card
1425 * The controller return to us a pseudo frame containing
1426 * the information in question - Jean II */
1427 infofid = hermes_read_regn(hw, INFOFID);
1428
1429 /* Read the info frame header - don't try too hard */
1430 err = hermes_bap_pread(hw, IRQ_BAP, &info, sizeof(info),
1431 infofid, 0);
1432 if (err) {
1433 printk(KERN_ERR "%s: error %d reading info frame. "
1434 "Frame dropped.\n", dev->name, err);
1435 return;
1436 }
David Kilroy6fe9deb2009-02-04 23:05:43 +00001437
Linus Torvalds1da177e2005-04-16 15:20:36 -07001438 len = HERMES_RECLEN_TO_BYTES(le16_to_cpu(info.len));
1439 type = le16_to_cpu(info.type);
1440
1441 switch (type) {
1442 case HERMES_INQ_TALLIES: {
1443 struct hermes_tallies_frame tallies;
1444 struct iw_statistics *wstats = &priv->wstats;
David Kilroy6fe9deb2009-02-04 23:05:43 +00001445
Linus Torvalds1da177e2005-04-16 15:20:36 -07001446 if (len > sizeof(tallies)) {
1447 printk(KERN_WARNING "%s: Tallies frame too long (%d bytes)\n",
1448 dev->name, len);
1449 len = sizeof(tallies);
1450 }
David Kilroy6fe9deb2009-02-04 23:05:43 +00001451
Christoph Hellwig84d8a2f2005-05-14 17:30:22 +02001452 err = hermes_bap_pread(hw, IRQ_BAP, &tallies, len,
1453 infofid, sizeof(info));
1454 if (err)
1455 break;
David Kilroy6fe9deb2009-02-04 23:05:43 +00001456
Linus Torvalds1da177e2005-04-16 15:20:36 -07001457 /* Increment our various counters */
1458 /* wstats->discard.nwid - no wrong BSSID stuff */
1459 wstats->discard.code +=
1460 le16_to_cpu(tallies.RxWEPUndecryptable);
David Kilroy6fe9deb2009-02-04 23:05:43 +00001461 if (len == sizeof(tallies))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001462 wstats->discard.code +=
1463 le16_to_cpu(tallies.RxDiscards_WEPICVError) +
1464 le16_to_cpu(tallies.RxDiscards_WEPExcluded);
1465 wstats->discard.misc +=
1466 le16_to_cpu(tallies.TxDiscardsWrongSA);
1467 wstats->discard.fragment +=
1468 le16_to_cpu(tallies.RxMsgInBadMsgFragments);
1469 wstats->discard.retries +=
1470 le16_to_cpu(tallies.TxRetryLimitExceeded);
1471 /* wstats->miss.beacon - no match */
1472 }
1473 break;
1474 case HERMES_INQ_LINKSTATUS: {
1475 struct hermes_linkstatus linkstatus;
1476 u16 newstatus;
1477 int connected;
1478
Christoph Hellwig8f2abf42005-06-19 01:28:02 +02001479 if (priv->iw_mode == IW_MODE_MONITOR)
1480 break;
1481
Linus Torvalds1da177e2005-04-16 15:20:36 -07001482 if (len != sizeof(linkstatus)) {
1483 printk(KERN_WARNING "%s: Unexpected size for linkstatus frame (%d bytes)\n",
1484 dev->name, len);
1485 break;
1486 }
1487
Christoph Hellwig84d8a2f2005-05-14 17:30:22 +02001488 err = hermes_bap_pread(hw, IRQ_BAP, &linkstatus, len,
1489 infofid, sizeof(info));
1490 if (err)
1491 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001492 newstatus = le16_to_cpu(linkstatus.linkstatus);
1493
Christoph Hellwig95dd91f2005-06-19 01:27:56 +02001494 /* Symbol firmware uses "out of range" to signal that
1495 * the hostscan frame can be requested. */
1496 if (newstatus == HERMES_LINKSTATUS_AP_OUT_OF_RANGE &&
1497 priv->firmware_type == FIRMWARE_TYPE_SYMBOL &&
1498 priv->has_hostscan && priv->scan_inprogress) {
1499 hermes_inquire(hw, HERMES_INQ_HOSTSCAN_SYMBOL);
1500 break;
1501 }
1502
Linus Torvalds1da177e2005-04-16 15:20:36 -07001503 connected = (newstatus == HERMES_LINKSTATUS_CONNECTED)
1504 || (newstatus == HERMES_LINKSTATUS_AP_CHANGE)
1505 || (newstatus == HERMES_LINKSTATUS_AP_IN_RANGE);
1506
1507 if (connected)
1508 netif_carrier_on(dev);
David Gibson7bb7c3a2005-05-12 20:02:10 -04001509 else if (!ignore_disconnect)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001510 netif_carrier_off(dev);
1511
Christoph Hellwig95dd91f2005-06-19 01:27:56 +02001512 if (newstatus != priv->last_linkstatus) {
1513 priv->last_linkstatus = newstatus;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001514 print_linkstatus(dev, newstatus);
Christoph Hellwig95dd91f2005-06-19 01:27:56 +02001515 /* The info frame contains only one word which is the
1516 * status (see hermes.h). The status is pretty boring
1517 * in itself, that's why we export the new BSSID...
1518 * Jean II */
1519 schedule_work(&priv->wevent_work);
1520 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001521 }
1522 break;
Christoph Hellwig95dd91f2005-06-19 01:27:56 +02001523 case HERMES_INQ_SCAN:
1524 if (!priv->scan_inprogress && priv->bssid_fixed &&
1525 priv->firmware_type == FIRMWARE_TYPE_INTERSIL) {
1526 schedule_work(&priv->join_work);
1527 break;
1528 }
1529 /* fall through */
1530 case HERMES_INQ_HOSTSCAN:
1531 case HERMES_INQ_HOSTSCAN_SYMBOL: {
1532 /* Result of a scanning. Contains information about
1533 * cells in the vicinity - Jean II */
1534 union iwreq_data wrqu;
1535 unsigned char *buf;
1536
Dan Williams1e3428e2007-10-10 23:56:25 -04001537 /* Scan is no longer in progress */
1538 priv->scan_inprogress = 0;
1539
Christoph Hellwig95dd91f2005-06-19 01:27:56 +02001540 /* Sanity check */
1541 if (len > 4096) {
1542 printk(KERN_WARNING "%s: Scan results too large (%d bytes)\n",
1543 dev->name, len);
1544 break;
1545 }
1546
Christoph Hellwig95dd91f2005-06-19 01:27:56 +02001547 /* Allocate buffer for results */
1548 buf = kmalloc(len, GFP_ATOMIC);
1549 if (buf == NULL)
1550 /* No memory, so can't printk()... */
1551 break;
1552
1553 /* Read scan data */
1554 err = hermes_bap_pread(hw, IRQ_BAP, (void *) buf, len,
1555 infofid, sizeof(info));
Pavel Roskin708218b2005-09-01 20:05:19 -04001556 if (err) {
1557 kfree(buf);
Christoph Hellwig95dd91f2005-06-19 01:27:56 +02001558 break;
Pavel Roskin708218b2005-09-01 20:05:19 -04001559 }
Christoph Hellwig95dd91f2005-06-19 01:27:56 +02001560
1561#ifdef ORINOCO_DEBUG
1562 {
1563 int i;
1564 printk(KERN_DEBUG "Scan result [%02X", buf[0]);
David Kilroya94e8422009-02-04 23:05:44 +00001565 for (i = 1; i < (len * 2); i++)
Christoph Hellwig95dd91f2005-06-19 01:27:56 +02001566 printk(":%02X", buf[i]);
1567 printk("]\n");
1568 }
1569#endif /* ORINOCO_DEBUG */
1570
David Kilroyaea48b12009-02-04 23:05:49 +00001571 if (orinoco_process_scan_results(priv, buf, len) == 0) {
Dan Williams1e3428e2007-10-10 23:56:25 -04001572 /* Send an empty event to user space.
1573 * We don't send the received data on the event because
1574 * it would require us to do complex transcoding, and
1575 * we want to minimise the work done in the irq handler
1576 * Use a request to extract the data - Jean II */
1577 wrqu.data.length = 0;
1578 wrqu.data.flags = 0;
1579 wireless_send_event(dev, SIOCGIWSCAN, &wrqu, NULL);
1580 }
1581 kfree(buf);
Christoph Hellwig95dd91f2005-06-19 01:27:56 +02001582 }
1583 break;
David Kilroy01632fa2008-08-21 23:27:58 +01001584 case HERMES_INQ_CHANNELINFO:
1585 {
1586 struct agere_ext_scan_info *bss;
1587
1588 if (!priv->scan_inprogress) {
1589 printk(KERN_DEBUG "%s: Got chaninfo without scan, "
1590 "len=%d\n", dev->name, len);
1591 break;
1592 }
1593
1594 /* An empty result indicates that the scan is complete */
1595 if (len == 0) {
1596 union iwreq_data wrqu;
1597
1598 /* Scan is no longer in progress */
1599 priv->scan_inprogress = 0;
1600
1601 wrqu.data.length = 0;
1602 wrqu.data.flags = 0;
1603 wireless_send_event(dev, SIOCGIWSCAN, &wrqu, NULL);
1604 break;
1605 }
1606
1607 /* Sanity check */
1608 else if (len > sizeof(*bss)) {
1609 printk(KERN_WARNING
1610 "%s: Ext scan results too large (%d bytes). "
1611 "Truncating results to %zd bytes.\n",
1612 dev->name, len, sizeof(*bss));
1613 len = sizeof(*bss);
1614 } else if (len < (offsetof(struct agere_ext_scan_info,
1615 data) + 2)) {
1616 /* Drop this result now so we don't have to
1617 * keep checking later */
1618 printk(KERN_WARNING
1619 "%s: Ext scan results too short (%d bytes)\n",
1620 dev->name, len);
1621 break;
1622 }
1623
1624 bss = kmalloc(sizeof(*bss), GFP_ATOMIC);
1625 if (bss == NULL)
1626 break;
1627
1628 /* Read scan data */
1629 err = hermes_bap_pread(hw, IRQ_BAP, (void *) bss, len,
1630 infofid, sizeof(info));
1631 if (err) {
1632 kfree(bss);
1633 break;
1634 }
1635
1636 orinoco_add_ext_scan_result(priv, bss);
1637
1638 kfree(bss);
1639 break;
1640 }
Christoph Hellwig95dd91f2005-06-19 01:27:56 +02001641 case HERMES_INQ_SEC_STAT_AGERE:
1642 /* Security status (Agere specific) */
1643 /* Ignore this frame for now */
1644 if (priv->firmware_type == FIRMWARE_TYPE_AGERE)
1645 break;
1646 /* fall through */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001647 default:
1648 printk(KERN_DEBUG "%s: Unknown information frame received: "
1649 "type 0x%04x, length %d\n", dev->name, type, len);
1650 /* We don't actually do anything about it */
1651 break;
1652 }
1653}
1654
1655static void __orinoco_ev_infdrop(struct net_device *dev, hermes_t *hw)
1656{
1657 if (net_ratelimit())
1658 printk(KERN_DEBUG "%s: Information frame lost.\n", dev->name);
1659}
1660
1661/********************************************************************/
1662/* Internal hardware control routines */
1663/********************************************************************/
1664
1665int __orinoco_up(struct net_device *dev)
1666{
1667 struct orinoco_private *priv = netdev_priv(dev);
1668 struct hermes *hw = &priv->hw;
1669 int err;
1670
Christoph Hellwig84d8a2f2005-05-14 17:30:22 +02001671 netif_carrier_off(dev); /* just to make sure */
1672
Linus Torvalds1da177e2005-04-16 15:20:36 -07001673 err = __orinoco_program_rids(dev);
1674 if (err) {
1675 printk(KERN_ERR "%s: Error %d configuring card\n",
1676 dev->name, err);
1677 return err;
1678 }
1679
1680 /* Fire things up again */
1681 hermes_set_irqmask(hw, ORINOCO_INTEN);
1682 err = hermes_enable_port(hw, 0);
1683 if (err) {
1684 printk(KERN_ERR "%s: Error %d enabling MAC port\n",
1685 dev->name, err);
1686 return err;
1687 }
1688
1689 netif_start_queue(dev);
1690
1691 return 0;
1692}
David Kilroy21312662009-02-04 23:05:47 +00001693EXPORT_SYMBOL(__orinoco_up);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001694
1695int __orinoco_down(struct net_device *dev)
1696{
1697 struct orinoco_private *priv = netdev_priv(dev);
1698 struct hermes *hw = &priv->hw;
1699 int err;
1700
1701 netif_stop_queue(dev);
1702
David Kilroya94e8422009-02-04 23:05:44 +00001703 if (!priv->hw_unavailable) {
1704 if (!priv->broken_disableport) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001705 err = hermes_disable_port(hw, 0);
1706 if (err) {
1707 /* Some firmwares (e.g. Intersil 1.3.x) seem
1708 * to have problems disabling the port, oh
1709 * well, too bad. */
1710 printk(KERN_WARNING "%s: Error %d disabling MAC port\n",
1711 dev->name, err);
1712 priv->broken_disableport = 1;
1713 }
1714 }
1715 hermes_set_irqmask(hw, 0);
1716 hermes_write_regn(hw, EVACK, 0xffff);
1717 }
David Kilroy6fe9deb2009-02-04 23:05:43 +00001718
Linus Torvalds1da177e2005-04-16 15:20:36 -07001719 /* firmware will have to reassociate */
1720 netif_carrier_off(dev);
1721 priv->last_linkstatus = 0xffff;
1722
1723 return 0;
1724}
David Kilroy21312662009-02-04 23:05:47 +00001725EXPORT_SYMBOL(__orinoco_down);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001726
Pavel Roskin37a6c612006-04-07 04:10:49 -04001727static int orinoco_allocate_fid(struct net_device *dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001728{
1729 struct orinoco_private *priv = netdev_priv(dev);
1730 struct hermes *hw = &priv->hw;
1731 int err;
1732
Linus Torvalds1da177e2005-04-16 15:20:36 -07001733 err = hermes_allocate(hw, priv->nicbuf_size, &priv->txfid);
David Gibsonb24d4582005-05-12 20:04:16 -04001734 if (err == -EIO && priv->nicbuf_size > TX_NICBUF_SIZE_BUG) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001735 /* Try workaround for old Symbol firmware bug */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001736 priv->nicbuf_size = TX_NICBUF_SIZE_BUG;
1737 err = hermes_allocate(hw, priv->nicbuf_size, &priv->txfid);
David Kilroy21312662009-02-04 23:05:47 +00001738
1739 printk(KERN_WARNING "%s: firmware ALLOC bug detected "
1740 "(old Symbol firmware?). Work around %s\n",
1741 dev->name, err ? "failed!" : "ok.");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001742 }
1743
1744 return err;
1745}
1746
Pavel Roskin37a6c612006-04-07 04:10:49 -04001747int orinoco_reinit_firmware(struct net_device *dev)
1748{
1749 struct orinoco_private *priv = netdev_priv(dev);
1750 struct hermes *hw = &priv->hw;
1751 int err;
1752
1753 err = hermes_init(hw);
Andrey Borzenkov0df6cbb2008-10-12 20:15:43 +04001754 if (priv->do_fw_download && !err) {
1755 err = orinoco_download(priv);
1756 if (err)
1757 priv->do_fw_download = 0;
1758 }
Pavel Roskin37a6c612006-04-07 04:10:49 -04001759 if (!err)
1760 err = orinoco_allocate_fid(dev);
1761
1762 return err;
1763}
David Kilroy21312662009-02-04 23:05:47 +00001764EXPORT_SYMBOL(orinoco_reinit_firmware);
Pavel Roskin37a6c612006-04-07 04:10:49 -04001765
Linus Torvalds1da177e2005-04-16 15:20:36 -07001766static int __orinoco_hw_set_bitrate(struct orinoco_private *priv)
1767{
1768 hermes_t *hw = &priv->hw;
David Kilroyb2f30a02009-02-04 23:05:46 +00001769 int ratemode = priv->bitratemode;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001770 int err = 0;
1771
David Kilroyb2f30a02009-02-04 23:05:46 +00001772 if (ratemode >= BITRATE_TABLE_SIZE) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001773 printk(KERN_ERR "%s: BUG: Invalid bitrate mode %d\n",
David Kilroyb2f30a02009-02-04 23:05:46 +00001774 priv->ndev->name, ratemode);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001775 return -EINVAL;
1776 }
1777
1778 switch (priv->firmware_type) {
1779 case FIRMWARE_TYPE_AGERE:
1780 err = hermes_write_wordrec(hw, USER_BAP,
David Kilroyb2f30a02009-02-04 23:05:46 +00001781 HERMES_RID_CNFTXRATECONTROL,
1782 bitrate_table[ratemode].agere_txratectrl);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001783 break;
1784 case FIRMWARE_TYPE_INTERSIL:
1785 case FIRMWARE_TYPE_SYMBOL:
1786 err = hermes_write_wordrec(hw, USER_BAP,
David Kilroyb2f30a02009-02-04 23:05:46 +00001787 HERMES_RID_CNFTXRATECONTROL,
1788 bitrate_table[ratemode].intersil_txratectrl);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001789 break;
1790 default:
1791 BUG();
1792 }
1793
1794 return err;
1795}
1796
Christoph Hellwig16739b02005-06-19 01:27:51 +02001797/* Set fixed AP address */
1798static int __orinoco_hw_set_wap(struct orinoco_private *priv)
1799{
1800 int roaming_flag;
1801 int err = 0;
1802 hermes_t *hw = &priv->hw;
1803
1804 switch (priv->firmware_type) {
1805 case FIRMWARE_TYPE_AGERE:
1806 /* not supported */
1807 break;
1808 case FIRMWARE_TYPE_INTERSIL:
1809 if (priv->bssid_fixed)
1810 roaming_flag = 2;
1811 else
1812 roaming_flag = 1;
1813
1814 err = hermes_write_wordrec(hw, USER_BAP,
1815 HERMES_RID_CNFROAMINGMODE,
1816 roaming_flag);
1817 break;
1818 case FIRMWARE_TYPE_SYMBOL:
1819 err = HERMES_WRITE_RECORD(hw, USER_BAP,
1820 HERMES_RID_CNFMANDATORYBSSID_SYMBOL,
1821 &priv->desired_bssid);
1822 break;
1823 }
1824 return err;
1825}
1826
Linus Torvalds1da177e2005-04-16 15:20:36 -07001827/* Change the WEP keys and/or the current keys. Can be called
David Kilroyd03032a2008-08-21 23:28:02 +01001828 * either from __orinoco_hw_setup_enc() or directly from
Linus Torvalds1da177e2005-04-16 15:20:36 -07001829 * orinoco_ioctl_setiwencode(). In the later case the association
1830 * with the AP is not broken (if the firmware can handle it),
1831 * which is needed for 802.1x implementations. */
1832static int __orinoco_hw_setup_wepkeys(struct orinoco_private *priv)
1833{
1834 hermes_t *hw = &priv->hw;
1835 int err = 0;
1836
1837 switch (priv->firmware_type) {
1838 case FIRMWARE_TYPE_AGERE:
1839 err = HERMES_WRITE_RECORD(hw, USER_BAP,
1840 HERMES_RID_CNFWEPKEYS_AGERE,
1841 &priv->keys);
1842 if (err)
1843 return err;
1844 err = hermes_write_wordrec(hw, USER_BAP,
1845 HERMES_RID_CNFTXKEY_AGERE,
1846 priv->tx_key);
1847 if (err)
1848 return err;
1849 break;
1850 case FIRMWARE_TYPE_INTERSIL:
1851 case FIRMWARE_TYPE_SYMBOL:
1852 {
1853 int keylen;
1854 int i;
1855
David Kilroyb2f30a02009-02-04 23:05:46 +00001856 /* Force uniform key length to work around
1857 * firmware bugs */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001858 keylen = le16_to_cpu(priv->keys[priv->tx_key].len);
David Kilroy6fe9deb2009-02-04 23:05:43 +00001859
Linus Torvalds1da177e2005-04-16 15:20:36 -07001860 if (keylen > LARGE_KEY_SIZE) {
1861 printk(KERN_ERR "%s: BUG: Key %d has oversize length %d.\n",
1862 priv->ndev->name, priv->tx_key, keylen);
1863 return -E2BIG;
1864 }
1865
1866 /* Write all 4 keys */
David Kilroya94e8422009-02-04 23:05:44 +00001867 for (i = 0; i < ORINOCO_MAX_KEYS; i++) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001868 err = hermes_write_ltv(hw, USER_BAP,
David Kilroyb2f30a02009-02-04 23:05:46 +00001869 HERMES_RID_CNFDEFAULTKEY0 + i,
1870 HERMES_BYTES_TO_RECLEN(keylen),
1871 priv->keys[i].data);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001872 if (err)
1873 return err;
1874 }
1875
1876 /* Write the index of the key used in transmission */
1877 err = hermes_write_wordrec(hw, USER_BAP,
David Kilroyb2f30a02009-02-04 23:05:46 +00001878 HERMES_RID_CNFWEPDEFAULTKEYID,
1879 priv->tx_key);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001880 if (err)
1881 return err;
1882 }
1883 break;
1884 }
1885
1886 return 0;
1887}
1888
David Kilroyd03032a2008-08-21 23:28:02 +01001889static int __orinoco_hw_setup_enc(struct orinoco_private *priv)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001890{
1891 hermes_t *hw = &priv->hw;
1892 int err = 0;
1893 int master_wep_flag;
1894 int auth_flag;
David Kilroy4ae6ee22008-08-21 23:27:59 +01001895 int enc_flag;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001896
David Kilroyd03032a2008-08-21 23:28:02 +01001897 /* Setup WEP keys for WEP and WPA */
1898 if (priv->encode_alg)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001899 __orinoco_hw_setup_wepkeys(priv);
1900
1901 if (priv->wep_restrict)
1902 auth_flag = HERMES_AUTH_SHARED_KEY;
1903 else
1904 auth_flag = HERMES_AUTH_OPEN;
1905
David Kilroyd03032a2008-08-21 23:28:02 +01001906 if (priv->wpa_enabled)
1907 enc_flag = 2;
1908 else if (priv->encode_alg == IW_ENCODE_ALG_WEP)
David Kilroy4ae6ee22008-08-21 23:27:59 +01001909 enc_flag = 1;
1910 else
1911 enc_flag = 0;
1912
Linus Torvalds1da177e2005-04-16 15:20:36 -07001913 switch (priv->firmware_type) {
1914 case FIRMWARE_TYPE_AGERE: /* Agere style WEP */
David Kilroy4ae6ee22008-08-21 23:27:59 +01001915 if (priv->encode_alg == IW_ENCODE_ALG_WEP) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001916 /* Enable the shared-key authentication. */
1917 err = hermes_write_wordrec(hw, USER_BAP,
David Kilroyb2f30a02009-02-04 23:05:46 +00001918 HERMES_RID_CNFAUTHENTICATION_AGERE,
1919 auth_flag);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001920 }
1921 err = hermes_write_wordrec(hw, USER_BAP,
1922 HERMES_RID_CNFWEPENABLED_AGERE,
David Kilroy4ae6ee22008-08-21 23:27:59 +01001923 enc_flag);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001924 if (err)
1925 return err;
David Kilroyd03032a2008-08-21 23:28:02 +01001926
1927 if (priv->has_wpa) {
1928 /* Set WPA key management */
1929 err = hermes_write_wordrec(hw, USER_BAP,
1930 HERMES_RID_CNFSETWPAAUTHMGMTSUITE_AGERE,
1931 priv->key_mgmt);
1932 if (err)
1933 return err;
1934 }
1935
Linus Torvalds1da177e2005-04-16 15:20:36 -07001936 break;
1937
1938 case FIRMWARE_TYPE_INTERSIL: /* Intersil style WEP */
1939 case FIRMWARE_TYPE_SYMBOL: /* Symbol style WEP */
David Kilroy4ae6ee22008-08-21 23:27:59 +01001940 if (priv->encode_alg == IW_ENCODE_ALG_WEP) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001941 if (priv->wep_restrict ||
1942 (priv->firmware_type == FIRMWARE_TYPE_SYMBOL))
1943 master_wep_flag = HERMES_WEP_PRIVACY_INVOKED |
1944 HERMES_WEP_EXCL_UNENCRYPTED;
1945 else
1946 master_wep_flag = HERMES_WEP_PRIVACY_INVOKED;
1947
1948 err = hermes_write_wordrec(hw, USER_BAP,
1949 HERMES_RID_CNFAUTHENTICATION,
1950 auth_flag);
1951 if (err)
1952 return err;
1953 } else
1954 master_wep_flag = 0;
1955
1956 if (priv->iw_mode == IW_MODE_MONITOR)
1957 master_wep_flag |= HERMES_WEP_HOST_DECRYPT;
1958
1959 /* Master WEP setting : on/off */
1960 err = hermes_write_wordrec(hw, USER_BAP,
1961 HERMES_RID_CNFWEPFLAGS_INTERSIL,
1962 master_wep_flag);
1963 if (err)
David Kilroy6fe9deb2009-02-04 23:05:43 +00001964 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001965
1966 break;
1967 }
1968
1969 return 0;
1970}
1971
David Kilroyd03032a2008-08-21 23:28:02 +01001972/* key must be 32 bytes, including the tx and rx MIC keys.
1973 * rsc must be 8 bytes
1974 * tsc must be 8 bytes or NULL
1975 */
1976static int __orinoco_hw_set_tkip_key(hermes_t *hw, int key_idx, int set_tx,
1977 u8 *key, u8 *rsc, u8 *tsc)
1978{
1979 struct {
1980 __le16 idx;
1981 u8 rsc[IW_ENCODE_SEQ_MAX_SIZE];
1982 u8 key[TKIP_KEYLEN];
1983 u8 tx_mic[MIC_KEYLEN];
1984 u8 rx_mic[MIC_KEYLEN];
1985 u8 tsc[IW_ENCODE_SEQ_MAX_SIZE];
1986 } __attribute__ ((packed)) buf;
1987 int ret;
1988 int err;
1989 int k;
1990 u16 xmitting;
1991
1992 key_idx &= 0x3;
1993
1994 if (set_tx)
1995 key_idx |= 0x8000;
1996
1997 buf.idx = cpu_to_le16(key_idx);
1998 memcpy(buf.key, key,
1999 sizeof(buf.key) + sizeof(buf.tx_mic) + sizeof(buf.rx_mic));
2000
2001 if (rsc == NULL)
2002 memset(buf.rsc, 0, sizeof(buf.rsc));
2003 else
2004 memcpy(buf.rsc, rsc, sizeof(buf.rsc));
2005
2006 if (tsc == NULL) {
2007 memset(buf.tsc, 0, sizeof(buf.tsc));
2008 buf.tsc[4] = 0x10;
2009 } else {
2010 memcpy(buf.tsc, tsc, sizeof(buf.tsc));
2011 }
2012
2013 /* Wait upto 100ms for tx queue to empty */
2014 k = 100;
2015 do {
2016 k--;
2017 udelay(1000);
2018 ret = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_TXQUEUEEMPTY,
2019 &xmitting);
2020 if (ret)
2021 break;
2022 } while ((k > 0) && xmitting);
2023
2024 if (k == 0)
2025 ret = -ETIMEDOUT;
2026
2027 err = HERMES_WRITE_RECORD(hw, USER_BAP,
2028 HERMES_RID_CNFADDDEFAULTTKIPKEY_AGERE,
2029 &buf);
2030
2031 return ret ? ret : err;
2032}
2033
2034static int orinoco_clear_tkip_key(struct orinoco_private *priv,
2035 int key_idx)
2036{
2037 hermes_t *hw = &priv->hw;
2038 int err;
2039
2040 memset(&priv->tkip_key[key_idx], 0, sizeof(priv->tkip_key[key_idx]));
2041 err = hermes_write_wordrec(hw, USER_BAP,
2042 HERMES_RID_CNFREMDEFAULTTKIPKEY_AGERE,
2043 key_idx);
2044 if (err)
2045 printk(KERN_WARNING "%s: Error %d clearing TKIP key %d\n",
2046 priv->ndev->name, err, key_idx);
2047 return err;
2048}
2049
Linus Torvalds1da177e2005-04-16 15:20:36 -07002050static int __orinoco_program_rids(struct net_device *dev)
2051{
2052 struct orinoco_private *priv = netdev_priv(dev);
2053 hermes_t *hw = &priv->hw;
2054 int err;
2055 struct hermes_idstring idbuf;
2056
2057 /* Set the MAC address */
2058 err = hermes_write_ltv(hw, USER_BAP, HERMES_RID_CNFOWNMACADDR,
2059 HERMES_BYTES_TO_RECLEN(ETH_ALEN), dev->dev_addr);
2060 if (err) {
2061 printk(KERN_ERR "%s: Error %d setting MAC address\n",
2062 dev->name, err);
2063 return err;
2064 }
2065
2066 /* Set up the link mode */
2067 err = hermes_write_wordrec(hw, USER_BAP, HERMES_RID_CNFPORTTYPE,
2068 priv->port_type);
2069 if (err) {
2070 printk(KERN_ERR "%s: Error %d setting port type\n",
2071 dev->name, err);
2072 return err;
2073 }
2074 /* Set the channel/frequency */
David Gibsond51d8b12005-05-12 20:03:36 -04002075 if (priv->channel != 0 && priv->iw_mode != IW_MODE_INFRA) {
2076 err = hermes_write_wordrec(hw, USER_BAP,
2077 HERMES_RID_CNFOWNCHANNEL,
2078 priv->channel);
2079 if (err) {
2080 printk(KERN_ERR "%s: Error %d setting channel %d\n",
2081 dev->name, err, priv->channel);
2082 return err;
2083 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002084 }
2085
2086 if (priv->has_ibss) {
2087 u16 createibss;
2088
2089 if ((strlen(priv->desired_essid) == 0) && (priv->createibss)) {
2090 printk(KERN_WARNING "%s: This firmware requires an "
2091 "ESSID in IBSS-Ad-Hoc mode.\n", dev->name);
2092 /* With wvlan_cs, in this case, we would crash.
2093 * hopefully, this driver will behave better...
2094 * Jean II */
2095 createibss = 0;
2096 } else {
2097 createibss = priv->createibss;
2098 }
David Kilroy6fe9deb2009-02-04 23:05:43 +00002099
Linus Torvalds1da177e2005-04-16 15:20:36 -07002100 err = hermes_write_wordrec(hw, USER_BAP,
2101 HERMES_RID_CNFCREATEIBSS,
2102 createibss);
2103 if (err) {
2104 printk(KERN_ERR "%s: Error %d setting CREATEIBSS\n",
2105 dev->name, err);
2106 return err;
2107 }
2108 }
2109
Christoph Hellwig16739b02005-06-19 01:27:51 +02002110 /* Set the desired BSSID */
2111 err = __orinoco_hw_set_wap(priv);
2112 if (err) {
2113 printk(KERN_ERR "%s: Error %d setting AP address\n",
2114 dev->name, err);
2115 return err;
2116 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002117 /* Set the desired ESSID */
2118 idbuf.len = cpu_to_le16(strlen(priv->desired_essid));
2119 memcpy(&idbuf.val, priv->desired_essid, sizeof(idbuf.val));
2120 /* WinXP wants partner to configure OWNSSID even in IBSS mode. (jimc) */
2121 err = hermes_write_ltv(hw, USER_BAP, HERMES_RID_CNFOWNSSID,
David Kilroyb2f30a02009-02-04 23:05:46 +00002122 HERMES_BYTES_TO_RECLEN(strlen(priv->desired_essid)+2),
2123 &idbuf);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002124 if (err) {
2125 printk(KERN_ERR "%s: Error %d setting OWNSSID\n",
2126 dev->name, err);
2127 return err;
2128 }
2129 err = hermes_write_ltv(hw, USER_BAP, HERMES_RID_CNFDESIREDSSID,
David Kilroyb2f30a02009-02-04 23:05:46 +00002130 HERMES_BYTES_TO_RECLEN(strlen(priv->desired_essid)+2),
2131 &idbuf);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002132 if (err) {
2133 printk(KERN_ERR "%s: Error %d setting DESIREDSSID\n",
2134 dev->name, err);
2135 return err;
2136 }
2137
2138 /* Set the station name */
2139 idbuf.len = cpu_to_le16(strlen(priv->nick));
2140 memcpy(&idbuf.val, priv->nick, sizeof(idbuf.val));
2141 err = hermes_write_ltv(hw, USER_BAP, HERMES_RID_CNFOWNNAME,
2142 HERMES_BYTES_TO_RECLEN(strlen(priv->nick)+2),
2143 &idbuf);
2144 if (err) {
2145 printk(KERN_ERR "%s: Error %d setting nickname\n",
2146 dev->name, err);
2147 return err;
2148 }
2149
2150 /* Set AP density */
2151 if (priv->has_sensitivity) {
2152 err = hermes_write_wordrec(hw, USER_BAP,
2153 HERMES_RID_CNFSYSTEMSCALE,
2154 priv->ap_density);
2155 if (err) {
David Kilroyb2f30a02009-02-04 23:05:46 +00002156 printk(KERN_WARNING "%s: Error %d setting SYSTEMSCALE. "
Linus Torvalds1da177e2005-04-16 15:20:36 -07002157 "Disabling sensitivity control\n",
2158 dev->name, err);
2159
2160 priv->has_sensitivity = 0;
2161 }
2162 }
2163
2164 /* Set RTS threshold */
2165 err = hermes_write_wordrec(hw, USER_BAP, HERMES_RID_CNFRTSTHRESHOLD,
2166 priv->rts_thresh);
2167 if (err) {
2168 printk(KERN_ERR "%s: Error %d setting RTS threshold\n",
2169 dev->name, err);
2170 return err;
2171 }
2172
2173 /* Set fragmentation threshold or MWO robustness */
2174 if (priv->has_mwo)
2175 err = hermes_write_wordrec(hw, USER_BAP,
2176 HERMES_RID_CNFMWOROBUST_AGERE,
2177 priv->mwo_robust);
2178 else
2179 err = hermes_write_wordrec(hw, USER_BAP,
2180 HERMES_RID_CNFFRAGMENTATIONTHRESHOLD,
2181 priv->frag_thresh);
2182 if (err) {
2183 printk(KERN_ERR "%s: Error %d setting fragmentation\n",
2184 dev->name, err);
2185 return err;
2186 }
2187
2188 /* Set bitrate */
2189 err = __orinoco_hw_set_bitrate(priv);
2190 if (err) {
2191 printk(KERN_ERR "%s: Error %d setting bitrate\n",
2192 dev->name, err);
2193 return err;
2194 }
2195
2196 /* Set power management */
2197 if (priv->has_pm) {
2198 err = hermes_write_wordrec(hw, USER_BAP,
2199 HERMES_RID_CNFPMENABLED,
2200 priv->pm_on);
2201 if (err) {
2202 printk(KERN_ERR "%s: Error %d setting up PM\n",
2203 dev->name, err);
2204 return err;
2205 }
2206
2207 err = hermes_write_wordrec(hw, USER_BAP,
2208 HERMES_RID_CNFMULTICASTRECEIVE,
2209 priv->pm_mcast);
2210 if (err) {
2211 printk(KERN_ERR "%s: Error %d setting up PM\n",
2212 dev->name, err);
2213 return err;
2214 }
2215 err = hermes_write_wordrec(hw, USER_BAP,
2216 HERMES_RID_CNFMAXSLEEPDURATION,
2217 priv->pm_period);
2218 if (err) {
2219 printk(KERN_ERR "%s: Error %d setting up PM\n",
2220 dev->name, err);
2221 return err;
2222 }
2223 err = hermes_write_wordrec(hw, USER_BAP,
2224 HERMES_RID_CNFPMHOLDOVERDURATION,
2225 priv->pm_timeout);
2226 if (err) {
2227 printk(KERN_ERR "%s: Error %d setting up PM\n",
2228 dev->name, err);
2229 return err;
2230 }
2231 }
2232
2233 /* Set preamble - only for Symbol so far... */
2234 if (priv->has_preamble) {
2235 err = hermes_write_wordrec(hw, USER_BAP,
2236 HERMES_RID_CNFPREAMBLE_SYMBOL,
2237 priv->preamble);
2238 if (err) {
2239 printk(KERN_ERR "%s: Error %d setting preamble\n",
2240 dev->name, err);
2241 return err;
2242 }
2243 }
2244
2245 /* Set up encryption */
David Kilroyd03032a2008-08-21 23:28:02 +01002246 if (priv->has_wep || priv->has_wpa) {
2247 err = __orinoco_hw_setup_enc(priv);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002248 if (err) {
David Kilroyd03032a2008-08-21 23:28:02 +01002249 printk(KERN_ERR "%s: Error %d activating encryption\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -07002250 dev->name, err);
2251 return err;
2252 }
2253 }
2254
Christoph Hellwig98c4cae2005-06-19 01:28:06 +02002255 if (priv->iw_mode == IW_MODE_MONITOR) {
2256 /* Enable monitor mode */
2257 dev->type = ARPHRD_IEEE80211;
David Kilroy6fe9deb2009-02-04 23:05:43 +00002258 err = hermes_docmd_wait(hw, HERMES_CMD_TEST |
Christoph Hellwig98c4cae2005-06-19 01:28:06 +02002259 HERMES_TEST_MONITOR, 0, NULL);
2260 } else {
2261 /* Disable monitor mode */
2262 dev->type = ARPHRD_ETHER;
2263 err = hermes_docmd_wait(hw, HERMES_CMD_TEST |
2264 HERMES_TEST_STOP, 0, NULL);
2265 }
2266 if (err)
2267 return err;
2268
Linus Torvalds1da177e2005-04-16 15:20:36 -07002269 /* Set promiscuity / multicast*/
2270 priv->promiscuous = 0;
2271 priv->mc_count = 0;
Herbert Xu932ff272006-06-09 12:20:56 -07002272
2273 /* FIXME: what about netif_tx_lock */
2274 __orinoco_set_multicast_list(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002275
2276 return 0;
2277}
2278
2279/* FIXME: return int? */
2280static void
2281__orinoco_set_multicast_list(struct net_device *dev)
2282{
2283 struct orinoco_private *priv = netdev_priv(dev);
2284 hermes_t *hw = &priv->hw;
2285 int err = 0;
2286 int promisc, mc_count;
2287
2288 /* The Hermes doesn't seem to have an allmulti mode, so we go
2289 * into promiscuous mode and let the upper levels deal. */
David Kilroya94e8422009-02-04 23:05:44 +00002290 if ((dev->flags & IFF_PROMISC) || (dev->flags & IFF_ALLMULTI) ||
2291 (dev->mc_count > MAX_MULTICAST(priv))) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002292 promisc = 1;
2293 mc_count = 0;
2294 } else {
2295 promisc = 0;
2296 mc_count = dev->mc_count;
2297 }
2298
2299 if (promisc != priv->promiscuous) {
2300 err = hermes_write_wordrec(hw, USER_BAP,
2301 HERMES_RID_CNFPROMISCUOUSMODE,
2302 promisc);
2303 if (err) {
2304 printk(KERN_ERR "%s: Error %d setting PROMISCUOUSMODE to 1.\n",
2305 dev->name, err);
David Kilroy6fe9deb2009-02-04 23:05:43 +00002306 } else
Linus Torvalds1da177e2005-04-16 15:20:36 -07002307 priv->promiscuous = promisc;
2308 }
2309
David Kilroy667d4102008-08-23 19:03:34 +01002310 /* If we're not in promiscuous mode, then we need to set the
2311 * group address if either we want to multicast, or if we were
2312 * multicasting and want to stop */
David Kilroya94e8422009-02-04 23:05:44 +00002313 if (!promisc && (mc_count || priv->mc_count)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002314 struct dev_mc_list *p = dev->mc_list;
2315 struct hermes_multicast mclist;
2316 int i;
2317
2318 for (i = 0; i < mc_count; i++) {
2319 /* paranoia: is list shorter than mc_count? */
David Kilroya94e8422009-02-04 23:05:44 +00002320 BUG_ON(!p);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002321 /* paranoia: bad address size in list? */
2322 BUG_ON(p->dmi_addrlen != ETH_ALEN);
David Kilroy6fe9deb2009-02-04 23:05:43 +00002323
Linus Torvalds1da177e2005-04-16 15:20:36 -07002324 memcpy(mclist.addr[i], p->dmi_addr, ETH_ALEN);
2325 p = p->next;
2326 }
David Kilroy6fe9deb2009-02-04 23:05:43 +00002327
Linus Torvalds1da177e2005-04-16 15:20:36 -07002328 if (p)
2329 printk(KERN_WARNING "%s: Multicast list is "
2330 "longer than mc_count\n", dev->name);
2331
David Kilroy667d4102008-08-23 19:03:34 +01002332 err = hermes_write_ltv(hw, USER_BAP,
2333 HERMES_RID_CNFGROUPADDRESSES,
2334 HERMES_BYTES_TO_RECLEN(mc_count * ETH_ALEN),
2335 &mclist);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002336 if (err)
2337 printk(KERN_ERR "%s: Error %d setting multicast list.\n",
2338 dev->name, err);
2339 else
2340 priv->mc_count = mc_count;
2341 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002342}
2343
Linus Torvalds1da177e2005-04-16 15:20:36 -07002344/* This must be called from user context, without locks held - use
2345 * schedule_work() */
David Howellsc4028952006-11-22 14:57:56 +00002346static void orinoco_reset(struct work_struct *work)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002347{
David Howellsc4028952006-11-22 14:57:56 +00002348 struct orinoco_private *priv =
2349 container_of(work, struct orinoco_private, reset_work);
2350 struct net_device *dev = priv->ndev;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002351 struct hermes *hw = &priv->hw;
Christoph Hellwig8551cb92005-05-14 17:30:04 +02002352 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002353 unsigned long flags;
2354
2355 if (orinoco_lock(priv, &flags) != 0)
2356 /* When the hardware becomes available again, whatever
2357 * detects that is responsible for re-initializing
2358 * it. So no need for anything further */
2359 return;
2360
2361 netif_stop_queue(dev);
2362
2363 /* Shut off interrupts. Depending on what state the hardware
2364 * is in, this might not work, but we'll try anyway */
2365 hermes_set_irqmask(hw, 0);
2366 hermes_write_regn(hw, EVACK, 0xffff);
2367
2368 priv->hw_unavailable++;
2369 priv->last_linkstatus = 0xffff; /* firmware will have to reassociate */
2370 netif_carrier_off(dev);
2371
2372 orinoco_unlock(priv, &flags);
2373
David Kilroy6fe9deb2009-02-04 23:05:43 +00002374 /* Scanning support: Cleanup of driver struct */
Dan Williams1e3428e2007-10-10 23:56:25 -04002375 orinoco_clear_scan_results(priv, 0);
Christoph Hellwig95dd91f2005-06-19 01:27:56 +02002376 priv->scan_inprogress = 0;
2377
Christoph Hellwig8551cb92005-05-14 17:30:04 +02002378 if (priv->hard_reset) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002379 err = (*priv->hard_reset)(priv);
Christoph Hellwig8551cb92005-05-14 17:30:04 +02002380 if (err) {
2381 printk(KERN_ERR "%s: orinoco_reset: Error %d "
2382 "performing hard reset\n", dev->name, err);
2383 goto disable;
2384 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002385 }
2386
2387 err = orinoco_reinit_firmware(dev);
2388 if (err) {
2389 printk(KERN_ERR "%s: orinoco_reset: Error %d re-initializing firmware\n",
2390 dev->name, err);
Christoph Hellwig8551cb92005-05-14 17:30:04 +02002391 goto disable;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002392 }
2393
David Kilroyb2f30a02009-02-04 23:05:46 +00002394 /* This has to be called from user context */
2395 spin_lock_irq(&priv->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002396
2397 priv->hw_unavailable--;
2398
2399 /* priv->open or priv->hw_unavailable might have changed while
2400 * we dropped the lock */
David Kilroya94e8422009-02-04 23:05:44 +00002401 if (priv->open && (!priv->hw_unavailable)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002402 err = __orinoco_up(dev);
2403 if (err) {
2404 printk(KERN_ERR "%s: orinoco_reset: Error %d reenabling card\n",
2405 dev->name, err);
2406 } else
2407 dev->trans_start = jiffies;
2408 }
2409
2410 spin_unlock_irq(&priv->lock);
2411
2412 return;
Christoph Hellwig8551cb92005-05-14 17:30:04 +02002413 disable:
2414 hermes_set_irqmask(hw, 0);
2415 netif_device_detach(dev);
2416 printk(KERN_ERR "%s: Device has been disabled!\n", dev->name);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002417}
2418
2419/********************************************************************/
2420/* Interrupt handler */
2421/********************************************************************/
2422
2423static void __orinoco_ev_tick(struct net_device *dev, hermes_t *hw)
2424{
2425 printk(KERN_DEBUG "%s: TICK\n", dev->name);
2426}
2427
2428static void __orinoco_ev_wterr(struct net_device *dev, hermes_t *hw)
2429{
2430 /* This seems to happen a fair bit under load, but ignoring it
2431 seems to work fine...*/
2432 printk(KERN_DEBUG "%s: MAC controller error (WTERR). Ignoring.\n",
2433 dev->name);
2434}
2435
David Howells7d12e782006-10-05 14:55:46 +01002436irqreturn_t orinoco_interrupt(int irq, void *dev_id)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002437{
Jeff Garzikc31f28e2006-10-06 14:56:04 -04002438 struct net_device *dev = dev_id;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002439 struct orinoco_private *priv = netdev_priv(dev);
2440 hermes_t *hw = &priv->hw;
2441 int count = MAX_IRQLOOPS_PER_IRQ;
2442 u16 evstat, events;
David Kilroy21312662009-02-04 23:05:47 +00002443 /* These are used to detect a runaway interrupt situation.
2444 *
2445 * If we get more than MAX_IRQLOOPS_PER_JIFFY iterations in a jiffy,
2446 * we panic and shut down the hardware
2447 */
2448 /* jiffies value the last time we were called */
2449 static int last_irq_jiffy; /* = 0 */
2450 static int loops_this_jiffy; /* = 0 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002451 unsigned long flags;
2452
2453 if (orinoco_lock(priv, &flags) != 0) {
2454 /* If hw is unavailable - we don't know if the irq was
2455 * for us or not */
2456 return IRQ_HANDLED;
2457 }
2458
2459 evstat = hermes_read_regn(hw, EVSTAT);
2460 events = evstat & hw->inten;
David Kilroya94e8422009-02-04 23:05:44 +00002461 if (!events) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002462 orinoco_unlock(priv, &flags);
2463 return IRQ_NONE;
2464 }
David Kilroy6fe9deb2009-02-04 23:05:43 +00002465
Linus Torvalds1da177e2005-04-16 15:20:36 -07002466 if (jiffies != last_irq_jiffy)
2467 loops_this_jiffy = 0;
2468 last_irq_jiffy = jiffies;
2469
2470 while (events && count--) {
2471 if (++loops_this_jiffy > MAX_IRQLOOPS_PER_JIFFY) {
2472 printk(KERN_WARNING "%s: IRQ handler is looping too "
2473 "much! Resetting.\n", dev->name);
2474 /* Disable interrupts for now */
2475 hermes_set_irqmask(hw, 0);
2476 schedule_work(&priv->reset_work);
2477 break;
2478 }
2479
2480 /* Check the card hasn't been removed */
David Kilroya94e8422009-02-04 23:05:44 +00002481 if (!hermes_present(hw)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002482 DEBUG(0, "orinoco_interrupt(): card removed\n");
2483 break;
2484 }
2485
2486 if (events & HERMES_EV_TICK)
2487 __orinoco_ev_tick(dev, hw);
2488 if (events & HERMES_EV_WTERR)
2489 __orinoco_ev_wterr(dev, hw);
2490 if (events & HERMES_EV_INFDROP)
2491 __orinoco_ev_infdrop(dev, hw);
2492 if (events & HERMES_EV_INFO)
2493 __orinoco_ev_info(dev, hw);
2494 if (events & HERMES_EV_RX)
2495 __orinoco_ev_rx(dev, hw);
2496 if (events & HERMES_EV_TXEXC)
2497 __orinoco_ev_txexc(dev, hw);
2498 if (events & HERMES_EV_TX)
2499 __orinoco_ev_tx(dev, hw);
2500 if (events & HERMES_EV_ALLOC)
2501 __orinoco_ev_alloc(dev, hw);
David Kilroy6fe9deb2009-02-04 23:05:43 +00002502
Christoph Hellwig84d8a2f2005-05-14 17:30:22 +02002503 hermes_write_regn(hw, EVACK, evstat);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002504
2505 evstat = hermes_read_regn(hw, EVSTAT);
2506 events = evstat & hw->inten;
2507 };
2508
2509 orinoco_unlock(priv, &flags);
2510 return IRQ_HANDLED;
2511}
David Kilroy21312662009-02-04 23:05:47 +00002512EXPORT_SYMBOL(orinoco_interrupt);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002513
2514/********************************************************************/
David Kilroy39d1ffe2008-11-22 10:37:28 +00002515/* Power management */
2516/********************************************************************/
2517#if defined(CONFIG_PM_SLEEP) && !defined(CONFIG_HERMES_CACHE_FW_ON_INIT)
2518static int orinoco_pm_notifier(struct notifier_block *notifier,
2519 unsigned long pm_event,
2520 void *unused)
2521{
2522 struct orinoco_private *priv = container_of(notifier,
2523 struct orinoco_private,
2524 pm_notifier);
2525
2526 /* All we need to do is cache the firmware before suspend, and
2527 * release it when we come out.
2528 *
2529 * Only need to do this if we're downloading firmware. */
2530 if (!priv->do_fw_download)
2531 return NOTIFY_DONE;
2532
2533 switch (pm_event) {
2534 case PM_HIBERNATION_PREPARE:
2535 case PM_SUSPEND_PREPARE:
2536 orinoco_cache_fw(priv, 0);
2537 break;
2538
2539 case PM_POST_RESTORE:
2540 /* Restore from hibernation failed. We need to clean
2541 * up in exactly the same way, so fall through. */
2542 case PM_POST_HIBERNATION:
2543 case PM_POST_SUSPEND:
2544 orinoco_uncache_fw(priv);
2545 break;
2546
2547 case PM_RESTORE_PREPARE:
2548 default:
2549 break;
2550 }
2551
2552 return NOTIFY_DONE;
2553}
2554#else /* !PM_SLEEP || HERMES_CACHE_FW_ON_INIT */
2555#define orinoco_pm_notifier NULL
2556#endif
2557
2558/********************************************************************/
Linus Torvalds1da177e2005-04-16 15:20:36 -07002559/* Initialization */
2560/********************************************************************/
2561
2562struct comp_id {
2563 u16 id, variant, major, minor;
2564} __attribute__ ((packed));
2565
2566static inline fwtype_t determine_firmware_type(struct comp_id *nic_id)
2567{
2568 if (nic_id->id < 0x8000)
2569 return FIRMWARE_TYPE_AGERE;
2570 else if (nic_id->id == 0x8000 && nic_id->major == 0)
2571 return FIRMWARE_TYPE_SYMBOL;
2572 else
2573 return FIRMWARE_TYPE_INTERSIL;
2574}
2575
2576/* Set priv->firmware type, determine firmware properties */
2577static int determine_firmware(struct net_device *dev)
2578{
2579 struct orinoco_private *priv = netdev_priv(dev);
2580 hermes_t *hw = &priv->hw;
2581 int err;
2582 struct comp_id nic_id, sta_id;
2583 unsigned int firmver;
Hennerich, Michaeldde6d432007-02-05 16:41:35 -08002584 char tmp[SYMBOL_MAX_VER_LEN+1] __attribute__((aligned(2)));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002585
2586 /* Get the hardware version */
2587 err = HERMES_READ_RECORD(hw, USER_BAP, HERMES_RID_NICID, &nic_id);
2588 if (err) {
2589 printk(KERN_ERR "%s: Cannot read hardware identity: error %d\n",
2590 dev->name, err);
2591 return err;
2592 }
2593
2594 le16_to_cpus(&nic_id.id);
2595 le16_to_cpus(&nic_id.variant);
2596 le16_to_cpus(&nic_id.major);
2597 le16_to_cpus(&nic_id.minor);
2598 printk(KERN_DEBUG "%s: Hardware identity %04x:%04x:%04x:%04x\n",
2599 dev->name, nic_id.id, nic_id.variant,
2600 nic_id.major, nic_id.minor);
2601
2602 priv->firmware_type = determine_firmware_type(&nic_id);
2603
2604 /* Get the firmware version */
2605 err = HERMES_READ_RECORD(hw, USER_BAP, HERMES_RID_STAID, &sta_id);
2606 if (err) {
2607 printk(KERN_ERR "%s: Cannot read station identity: error %d\n",
2608 dev->name, err);
2609 return err;
2610 }
2611
2612 le16_to_cpus(&sta_id.id);
2613 le16_to_cpus(&sta_id.variant);
2614 le16_to_cpus(&sta_id.major);
2615 le16_to_cpus(&sta_id.minor);
2616 printk(KERN_DEBUG "%s: Station identity %04x:%04x:%04x:%04x\n",
2617 dev->name, sta_id.id, sta_id.variant,
2618 sta_id.major, sta_id.minor);
2619
2620 switch (sta_id.id) {
2621 case 0x15:
2622 printk(KERN_ERR "%s: Primary firmware is active\n",
2623 dev->name);
2624 return -ENODEV;
2625 case 0x14b:
2626 printk(KERN_ERR "%s: Tertiary firmware is active\n",
2627 dev->name);
2628 return -ENODEV;
2629 case 0x1f: /* Intersil, Agere, Symbol Spectrum24 */
2630 case 0x21: /* Symbol Spectrum24 Trilogy */
2631 break;
2632 default:
2633 printk(KERN_NOTICE "%s: Unknown station ID, please report\n",
2634 dev->name);
2635 break;
2636 }
2637
2638 /* Default capabilities */
2639 priv->has_sensitivity = 1;
2640 priv->has_mwo = 0;
2641 priv->has_preamble = 0;
2642 priv->has_port3 = 1;
2643 priv->has_ibss = 1;
2644 priv->has_wep = 0;
2645 priv->has_big_wep = 0;
David Kilroy6eecad72008-08-21 23:27:56 +01002646 priv->has_alt_txcntl = 0;
David Kilroy01632fa2008-08-21 23:27:58 +01002647 priv->has_ext_scan = 0;
David Kilroyd03032a2008-08-21 23:28:02 +01002648 priv->has_wpa = 0;
David Kilroy3994d502008-08-21 23:27:54 +01002649 priv->do_fw_download = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002650
2651 /* Determine capabilities from the firmware version */
2652 switch (priv->firmware_type) {
2653 case FIRMWARE_TYPE_AGERE:
2654 /* Lucent Wavelan IEEE, Lucent Orinoco, Cabletron RoamAbout,
2655 ELSA, Melco, HP, IBM, Dell 1150, Compaq 110/210 */
2656 snprintf(priv->fw_name, sizeof(priv->fw_name) - 1,
2657 "Lucent/Agere %d.%02d", sta_id.major, sta_id.minor);
2658
2659 firmver = ((unsigned long)sta_id.major << 16) | sta_id.minor;
2660
2661 priv->has_ibss = (firmver >= 0x60006);
2662 priv->has_wep = (firmver >= 0x40020);
2663 priv->has_big_wep = 1; /* FIXME: this is wrong - how do we tell
2664 Gold cards from the others? */
2665 priv->has_mwo = (firmver >= 0x60000);
2666 priv->has_pm = (firmver >= 0x40020); /* Don't work in 7.52 ? */
2667 priv->ibss_port = 1;
Christoph Hellwig95dd91f2005-06-19 01:27:56 +02002668 priv->has_hostscan = (firmver >= 0x8000a);
David Kilroy3994d502008-08-21 23:27:54 +01002669 priv->do_fw_download = 1;
Christoph Hellwig98c4cae2005-06-19 01:28:06 +02002670 priv->broken_monitor = (firmver >= 0x80000);
David Kilroy6eecad72008-08-21 23:27:56 +01002671 priv->has_alt_txcntl = (firmver >= 0x90000); /* All 9.x ? */
David Kilroy01632fa2008-08-21 23:27:58 +01002672 priv->has_ext_scan = (firmver >= 0x90000); /* All 9.x ? */
David Kilroyd03032a2008-08-21 23:28:02 +01002673 priv->has_wpa = (firmver >= 0x9002a);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002674 /* Tested with Agere firmware :
2675 * 1.16 ; 4.08 ; 4.52 ; 6.04 ; 6.16 ; 7.28 => Jean II
2676 * Tested CableTron firmware : 4.32 => Anton */
2677 break;
2678 case FIRMWARE_TYPE_SYMBOL:
2679 /* Symbol , 3Com AirConnect, Intel, Ericsson WLAN */
2680 /* Intel MAC : 00:02:B3:* */
2681 /* 3Com MAC : 00:50:DA:* */
2682 memset(tmp, 0, sizeof(tmp));
2683 /* Get the Symbol firmware version */
2684 err = hermes_read_ltv(hw, USER_BAP,
2685 HERMES_RID_SECONDARYVERSION_SYMBOL,
2686 SYMBOL_MAX_VER_LEN, NULL, &tmp);
2687 if (err) {
2688 printk(KERN_WARNING
David Kilroyb2f30a02009-02-04 23:05:46 +00002689 "%s: Error %d reading Symbol firmware info. "
2690 "Wildly guessing capabilities...\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -07002691 dev->name, err);
2692 firmver = 0;
2693 tmp[0] = '\0';
2694 } else {
2695 /* The firmware revision is a string, the format is
2696 * something like : "V2.20-01".
2697 * Quick and dirty parsing... - Jean II
2698 */
David Kilroyb2f30a02009-02-04 23:05:46 +00002699 firmver = ((tmp[1] - '0') << 16)
2700 | ((tmp[3] - '0') << 12)
2701 | ((tmp[4] - '0') << 8)
2702 | ((tmp[6] - '0') << 4)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002703 | (tmp[7] - '0');
2704
2705 tmp[SYMBOL_MAX_VER_LEN] = '\0';
2706 }
2707
2708 snprintf(priv->fw_name, sizeof(priv->fw_name) - 1,
2709 "Symbol %s", tmp);
2710
2711 priv->has_ibss = (firmver >= 0x20000);
2712 priv->has_wep = (firmver >= 0x15012);
2713 priv->has_big_wep = (firmver >= 0x20000);
David Kilroy6fe9deb2009-02-04 23:05:43 +00002714 priv->has_pm = (firmver >= 0x20000 && firmver < 0x22000) ||
Linus Torvalds1da177e2005-04-16 15:20:36 -07002715 (firmver >= 0x29000 && firmver < 0x30000) ||
2716 firmver >= 0x31000;
2717 priv->has_preamble = (firmver >= 0x20000);
2718 priv->ibss_port = 4;
David Kilroy3994d502008-08-21 23:27:54 +01002719
2720 /* Symbol firmware is found on various cards, but
2721 * there has been no attempt to check firmware
2722 * download on non-spectrum_cs based cards.
2723 *
2724 * Given that the Agere firmware download works
2725 * differently, we should avoid doing a firmware
2726 * download with the Symbol algorithm on non-spectrum
2727 * cards.
2728 *
2729 * For now we can identify a spectrum_cs based card
2730 * because it has a firmware reset function.
2731 */
2732 priv->do_fw_download = (priv->stop_fw != NULL);
2733
David Kilroy6fe9deb2009-02-04 23:05:43 +00002734 priv->broken_disableport = (firmver == 0x25013) ||
David Kilroyb2f30a02009-02-04 23:05:46 +00002735 (firmver >= 0x30000 && firmver <= 0x31000);
Christoph Hellwig95dd91f2005-06-19 01:27:56 +02002736 priv->has_hostscan = (firmver >= 0x31001) ||
2737 (firmver >= 0x29057 && firmver < 0x30000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002738 /* Tested with Intel firmware : 0x20015 => Jean II */
2739 /* Tested with 3Com firmware : 0x15012 & 0x22001 => Jean II */
2740 break;
2741 case FIRMWARE_TYPE_INTERSIL:
2742 /* D-Link, Linksys, Adtron, ZoomAir, and many others...
2743 * Samsung, Compaq 100/200 and Proxim are slightly
2744 * different and less well tested */
2745 /* D-Link MAC : 00:40:05:* */
2746 /* Addtron MAC : 00:90:D1:* */
2747 snprintf(priv->fw_name, sizeof(priv->fw_name) - 1,
2748 "Intersil %d.%d.%d", sta_id.major, sta_id.minor,
2749 sta_id.variant);
2750
2751 firmver = ((unsigned long)sta_id.major << 16) |
2752 ((unsigned long)sta_id.minor << 8) | sta_id.variant;
2753
2754 priv->has_ibss = (firmver >= 0x000700); /* FIXME */
2755 priv->has_big_wep = priv->has_wep = (firmver >= 0x000800);
2756 priv->has_pm = (firmver >= 0x000700);
Christoph Hellwig95dd91f2005-06-19 01:27:56 +02002757 priv->has_hostscan = (firmver >= 0x010301);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002758
2759 if (firmver >= 0x000800)
2760 priv->ibss_port = 0;
2761 else {
2762 printk(KERN_NOTICE "%s: Intersil firmware earlier "
2763 "than v0.8.x - several features not supported\n",
2764 dev->name);
2765 priv->ibss_port = 1;
2766 }
2767 break;
2768 }
2769 printk(KERN_DEBUG "%s: Firmware determined as %s\n", dev->name,
2770 priv->fw_name);
2771
2772 return 0;
2773}
2774
2775static int orinoco_init(struct net_device *dev)
2776{
2777 struct orinoco_private *priv = netdev_priv(dev);
2778 hermes_t *hw = &priv->hw;
2779 int err = 0;
2780 struct hermes_idstring nickbuf;
2781 u16 reclen;
2782 int len;
2783
Linus Torvalds1da177e2005-04-16 15:20:36 -07002784 /* No need to lock, the hw_unavailable flag is already set in
2785 * alloc_orinocodev() */
Johannes Berg2c7060022008-10-30 22:09:54 +01002786 priv->nicbuf_size = IEEE80211_MAX_FRAME_LEN + ETH_HLEN;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002787
2788 /* Initialize the firmware */
Pavel Roskin37a6c612006-04-07 04:10:49 -04002789 err = hermes_init(hw);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002790 if (err != 0) {
2791 printk(KERN_ERR "%s: failed to initialize firmware (err = %d)\n",
2792 dev->name, err);
2793 goto out;
2794 }
2795
2796 err = determine_firmware(dev);
2797 if (err != 0) {
2798 printk(KERN_ERR "%s: Incompatible firmware, aborting\n",
2799 dev->name);
2800 goto out;
2801 }
2802
David Kilroy3994d502008-08-21 23:27:54 +01002803 if (priv->do_fw_download) {
David Kilroy39d1ffe2008-11-22 10:37:28 +00002804#ifdef CONFIG_HERMES_CACHE_FW_ON_INIT
David Kilroy74734312008-11-22 10:37:25 +00002805 orinoco_cache_fw(priv, 0);
David Kilroy39d1ffe2008-11-22 10:37:28 +00002806#endif
David Kilroy74734312008-11-22 10:37:25 +00002807
David Kilroy3994d502008-08-21 23:27:54 +01002808 err = orinoco_download(priv);
2809 if (err)
2810 priv->do_fw_download = 0;
2811
2812 /* Check firmware version again */
2813 err = determine_firmware(dev);
2814 if (err != 0) {
2815 printk(KERN_ERR "%s: Incompatible firmware, aborting\n",
2816 dev->name);
2817 goto out;
2818 }
2819 }
2820
Linus Torvalds1da177e2005-04-16 15:20:36 -07002821 if (priv->has_port3)
David Kilroyb2f30a02009-02-04 23:05:46 +00002822 printk(KERN_DEBUG "%s: Ad-hoc demo mode supported\n",
2823 dev->name);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002824 if (priv->has_ibss)
2825 printk(KERN_DEBUG "%s: IEEE standard IBSS ad-hoc mode supported\n",
2826 dev->name);
2827 if (priv->has_wep) {
David Kilroy21312662009-02-04 23:05:47 +00002828 printk(KERN_DEBUG "%s: WEP supported, %s-bit key\n", dev->name,
2829 priv->has_big_wep ? "104" : "40");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002830 }
David Kilroy23edcc42008-08-21 23:28:05 +01002831 if (priv->has_wpa) {
David Kilroyd03032a2008-08-21 23:28:02 +01002832 printk(KERN_DEBUG "%s: WPA-PSK supported\n", dev->name);
David Kilroy23edcc42008-08-21 23:28:05 +01002833 if (orinoco_mic_init(priv)) {
2834 printk(KERN_ERR "%s: Failed to setup MIC crypto "
2835 "algorithm. Disabling WPA support\n", dev->name);
2836 priv->has_wpa = 0;
2837 }
2838 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002839
David Kilroy01632fa2008-08-21 23:27:58 +01002840 /* Now we have the firmware capabilities, allocate appropiate
2841 * sized scan buffers */
2842 if (orinoco_bss_data_allocate(priv))
2843 goto out;
2844 orinoco_bss_data_init(priv);
2845
Linus Torvalds1da177e2005-04-16 15:20:36 -07002846 /* Get the MAC address */
2847 err = hermes_read_ltv(hw, USER_BAP, HERMES_RID_CNFOWNMACADDR,
2848 ETH_ALEN, NULL, dev->dev_addr);
2849 if (err) {
2850 printk(KERN_WARNING "%s: failed to read MAC address!\n",
2851 dev->name);
2852 goto out;
2853 }
2854
Johannes Berge1749612008-10-27 15:59:26 -07002855 printk(KERN_DEBUG "%s: MAC address %pM\n",
2856 dev->name, dev->dev_addr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002857
2858 /* Get the station name */
2859 err = hermes_read_ltv(hw, USER_BAP, HERMES_RID_CNFOWNNAME,
2860 sizeof(nickbuf), &reclen, &nickbuf);
2861 if (err) {
2862 printk(KERN_ERR "%s: failed to read station name\n",
2863 dev->name);
2864 goto out;
2865 }
2866 if (nickbuf.len)
2867 len = min(IW_ESSID_MAX_SIZE, (int)le16_to_cpu(nickbuf.len));
2868 else
2869 len = min(IW_ESSID_MAX_SIZE, 2 * reclen);
2870 memcpy(priv->nick, &nickbuf.val, len);
2871 priv->nick[len] = '\0';
2872
2873 printk(KERN_DEBUG "%s: Station name \"%s\"\n", dev->name, priv->nick);
2874
Pavel Roskin37a6c612006-04-07 04:10:49 -04002875 err = orinoco_allocate_fid(dev);
2876 if (err) {
2877 printk(KERN_ERR "%s: failed to allocate NIC buffer!\n",
2878 dev->name);
2879 goto out;
2880 }
2881
Linus Torvalds1da177e2005-04-16 15:20:36 -07002882 /* Get allowed channels */
2883 err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_CHANNELLIST,
2884 &priv->channel_mask);
2885 if (err) {
2886 printk(KERN_ERR "%s: failed to read channel list!\n",
2887 dev->name);
2888 goto out;
2889 }
2890
2891 /* Get initial AP density */
2892 err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_CNFSYSTEMSCALE,
2893 &priv->ap_density);
David Kilroy566f2d92009-02-04 23:05:45 +00002894 if (err || priv->ap_density < 1 || priv->ap_density > 3)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002895 priv->has_sensitivity = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002896
2897 /* Get initial RTS threshold */
2898 err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_CNFRTSTHRESHOLD,
2899 &priv->rts_thresh);
2900 if (err) {
2901 printk(KERN_ERR "%s: failed to read RTS threshold!\n",
2902 dev->name);
2903 goto out;
2904 }
2905
2906 /* Get initial fragmentation settings */
2907 if (priv->has_mwo)
2908 err = hermes_read_wordrec(hw, USER_BAP,
2909 HERMES_RID_CNFMWOROBUST_AGERE,
2910 &priv->mwo_robust);
2911 else
David Kilroyb2f30a02009-02-04 23:05:46 +00002912 err = hermes_read_wordrec(hw, USER_BAP,
2913 HERMES_RID_CNFFRAGMENTATIONTHRESHOLD,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002914 &priv->frag_thresh);
2915 if (err) {
2916 printk(KERN_ERR "%s: failed to read fragmentation settings!\n",
2917 dev->name);
2918 goto out;
2919 }
2920
2921 /* Power management setup */
2922 if (priv->has_pm) {
2923 priv->pm_on = 0;
2924 priv->pm_mcast = 1;
2925 err = hermes_read_wordrec(hw, USER_BAP,
2926 HERMES_RID_CNFMAXSLEEPDURATION,
2927 &priv->pm_period);
2928 if (err) {
2929 printk(KERN_ERR "%s: failed to read power management period!\n",
2930 dev->name);
2931 goto out;
2932 }
2933 err = hermes_read_wordrec(hw, USER_BAP,
2934 HERMES_RID_CNFPMHOLDOVERDURATION,
2935 &priv->pm_timeout);
2936 if (err) {
2937 printk(KERN_ERR "%s: failed to read power management timeout!\n",
2938 dev->name);
2939 goto out;
2940 }
2941 }
2942
2943 /* Preamble setup */
2944 if (priv->has_preamble) {
2945 err = hermes_read_wordrec(hw, USER_BAP,
2946 HERMES_RID_CNFPREAMBLE_SYMBOL,
2947 &priv->preamble);
2948 if (err)
2949 goto out;
2950 }
David Kilroy6fe9deb2009-02-04 23:05:43 +00002951
Linus Torvalds1da177e2005-04-16 15:20:36 -07002952 /* Set up the default configuration */
2953 priv->iw_mode = IW_MODE_INFRA;
2954 /* By default use IEEE/IBSS ad-hoc mode if we have it */
David Kilroya94e8422009-02-04 23:05:44 +00002955 priv->prefer_port3 = priv->has_port3 && (!priv->has_ibss);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002956 set_port_type(priv);
David Gibsond51d8b12005-05-12 20:03:36 -04002957 priv->channel = 0; /* use firmware default */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002958
2959 priv->promiscuous = 0;
David Kilroy4ae6ee22008-08-21 23:27:59 +01002960 priv->encode_alg = IW_ENCODE_ALG_NONE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002961 priv->tx_key = 0;
David Kilroyd03032a2008-08-21 23:28:02 +01002962 priv->wpa_enabled = 0;
2963 priv->tkip_cm_active = 0;
2964 priv->key_mgmt = 0;
2965 priv->wpa_ie_len = 0;
2966 priv->wpa_ie = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002967
Linus Torvalds1da177e2005-04-16 15:20:36 -07002968 /* Make the hardware available, as long as it hasn't been
2969 * removed elsewhere (e.g. by PCMCIA hot unplug) */
2970 spin_lock_irq(&priv->lock);
2971 priv->hw_unavailable--;
2972 spin_unlock_irq(&priv->lock);
2973
2974 printk(KERN_DEBUG "%s: ready\n", dev->name);
2975
2976 out:
Linus Torvalds1da177e2005-04-16 15:20:36 -07002977 return err;
2978}
2979
Andrey Borzenkov89ea4092009-01-21 20:46:46 +03002980static const struct net_device_ops orinoco_netdev_ops = {
2981 .ndo_init = orinoco_init,
2982 .ndo_open = orinoco_open,
2983 .ndo_stop = orinoco_stop,
2984 .ndo_start_xmit = orinoco_xmit,
2985 .ndo_set_multicast_list = orinoco_set_multicast_list,
2986 .ndo_change_mtu = orinoco_change_mtu,
2987 .ndo_tx_timeout = orinoco_tx_timeout,
2988 .ndo_get_stats = orinoco_get_stats,
2989};
2990
David Kilroy3994d502008-08-21 23:27:54 +01002991struct net_device
2992*alloc_orinocodev(int sizeof_card,
2993 struct device *device,
2994 int (*hard_reset)(struct orinoco_private *),
2995 int (*stop_fw)(struct orinoco_private *, int))
Linus Torvalds1da177e2005-04-16 15:20:36 -07002996{
2997 struct net_device *dev;
2998 struct orinoco_private *priv;
2999
3000 dev = alloc_etherdev(sizeof(struct orinoco_private) + sizeof_card);
Andrey Borzenkove129a942009-01-21 21:55:29 +03003001 if (!dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003002 return NULL;
3003 priv = netdev_priv(dev);
3004 priv->ndev = dev;
3005 if (sizeof_card)
Christoph Hellwig84d8a2f2005-05-14 17:30:22 +02003006 priv->card = (void *)((unsigned long)priv
Linus Torvalds1da177e2005-04-16 15:20:36 -07003007 + sizeof(struct orinoco_private));
3008 else
3009 priv->card = NULL;
David Kilroy3994d502008-08-21 23:27:54 +01003010 priv->dev = device;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003011
3012 /* Setup / override net_device fields */
Andrey Borzenkov89ea4092009-01-21 20:46:46 +03003013 dev->netdev_ops = &orinoco_netdev_ops;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003014 dev->watchdog_timeo = HZ; /* 1 second timeout */
Christoph Hellwig1fab2e82005-06-19 01:27:40 +02003015 dev->ethtool_ops = &orinoco_ethtool_ops;
Andrey Borzenkove129a942009-01-21 21:55:29 +03003016 dev->wireless_handlers = &orinoco_handler_def;
Pavel Roskin343c6862005-09-09 18:43:02 -04003017#ifdef WIRELESS_SPY
3018 priv->wireless_data.spy_data = &priv->spy_data;
3019 dev->wireless_data = &priv->wireless_data;
3020#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -07003021 /* we use the default eth_mac_addr for setting the MAC addr */
3022
David Kilroy23edcc42008-08-21 23:28:05 +01003023 /* Reserve space in skb for the SNAP header */
3024 dev->hard_header_len += ENCAPS_OVERHEAD;
3025
Linus Torvalds1da177e2005-04-16 15:20:36 -07003026 /* Set up default callbacks */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003027 priv->hard_reset = hard_reset;
David Kilroy3994d502008-08-21 23:27:54 +01003028 priv->stop_fw = stop_fw;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003029
3030 spin_lock_init(&priv->lock);
3031 priv->open = 0;
3032 priv->hw_unavailable = 1; /* orinoco_init() must clear this
3033 * before anything else touches the
3034 * hardware */
David Howellsc4028952006-11-22 14:57:56 +00003035 INIT_WORK(&priv->reset_work, orinoco_reset);
3036 INIT_WORK(&priv->join_work, orinoco_join_ap);
3037 INIT_WORK(&priv->wevent_work, orinoco_send_wevents);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003038
David Kilroy31afcef2008-08-21 23:28:04 +01003039 INIT_LIST_HEAD(&priv->rx_list);
3040 tasklet_init(&priv->rx_tasklet, orinoco_rx_isr_tasklet,
3041 (unsigned long) dev);
3042
Linus Torvalds1da177e2005-04-16 15:20:36 -07003043 netif_carrier_off(dev);
3044 priv->last_linkstatus = 0xffff;
3045
David Kilroy2cea7b22008-11-22 10:37:26 +00003046 priv->cached_pri_fw = NULL;
Andrey Borzenkov4fb30782008-10-19 12:06:11 +04003047 priv->cached_fw = NULL;
3048
David Kilroy39d1ffe2008-11-22 10:37:28 +00003049 /* Register PM notifiers */
3050 priv->pm_notifier.notifier_call = orinoco_pm_notifier;
3051 register_pm_notifier(&priv->pm_notifier);
3052
Linus Torvalds1da177e2005-04-16 15:20:36 -07003053 return dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003054}
David Kilroy21312662009-02-04 23:05:47 +00003055EXPORT_SYMBOL(alloc_orinocodev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003056
3057void free_orinocodev(struct net_device *dev)
3058{
Christoph Hellwig95dd91f2005-06-19 01:27:56 +02003059 struct orinoco_private *priv = netdev_priv(dev);
David Kilroy20953ad2009-01-07 00:23:55 +00003060 struct orinoco_rx_data *rx_data, *temp;
Christoph Hellwig95dd91f2005-06-19 01:27:56 +02003061
David Kilroy20953ad2009-01-07 00:23:55 +00003062 /* If the tasklet is scheduled when we call tasklet_kill it
3063 * will run one final time. However the tasklet will only
3064 * drain priv->rx_list if the hw is still available. */
David Kilroy31afcef2008-08-21 23:28:04 +01003065 tasklet_kill(&priv->rx_tasklet);
David Kilroy74734312008-11-22 10:37:25 +00003066
David Kilroy20953ad2009-01-07 00:23:55 +00003067 /* Explicitly drain priv->rx_list */
3068 list_for_each_entry_safe(rx_data, temp, &priv->rx_list, list) {
3069 list_del(&rx_data->list);
3070
3071 dev_kfree_skb(rx_data->skb);
3072 kfree(rx_data->desc);
3073 kfree(rx_data);
3074 }
3075
David Kilroy39d1ffe2008-11-22 10:37:28 +00003076 unregister_pm_notifier(&priv->pm_notifier);
David Kilroy74734312008-11-22 10:37:25 +00003077 orinoco_uncache_fw(priv);
3078
David Kilroyd03032a2008-08-21 23:28:02 +01003079 priv->wpa_ie_len = 0;
3080 kfree(priv->wpa_ie);
David Kilroy23edcc42008-08-21 23:28:05 +01003081 orinoco_mic_free(priv);
Dan Williams1e3428e2007-10-10 23:56:25 -04003082 orinoco_bss_data_free(priv);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003083 free_netdev(dev);
3084}
David Kilroy21312662009-02-04 23:05:47 +00003085EXPORT_SYMBOL(free_orinocodev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003086
3087/********************************************************************/
3088/* Wireless extensions */
3089/********************************************************************/
3090
Jean Tourrilhes7e4e8d92006-10-10 14:45:44 -07003091/* Return : < 0 -> error code ; >= 0 -> length */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003092static int orinoco_hw_get_essid(struct orinoco_private *priv, int *active,
3093 char buf[IW_ESSID_MAX_SIZE+1])
3094{
3095 hermes_t *hw = &priv->hw;
3096 int err = 0;
3097 struct hermes_idstring essidbuf;
3098 char *p = (char *)(&essidbuf.val);
3099 int len;
3100 unsigned long flags;
3101
3102 if (orinoco_lock(priv, &flags) != 0)
3103 return -EBUSY;
3104
3105 if (strlen(priv->desired_essid) > 0) {
3106 /* We read the desired SSID from the hardware rather
3107 than from priv->desired_essid, just in case the
3108 firmware is allowed to change it on us. I'm not
3109 sure about this */
3110 /* My guess is that the OWNSSID should always be whatever
3111 * we set to the card, whereas CURRENT_SSID is the one that
3112 * may change... - Jean II */
3113 u16 rid;
3114
3115 *active = 1;
3116
3117 rid = (priv->port_type == 3) ? HERMES_RID_CNFOWNSSID :
3118 HERMES_RID_CNFDESIREDSSID;
David Kilroy6fe9deb2009-02-04 23:05:43 +00003119
Linus Torvalds1da177e2005-04-16 15:20:36 -07003120 err = hermes_read_ltv(hw, USER_BAP, rid, sizeof(essidbuf),
3121 NULL, &essidbuf);
3122 if (err)
3123 goto fail_unlock;
3124 } else {
3125 *active = 0;
3126
3127 err = hermes_read_ltv(hw, USER_BAP, HERMES_RID_CURRENTSSID,
3128 sizeof(essidbuf), NULL, &essidbuf);
3129 if (err)
3130 goto fail_unlock;
3131 }
3132
3133 len = le16_to_cpu(essidbuf.len);
Christoph Hellwig84d8a2f2005-05-14 17:30:22 +02003134 BUG_ON(len > IW_ESSID_MAX_SIZE);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003135
Jean Tourrilhes7e4e8d92006-10-10 14:45:44 -07003136 memset(buf, 0, IW_ESSID_MAX_SIZE);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003137 memcpy(buf, p, len);
Jean Tourrilhes7e4e8d92006-10-10 14:45:44 -07003138 err = len;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003139
3140 fail_unlock:
3141 orinoco_unlock(priv, &flags);
3142
David Kilroy6fe9deb2009-02-04 23:05:43 +00003143 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003144}
3145
David Kilroy9ee677c2008-12-23 14:03:38 +00003146static int orinoco_hw_get_freq(struct orinoco_private *priv)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003147{
David Kilroy6fe9deb2009-02-04 23:05:43 +00003148
Linus Torvalds1da177e2005-04-16 15:20:36 -07003149 hermes_t *hw = &priv->hw;
3150 int err = 0;
3151 u16 channel;
David Kilroy9ee677c2008-12-23 14:03:38 +00003152 int freq = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003153 unsigned long flags;
3154
3155 if (orinoco_lock(priv, &flags) != 0)
3156 return -EBUSY;
David Kilroy6fe9deb2009-02-04 23:05:43 +00003157
David Kilroyb2f30a02009-02-04 23:05:46 +00003158 err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_CURRENTCHANNEL,
3159 &channel);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003160 if (err)
3161 goto out;
3162
3163 /* Intersil firmware 1.3.5 returns 0 when the interface is down */
3164 if (channel == 0) {
3165 err = -EBUSY;
3166 goto out;
3167 }
3168
David Kilroya94e8422009-02-04 23:05:44 +00003169 if ((channel < 1) || (channel > NUM_CHANNELS)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07003170 printk(KERN_WARNING "%s: Channel out of range (%d)!\n",
3171 priv->ndev->name, channel);
3172 err = -EBUSY;
3173 goto out;
3174
3175 }
David Kilroy9ee677c2008-12-23 14:03:38 +00003176 freq = ieee80211_dsss_chan_to_freq(channel);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003177
3178 out:
3179 orinoco_unlock(priv, &flags);
3180
3181 if (err > 0)
3182 err = -EBUSY;
3183 return err ? err : freq;
3184}
3185
3186static int orinoco_hw_get_bitratelist(struct orinoco_private *priv,
3187 int *numrates, s32 *rates, int max)
3188{
3189 hermes_t *hw = &priv->hw;
3190 struct hermes_idstring list;
3191 unsigned char *p = (unsigned char *)&list.val;
3192 int err = 0;
3193 int num;
3194 int i;
3195 unsigned long flags;
3196
3197 if (orinoco_lock(priv, &flags) != 0)
3198 return -EBUSY;
3199
3200 err = hermes_read_ltv(hw, USER_BAP, HERMES_RID_SUPPORTEDDATARATES,
3201 sizeof(list), NULL, &list);
3202 orinoco_unlock(priv, &flags);
3203
3204 if (err)
3205 return err;
David Kilroy6fe9deb2009-02-04 23:05:43 +00003206
Linus Torvalds1da177e2005-04-16 15:20:36 -07003207 num = le16_to_cpu(list.len);
3208 *numrates = num;
3209 num = min(num, max);
3210
David Kilroy566f2d92009-02-04 23:05:45 +00003211 for (i = 0; i < num; i++)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003212 rates[i] = (p[i] & 0x7f) * 500000; /* convert to bps */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003213
3214 return 0;
3215}
3216
Christoph Hellwig620554e2005-06-19 01:27:33 +02003217static int orinoco_ioctl_getname(struct net_device *dev,
3218 struct iw_request_info *info,
3219 char *name,
3220 char *extra)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003221{
3222 struct orinoco_private *priv = netdev_priv(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003223 int numrates;
Christoph Hellwig620554e2005-06-19 01:27:33 +02003224 int err;
3225
3226 err = orinoco_hw_get_bitratelist(priv, &numrates, NULL, 0);
3227
3228 if (!err && (numrates > 2))
3229 strcpy(name, "IEEE 802.11b");
3230 else
3231 strcpy(name, "IEEE 802.11-DS");
3232
3233 return 0;
3234}
3235
Christoph Hellwig16739b02005-06-19 01:27:51 +02003236static int orinoco_ioctl_setwap(struct net_device *dev,
3237 struct iw_request_info *info,
3238 struct sockaddr *ap_addr,
3239 char *extra)
3240{
3241 struct orinoco_private *priv = netdev_priv(dev);
3242 int err = -EINPROGRESS; /* Call commit handler */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003243 unsigned long flags;
Christoph Hellwig16739b02005-06-19 01:27:51 +02003244 static const u8 off_addr[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
3245 static const u8 any_addr[] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
Linus Torvalds1da177e2005-04-16 15:20:36 -07003246
3247 if (orinoco_lock(priv, &flags) != 0)
3248 return -EBUSY;
3249
Christoph Hellwig16739b02005-06-19 01:27:51 +02003250 /* Enable automatic roaming - no sanity checks are needed */
3251 if (memcmp(&ap_addr->sa_data, off_addr, ETH_ALEN) == 0 ||
3252 memcmp(&ap_addr->sa_data, any_addr, ETH_ALEN) == 0) {
3253 priv->bssid_fixed = 0;
3254 memset(priv->desired_bssid, 0, ETH_ALEN);
3255
3256 /* "off" means keep existing connection */
3257 if (ap_addr->sa_data[0] == 0) {
3258 __orinoco_hw_set_wap(priv);
3259 err = 0;
3260 }
3261 goto out;
3262 }
3263
3264 if (priv->firmware_type == FIRMWARE_TYPE_AGERE) {
3265 printk(KERN_WARNING "%s: Lucent/Agere firmware doesn't "
3266 "support manual roaming\n",
3267 dev->name);
3268 err = -EOPNOTSUPP;
3269 goto out;
3270 }
3271
3272 if (priv->iw_mode != IW_MODE_INFRA) {
3273 printk(KERN_WARNING "%s: Manual roaming supported only in "
3274 "managed mode\n", dev->name);
3275 err = -EOPNOTSUPP;
3276 goto out;
3277 }
3278
3279 /* Intersil firmware hangs without Desired ESSID */
3280 if (priv->firmware_type == FIRMWARE_TYPE_INTERSIL &&
3281 strlen(priv->desired_essid) == 0) {
3282 printk(KERN_WARNING "%s: Desired ESSID must be set for "
3283 "manual roaming\n", dev->name);
3284 err = -EOPNOTSUPP;
3285 goto out;
3286 }
3287
3288 /* Finally, enable manual roaming */
3289 priv->bssid_fixed = 1;
3290 memcpy(priv->desired_bssid, &ap_addr->sa_data, ETH_ALEN);
3291
3292 out:
3293 orinoco_unlock(priv, &flags);
3294 return err;
3295}
3296
Christoph Hellwig620554e2005-06-19 01:27:33 +02003297static int orinoco_ioctl_getwap(struct net_device *dev,
3298 struct iw_request_info *info,
3299 struct sockaddr *ap_addr,
3300 char *extra)
3301{
3302 struct orinoco_private *priv = netdev_priv(dev);
3303
3304 hermes_t *hw = &priv->hw;
3305 int err = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003306 unsigned long flags;
3307
Linus Torvalds1da177e2005-04-16 15:20:36 -07003308 if (orinoco_lock(priv, &flags) != 0)
3309 return -EBUSY;
3310
Christoph Hellwig620554e2005-06-19 01:27:33 +02003311 ap_addr->sa_family = ARPHRD_ETHER;
3312 err = hermes_read_ltv(hw, USER_BAP, HERMES_RID_CURRENTBSSID,
3313 ETH_ALEN, NULL, ap_addr->sa_data);
3314
Linus Torvalds1da177e2005-04-16 15:20:36 -07003315 orinoco_unlock(priv, &flags);
3316
Christoph Hellwig620554e2005-06-19 01:27:33 +02003317 return err;
3318}
Linus Torvalds1da177e2005-04-16 15:20:36 -07003319
Christoph Hellwig620554e2005-06-19 01:27:33 +02003320static int orinoco_ioctl_setmode(struct net_device *dev,
3321 struct iw_request_info *info,
3322 u32 *mode,
3323 char *extra)
3324{
3325 struct orinoco_private *priv = netdev_priv(dev);
3326 int err = -EINPROGRESS; /* Call commit handler */
3327 unsigned long flags;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003328
Christoph Hellwig620554e2005-06-19 01:27:33 +02003329 if (priv->iw_mode == *mode)
3330 return 0;
3331
3332 if (orinoco_lock(priv, &flags) != 0)
3333 return -EBUSY;
3334
3335 switch (*mode) {
3336 case IW_MODE_ADHOC:
3337 if (!priv->has_ibss && !priv->has_port3)
3338 err = -EOPNOTSUPP;
3339 break;
3340
3341 case IW_MODE_INFRA:
3342 break;
3343
Christoph Hellwig98c4cae2005-06-19 01:28:06 +02003344 case IW_MODE_MONITOR:
3345 if (priv->broken_monitor && !force_monitor) {
3346 printk(KERN_WARNING "%s: Monitor mode support is "
3347 "buggy in this firmware, not enabling\n",
3348 dev->name);
3349 err = -EOPNOTSUPP;
3350 }
3351 break;
3352
Christoph Hellwig620554e2005-06-19 01:27:33 +02003353 default:
3354 err = -EOPNOTSUPP;
3355 break;
3356 }
3357
3358 if (err == -EINPROGRESS) {
3359 priv->iw_mode = *mode;
3360 set_port_type(priv);
3361 }
3362
3363 orinoco_unlock(priv, &flags);
3364
3365 return err;
3366}
3367
3368static int orinoco_ioctl_getmode(struct net_device *dev,
3369 struct iw_request_info *info,
3370 u32 *mode,
3371 char *extra)
3372{
3373 struct orinoco_private *priv = netdev_priv(dev);
3374
3375 *mode = priv->iw_mode;
3376 return 0;
3377}
3378
3379static int orinoco_ioctl_getiwrange(struct net_device *dev,
3380 struct iw_request_info *info,
3381 struct iw_point *rrq,
3382 char *extra)
3383{
3384 struct orinoco_private *priv = netdev_priv(dev);
3385 int err = 0;
3386 struct iw_range *range = (struct iw_range *) extra;
3387 int numrates;
3388 int i, k;
3389
Christoph Hellwig620554e2005-06-19 01:27:33 +02003390 rrq->length = sizeof(struct iw_range);
3391 memset(range, 0, sizeof(struct iw_range));
3392
3393 range->we_version_compiled = WIRELESS_EXT;
David Kilroyd03032a2008-08-21 23:28:02 +01003394 range->we_version_source = 22;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003395
3396 /* Set available channels/frequencies */
Christoph Hellwig620554e2005-06-19 01:27:33 +02003397 range->num_channels = NUM_CHANNELS;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003398 k = 0;
3399 for (i = 0; i < NUM_CHANNELS; i++) {
3400 if (priv->channel_mask & (1 << i)) {
Christoph Hellwig620554e2005-06-19 01:27:33 +02003401 range->freq[k].i = i + 1;
David Kilroy9ee677c2008-12-23 14:03:38 +00003402 range->freq[k].m = (ieee80211_dsss_chan_to_freq(i + 1) *
3403 100000);
Christoph Hellwig620554e2005-06-19 01:27:33 +02003404 range->freq[k].e = 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003405 k++;
3406 }
David Kilroy6fe9deb2009-02-04 23:05:43 +00003407
Linus Torvalds1da177e2005-04-16 15:20:36 -07003408 if (k >= IW_MAX_FREQUENCIES)
3409 break;
3410 }
Christoph Hellwig620554e2005-06-19 01:27:33 +02003411 range->num_frequency = k;
3412 range->sensitivity = 3;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003413
Christoph Hellwig620554e2005-06-19 01:27:33 +02003414 if (priv->has_wep) {
3415 range->max_encoding_tokens = ORINOCO_MAX_KEYS;
3416 range->encoding_size[0] = SMALL_KEY_SIZE;
3417 range->num_encoding_sizes = 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003418
Christoph Hellwig620554e2005-06-19 01:27:33 +02003419 if (priv->has_big_wep) {
3420 range->encoding_size[1] = LARGE_KEY_SIZE;
3421 range->num_encoding_sizes = 2;
3422 }
3423 }
3424
David Kilroyd03032a2008-08-21 23:28:02 +01003425 if (priv->has_wpa)
3426 range->enc_capa = IW_ENC_CAPA_WPA | IW_ENC_CAPA_CIPHER_TKIP;
3427
David Kilroya94e8422009-02-04 23:05:44 +00003428 if ((priv->iw_mode == IW_MODE_ADHOC) && (!SPY_NUMBER(priv))) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07003429 /* Quality stats meaningless in ad-hoc mode */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003430 } else {
Christoph Hellwig620554e2005-06-19 01:27:33 +02003431 range->max_qual.qual = 0x8b - 0x2f;
3432 range->max_qual.level = 0x2f - 0x95 - 1;
3433 range->max_qual.noise = 0x2f - 0x95 - 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003434 /* Need to get better values */
Christoph Hellwig620554e2005-06-19 01:27:33 +02003435 range->avg_qual.qual = 0x24;
3436 range->avg_qual.level = 0xC2;
3437 range->avg_qual.noise = 0x9E;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003438 }
3439
3440 err = orinoco_hw_get_bitratelist(priv, &numrates,
Christoph Hellwig620554e2005-06-19 01:27:33 +02003441 range->bitrate, IW_MAX_BITRATES);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003442 if (err)
3443 return err;
Christoph Hellwig620554e2005-06-19 01:27:33 +02003444 range->num_bitrates = numrates;
3445
Linus Torvalds1da177e2005-04-16 15:20:36 -07003446 /* Set an indication of the max TCP throughput in bit/s that we can
3447 * expect using this interface. May be use for QoS stuff...
3448 * Jean II */
Christoph Hellwig620554e2005-06-19 01:27:33 +02003449 if (numrates > 2)
3450 range->throughput = 5 * 1000 * 1000; /* ~5 Mb/s */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003451 else
Christoph Hellwig620554e2005-06-19 01:27:33 +02003452 range->throughput = 1.5 * 1000 * 1000; /* ~1.5 Mb/s */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003453
Christoph Hellwig620554e2005-06-19 01:27:33 +02003454 range->min_rts = 0;
3455 range->max_rts = 2347;
3456 range->min_frag = 256;
3457 range->max_frag = 2346;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003458
Christoph Hellwig620554e2005-06-19 01:27:33 +02003459 range->min_pmp = 0;
3460 range->max_pmp = 65535000;
3461 range->min_pmt = 0;
3462 range->max_pmt = 65535 * 1000; /* ??? */
3463 range->pmp_flags = IW_POWER_PERIOD;
3464 range->pmt_flags = IW_POWER_TIMEOUT;
David Kilroyb2f30a02009-02-04 23:05:46 +00003465 range->pm_capa = (IW_POWER_PERIOD | IW_POWER_TIMEOUT |
3466 IW_POWER_UNICAST_R);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003467
Christoph Hellwig620554e2005-06-19 01:27:33 +02003468 range->retry_capa = IW_RETRY_LIMIT | IW_RETRY_LIFETIME;
3469 range->retry_flags = IW_RETRY_LIMIT;
3470 range->r_time_flags = IW_RETRY_LIFETIME;
3471 range->min_retry = 0;
3472 range->max_retry = 65535; /* ??? */
3473 range->min_r_time = 0;
3474 range->max_r_time = 65535 * 1000; /* ??? */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003475
David Kilroy0753bba2008-08-21 23:27:46 +01003476 if (priv->firmware_type == FIRMWARE_TYPE_AGERE)
3477 range->scan_capa = IW_SCAN_CAPA_ESSID;
3478 else
3479 range->scan_capa = IW_SCAN_CAPA_NONE;
3480
Pavel Roskin343c6862005-09-09 18:43:02 -04003481 /* Event capability (kernel) */
3482 IW_EVENT_CAPA_SET_KERNEL(range->event_capa);
3483 /* Event capability (driver) */
3484 IW_EVENT_CAPA_SET(range->event_capa, SIOCGIWTHRSPY);
3485 IW_EVENT_CAPA_SET(range->event_capa, SIOCGIWAP);
3486 IW_EVENT_CAPA_SET(range->event_capa, SIOCGIWSCAN);
3487 IW_EVENT_CAPA_SET(range->event_capa, IWEVTXDROP);
3488
Linus Torvalds1da177e2005-04-16 15:20:36 -07003489 return 0;
3490}
3491
Christoph Hellwig620554e2005-06-19 01:27:33 +02003492static int orinoco_ioctl_setiwencode(struct net_device *dev,
3493 struct iw_request_info *info,
3494 struct iw_point *erq,
3495 char *keybuf)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003496{
3497 struct orinoco_private *priv = netdev_priv(dev);
3498 int index = (erq->flags & IW_ENCODE_INDEX) - 1;
3499 int setindex = priv->tx_key;
David Kilroy4ae6ee22008-08-21 23:27:59 +01003500 int encode_alg = priv->encode_alg;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003501 int restricted = priv->wep_restrict;
3502 u16 xlen = 0;
Christoph Hellwig620554e2005-06-19 01:27:33 +02003503 int err = -EINPROGRESS; /* Call commit handler */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003504 unsigned long flags;
3505
David Kilroya94e8422009-02-04 23:05:44 +00003506 if (!priv->has_wep)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003507 return -EOPNOTSUPP;
3508
3509 if (erq->pointer) {
3510 /* We actually have a key to set - check its length */
3511 if (erq->length > LARGE_KEY_SIZE)
3512 return -E2BIG;
3513
David Kilroya94e8422009-02-04 23:05:44 +00003514 if ((erq->length > SMALL_KEY_SIZE) && !priv->has_big_wep)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003515 return -E2BIG;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003516 }
3517
3518 if (orinoco_lock(priv, &flags) != 0)
3519 return -EBUSY;
3520
David Kilroyd03032a2008-08-21 23:28:02 +01003521 /* Clear any TKIP key we have */
3522 if ((priv->has_wpa) && (priv->encode_alg == IW_ENCODE_ALG_TKIP))
3523 (void) orinoco_clear_tkip_key(priv, setindex);
3524
Dan Williamsfe397d42006-07-14 11:41:47 -04003525 if (erq->length > 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07003526 if ((index < 0) || (index >= ORINOCO_MAX_KEYS))
3527 index = priv->tx_key;
3528
3529 /* Adjust key length to a supported value */
David Kilroy566f2d92009-02-04 23:05:45 +00003530 if (erq->length > SMALL_KEY_SIZE)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003531 xlen = LARGE_KEY_SIZE;
David Kilroy566f2d92009-02-04 23:05:45 +00003532 else if (erq->length > 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003533 xlen = SMALL_KEY_SIZE;
David Kilroy566f2d92009-02-04 23:05:45 +00003534 else
Linus Torvalds1da177e2005-04-16 15:20:36 -07003535 xlen = 0;
3536
3537 /* Switch on WEP if off */
David Kilroy4ae6ee22008-08-21 23:27:59 +01003538 if ((encode_alg != IW_ENCODE_ALG_WEP) && (xlen > 0)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07003539 setindex = index;
David Kilroy4ae6ee22008-08-21 23:27:59 +01003540 encode_alg = IW_ENCODE_ALG_WEP;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003541 }
3542 } else {
3543 /* Important note : if the user do "iwconfig eth0 enc off",
3544 * we will arrive there with an index of -1. This is valid
3545 * but need to be taken care off... Jean II */
3546 if ((index < 0) || (index >= ORINOCO_MAX_KEYS)) {
David Kilroya94e8422009-02-04 23:05:44 +00003547 if ((index != -1) || (erq->flags == 0)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07003548 err = -EINVAL;
3549 goto out;
3550 }
3551 } else {
3552 /* Set the index : Check that the key is valid */
David Kilroya94e8422009-02-04 23:05:44 +00003553 if (priv->keys[index].len == 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07003554 err = -EINVAL;
3555 goto out;
3556 }
3557 setindex = index;
3558 }
3559 }
3560
3561 if (erq->flags & IW_ENCODE_DISABLED)
David Kilroy4ae6ee22008-08-21 23:27:59 +01003562 encode_alg = IW_ENCODE_ALG_NONE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003563 if (erq->flags & IW_ENCODE_OPEN)
3564 restricted = 0;
3565 if (erq->flags & IW_ENCODE_RESTRICTED)
3566 restricted = 1;
3567
Dan Williamsfe397d42006-07-14 11:41:47 -04003568 if (erq->pointer && erq->length > 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07003569 priv->keys[index].len = cpu_to_le16(xlen);
3570 memset(priv->keys[index].data, 0,
3571 sizeof(priv->keys[index].data));
3572 memcpy(priv->keys[index].data, keybuf, erq->length);
3573 }
3574 priv->tx_key = setindex;
3575
3576 /* Try fast key change if connected and only keys are changed */
David Kilroy4ae6ee22008-08-21 23:27:59 +01003577 if ((priv->encode_alg == encode_alg) &&
3578 (priv->wep_restrict == restricted) &&
Linus Torvalds1da177e2005-04-16 15:20:36 -07003579 netif_carrier_ok(dev)) {
3580 err = __orinoco_hw_setup_wepkeys(priv);
3581 /* No need to commit if successful */
3582 goto out;
3583 }
3584
David Kilroy4ae6ee22008-08-21 23:27:59 +01003585 priv->encode_alg = encode_alg;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003586 priv->wep_restrict = restricted;
3587
3588 out:
3589 orinoco_unlock(priv, &flags);
3590
3591 return err;
3592}
3593
Christoph Hellwig620554e2005-06-19 01:27:33 +02003594static int orinoco_ioctl_getiwencode(struct net_device *dev,
3595 struct iw_request_info *info,
3596 struct iw_point *erq,
3597 char *keybuf)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003598{
3599 struct orinoco_private *priv = netdev_priv(dev);
3600 int index = (erq->flags & IW_ENCODE_INDEX) - 1;
3601 u16 xlen = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003602 unsigned long flags;
3603
David Kilroya94e8422009-02-04 23:05:44 +00003604 if (!priv->has_wep)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003605 return -EOPNOTSUPP;
3606
3607 if (orinoco_lock(priv, &flags) != 0)
3608 return -EBUSY;
3609
3610 if ((index < 0) || (index >= ORINOCO_MAX_KEYS))
3611 index = priv->tx_key;
3612
3613 erq->flags = 0;
David Kilroy4ae6ee22008-08-21 23:27:59 +01003614 if (!priv->encode_alg)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003615 erq->flags |= IW_ENCODE_DISABLED;
3616 erq->flags |= index + 1;
3617
3618 if (priv->wep_restrict)
3619 erq->flags |= IW_ENCODE_RESTRICTED;
3620 else
3621 erq->flags |= IW_ENCODE_OPEN;
3622
3623 xlen = le16_to_cpu(priv->keys[index].len);
3624
3625 erq->length = xlen;
3626
3627 memcpy(keybuf, priv->keys[index].data, ORINOCO_MAX_KEY_SIZE);
3628
3629 orinoco_unlock(priv, &flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003630 return 0;
3631}
3632
Christoph Hellwig620554e2005-06-19 01:27:33 +02003633static int orinoco_ioctl_setessid(struct net_device *dev,
3634 struct iw_request_info *info,
3635 struct iw_point *erq,
3636 char *essidbuf)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003637{
3638 struct orinoco_private *priv = netdev_priv(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003639 unsigned long flags;
3640
3641 /* Note : ESSID is ignored in Ad-Hoc demo mode, but we can set it
3642 * anyway... - Jean II */
3643
Christoph Hellwig620554e2005-06-19 01:27:33 +02003644 /* Hum... Should not use Wireless Extension constant (may change),
3645 * should use our own... - Jean II */
3646 if (erq->length > IW_ESSID_MAX_SIZE)
3647 return -E2BIG;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003648
3649 if (orinoco_lock(priv, &flags) != 0)
3650 return -EBUSY;
3651
Christoph Hellwig620554e2005-06-19 01:27:33 +02003652 /* NULL the string (for NULL termination & ESSID = ANY) - Jean II */
3653 memset(priv->desired_essid, 0, sizeof(priv->desired_essid));
3654
3655 /* If not ANY, get the new ESSID */
David Kilroy566f2d92009-02-04 23:05:45 +00003656 if (erq->flags)
Christoph Hellwig620554e2005-06-19 01:27:33 +02003657 memcpy(priv->desired_essid, essidbuf, erq->length);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003658
3659 orinoco_unlock(priv, &flags);
3660
Christoph Hellwig620554e2005-06-19 01:27:33 +02003661 return -EINPROGRESS; /* Call commit handler */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003662}
3663
Christoph Hellwig620554e2005-06-19 01:27:33 +02003664static int orinoco_ioctl_getessid(struct net_device *dev,
3665 struct iw_request_info *info,
3666 struct iw_point *erq,
3667 char *essidbuf)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003668{
3669 struct orinoco_private *priv = netdev_priv(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003670 int active;
3671 int err = 0;
3672 unsigned long flags;
3673
Linus Torvalds1da177e2005-04-16 15:20:36 -07003674 if (netif_running(dev)) {
3675 err = orinoco_hw_get_essid(priv, &active, essidbuf);
Jean Tourrilhes7e4e8d92006-10-10 14:45:44 -07003676 if (err < 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003677 return err;
Jean Tourrilhes7e4e8d92006-10-10 14:45:44 -07003678 erq->length = err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003679 } else {
3680 if (orinoco_lock(priv, &flags) != 0)
3681 return -EBUSY;
Jean Tourrilhes7e4e8d92006-10-10 14:45:44 -07003682 memcpy(essidbuf, priv->desired_essid, IW_ESSID_MAX_SIZE);
3683 erq->length = strlen(priv->desired_essid);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003684 orinoco_unlock(priv, &flags);
3685 }
3686
3687 erq->flags = 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003688
Linus Torvalds1da177e2005-04-16 15:20:36 -07003689 return 0;
3690}
3691
Christoph Hellwig620554e2005-06-19 01:27:33 +02003692static int orinoco_ioctl_setnick(struct net_device *dev,
3693 struct iw_request_info *info,
3694 struct iw_point *nrq,
3695 char *nickbuf)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003696{
3697 struct orinoco_private *priv = netdev_priv(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003698 unsigned long flags;
3699
3700 if (nrq->length > IW_ESSID_MAX_SIZE)
3701 return -E2BIG;
3702
Linus Torvalds1da177e2005-04-16 15:20:36 -07003703 if (orinoco_lock(priv, &flags) != 0)
3704 return -EBUSY;
3705
Christoph Hellwig620554e2005-06-19 01:27:33 +02003706 memset(priv->nick, 0, sizeof(priv->nick));
3707 memcpy(priv->nick, nickbuf, nrq->length);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003708
3709 orinoco_unlock(priv, &flags);
3710
Christoph Hellwig620554e2005-06-19 01:27:33 +02003711 return -EINPROGRESS; /* Call commit handler */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003712}
3713
Christoph Hellwig620554e2005-06-19 01:27:33 +02003714static int orinoco_ioctl_getnick(struct net_device *dev,
3715 struct iw_request_info *info,
3716 struct iw_point *nrq,
3717 char *nickbuf)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003718{
3719 struct orinoco_private *priv = netdev_priv(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003720 unsigned long flags;
3721
3722 if (orinoco_lock(priv, &flags) != 0)
3723 return -EBUSY;
3724
Jean Tourrilhes7e4e8d92006-10-10 14:45:44 -07003725 memcpy(nickbuf, priv->nick, IW_ESSID_MAX_SIZE);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003726 orinoco_unlock(priv, &flags);
3727
Jean Tourrilhes7e4e8d92006-10-10 14:45:44 -07003728 nrq->length = strlen(priv->nick);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003729
Linus Torvalds1da177e2005-04-16 15:20:36 -07003730 return 0;
3731}
3732
Christoph Hellwig620554e2005-06-19 01:27:33 +02003733static int orinoco_ioctl_setfreq(struct net_device *dev,
3734 struct iw_request_info *info,
3735 struct iw_freq *frq,
3736 char *extra)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003737{
3738 struct orinoco_private *priv = netdev_priv(dev);
3739 int chan = -1;
3740 unsigned long flags;
Christoph Hellwig620554e2005-06-19 01:27:33 +02003741 int err = -EINPROGRESS; /* Call commit handler */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003742
Christoph Hellwig98c4cae2005-06-19 01:28:06 +02003743 /* In infrastructure mode the AP sets the channel */
3744 if (priv->iw_mode == IW_MODE_INFRA)
3745 return -EBUSY;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003746
David Kilroya94e8422009-02-04 23:05:44 +00003747 if ((frq->e == 0) && (frq->m <= 1000)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07003748 /* Setting by channel number */
3749 chan = frq->m;
3750 } else {
David Kilroy9ee677c2008-12-23 14:03:38 +00003751 /* Setting by frequency */
3752 int denom = 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003753 int i;
3754
David Kilroy9ee677c2008-12-23 14:03:38 +00003755 /* Calculate denominator to rescale to MHz */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003756 for (i = 0; i < (6 - frq->e); i++)
David Kilroy9ee677c2008-12-23 14:03:38 +00003757 denom *= 10;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003758
David Kilroy9ee677c2008-12-23 14:03:38 +00003759 chan = ieee80211_freq_to_dsss_chan(frq->m / denom);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003760 }
3761
David Kilroya94e8422009-02-04 23:05:44 +00003762 if ((chan < 1) || (chan > NUM_CHANNELS) ||
3763 !(priv->channel_mask & (1 << (chan-1))))
Linus Torvalds1da177e2005-04-16 15:20:36 -07003764 return -EINVAL;
3765
3766 if (orinoco_lock(priv, &flags) != 0)
3767 return -EBUSY;
Christoph Hellwig98c4cae2005-06-19 01:28:06 +02003768
Linus Torvalds1da177e2005-04-16 15:20:36 -07003769 priv->channel = chan;
Christoph Hellwig98c4cae2005-06-19 01:28:06 +02003770 if (priv->iw_mode == IW_MODE_MONITOR) {
3771 /* Fast channel change - no commit if successful */
3772 hermes_t *hw = &priv->hw;
3773 err = hermes_docmd_wait(hw, HERMES_CMD_TEST |
3774 HERMES_TEST_SET_CHANNEL,
3775 chan, NULL);
3776 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07003777 orinoco_unlock(priv, &flags);
3778
Christoph Hellwig620554e2005-06-19 01:27:33 +02003779 return err;
3780}
3781
3782static int orinoco_ioctl_getfreq(struct net_device *dev,
3783 struct iw_request_info *info,
3784 struct iw_freq *frq,
3785 char *extra)
3786{
3787 struct orinoco_private *priv = netdev_priv(dev);
3788 int tmp;
3789
3790 /* Locking done in there */
3791 tmp = orinoco_hw_get_freq(priv);
David Kilroy566f2d92009-02-04 23:05:45 +00003792 if (tmp < 0)
Christoph Hellwig620554e2005-06-19 01:27:33 +02003793 return tmp;
Christoph Hellwig620554e2005-06-19 01:27:33 +02003794
David Kilroy9ee677c2008-12-23 14:03:38 +00003795 frq->m = tmp * 100000;
Christoph Hellwig620554e2005-06-19 01:27:33 +02003796 frq->e = 1;
3797
Linus Torvalds1da177e2005-04-16 15:20:36 -07003798 return 0;
3799}
3800
Christoph Hellwig620554e2005-06-19 01:27:33 +02003801static int orinoco_ioctl_getsens(struct net_device *dev,
3802 struct iw_request_info *info,
3803 struct iw_param *srq,
3804 char *extra)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003805{
3806 struct orinoco_private *priv = netdev_priv(dev);
3807 hermes_t *hw = &priv->hw;
3808 u16 val;
3809 int err;
3810 unsigned long flags;
3811
3812 if (!priv->has_sensitivity)
3813 return -EOPNOTSUPP;
3814
3815 if (orinoco_lock(priv, &flags) != 0)
3816 return -EBUSY;
3817 err = hermes_read_wordrec(hw, USER_BAP,
3818 HERMES_RID_CNFSYSTEMSCALE, &val);
3819 orinoco_unlock(priv, &flags);
3820
3821 if (err)
3822 return err;
3823
3824 srq->value = val;
3825 srq->fixed = 0; /* auto */
3826
3827 return 0;
3828}
3829
Christoph Hellwig620554e2005-06-19 01:27:33 +02003830static int orinoco_ioctl_setsens(struct net_device *dev,
3831 struct iw_request_info *info,
3832 struct iw_param *srq,
3833 char *extra)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003834{
3835 struct orinoco_private *priv = netdev_priv(dev);
3836 int val = srq->value;
3837 unsigned long flags;
3838
3839 if (!priv->has_sensitivity)
3840 return -EOPNOTSUPP;
3841
3842 if ((val < 1) || (val > 3))
3843 return -EINVAL;
David Kilroy6fe9deb2009-02-04 23:05:43 +00003844
Linus Torvalds1da177e2005-04-16 15:20:36 -07003845 if (orinoco_lock(priv, &flags) != 0)
3846 return -EBUSY;
3847 priv->ap_density = val;
3848 orinoco_unlock(priv, &flags);
3849
Christoph Hellwig620554e2005-06-19 01:27:33 +02003850 return -EINPROGRESS; /* Call commit handler */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003851}
3852
Christoph Hellwig620554e2005-06-19 01:27:33 +02003853static int orinoco_ioctl_setrts(struct net_device *dev,
3854 struct iw_request_info *info,
3855 struct iw_param *rrq,
3856 char *extra)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003857{
3858 struct orinoco_private *priv = netdev_priv(dev);
3859 int val = rrq->value;
3860 unsigned long flags;
3861
3862 if (rrq->disabled)
3863 val = 2347;
3864
David Kilroya94e8422009-02-04 23:05:44 +00003865 if ((val < 0) || (val > 2347))
Linus Torvalds1da177e2005-04-16 15:20:36 -07003866 return -EINVAL;
3867
3868 if (orinoco_lock(priv, &flags) != 0)
3869 return -EBUSY;
3870
3871 priv->rts_thresh = val;
3872 orinoco_unlock(priv, &flags);
3873
Christoph Hellwig620554e2005-06-19 01:27:33 +02003874 return -EINPROGRESS; /* Call commit handler */
3875}
3876
3877static int orinoco_ioctl_getrts(struct net_device *dev,
3878 struct iw_request_info *info,
3879 struct iw_param *rrq,
3880 char *extra)
3881{
3882 struct orinoco_private *priv = netdev_priv(dev);
3883
3884 rrq->value = priv->rts_thresh;
3885 rrq->disabled = (rrq->value == 2347);
3886 rrq->fixed = 1;
3887
Linus Torvalds1da177e2005-04-16 15:20:36 -07003888 return 0;
3889}
3890
Christoph Hellwig620554e2005-06-19 01:27:33 +02003891static int orinoco_ioctl_setfrag(struct net_device *dev,
3892 struct iw_request_info *info,
3893 struct iw_param *frq,
3894 char *extra)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003895{
3896 struct orinoco_private *priv = netdev_priv(dev);
Christoph Hellwig620554e2005-06-19 01:27:33 +02003897 int err = -EINPROGRESS; /* Call commit handler */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003898 unsigned long flags;
3899
3900 if (orinoco_lock(priv, &flags) != 0)
3901 return -EBUSY;
3902
3903 if (priv->has_mwo) {
3904 if (frq->disabled)
3905 priv->mwo_robust = 0;
3906 else {
3907 if (frq->fixed)
David Kilroyb2f30a02009-02-04 23:05:46 +00003908 printk(KERN_WARNING "%s: Fixed fragmentation "
3909 "is not supported on this firmware. "
3910 "Using MWO robust instead.\n",
3911 dev->name);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003912 priv->mwo_robust = 1;
3913 }
3914 } else {
3915 if (frq->disabled)
3916 priv->frag_thresh = 2346;
3917 else {
David Kilroya94e8422009-02-04 23:05:44 +00003918 if ((frq->value < 256) || (frq->value > 2346))
Linus Torvalds1da177e2005-04-16 15:20:36 -07003919 err = -EINVAL;
3920 else
David Kilroyb2f30a02009-02-04 23:05:46 +00003921 /* must be even */
3922 priv->frag_thresh = frq->value & ~0x1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003923 }
3924 }
3925
3926 orinoco_unlock(priv, &flags);
3927
3928 return err;
3929}
3930
Christoph Hellwig620554e2005-06-19 01:27:33 +02003931static int orinoco_ioctl_getfrag(struct net_device *dev,
3932 struct iw_request_info *info,
3933 struct iw_param *frq,
3934 char *extra)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003935{
3936 struct orinoco_private *priv = netdev_priv(dev);
3937 hermes_t *hw = &priv->hw;
Christoph Hellwig620554e2005-06-19 01:27:33 +02003938 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003939 u16 val;
3940 unsigned long flags;
3941
3942 if (orinoco_lock(priv, &flags) != 0)
3943 return -EBUSY;
David Kilroy6fe9deb2009-02-04 23:05:43 +00003944
Linus Torvalds1da177e2005-04-16 15:20:36 -07003945 if (priv->has_mwo) {
3946 err = hermes_read_wordrec(hw, USER_BAP,
3947 HERMES_RID_CNFMWOROBUST_AGERE,
3948 &val);
3949 if (err)
3950 val = 0;
3951
3952 frq->value = val ? 2347 : 0;
David Kilroya94e8422009-02-04 23:05:44 +00003953 frq->disabled = !val;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003954 frq->fixed = 0;
3955 } else {
David Kilroyb2f30a02009-02-04 23:05:46 +00003956 err = hermes_read_wordrec(hw, USER_BAP,
3957 HERMES_RID_CNFFRAGMENTATIONTHRESHOLD,
Linus Torvalds1da177e2005-04-16 15:20:36 -07003958 &val);
3959 if (err)
3960 val = 0;
3961
3962 frq->value = val;
3963 frq->disabled = (val >= 2346);
3964 frq->fixed = 1;
3965 }
3966
3967 orinoco_unlock(priv, &flags);
David Kilroy6fe9deb2009-02-04 23:05:43 +00003968
Linus Torvalds1da177e2005-04-16 15:20:36 -07003969 return err;
3970}
3971
Christoph Hellwig620554e2005-06-19 01:27:33 +02003972static int orinoco_ioctl_setrate(struct net_device *dev,
3973 struct iw_request_info *info,
3974 struct iw_param *rrq,
3975 char *extra)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003976{
3977 struct orinoco_private *priv = netdev_priv(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003978 int ratemode = -1;
3979 int bitrate; /* 100s of kilobits */
3980 int i;
3981 unsigned long flags;
David Kilroy6fe9deb2009-02-04 23:05:43 +00003982
Linus Torvalds1da177e2005-04-16 15:20:36 -07003983 /* As the user space doesn't know our highest rate, it uses -1
3984 * to ask us to set the highest rate. Test it using "iwconfig
3985 * ethX rate auto" - Jean II */
3986 if (rrq->value == -1)
3987 bitrate = 110;
3988 else {
3989 if (rrq->value % 100000)
3990 return -EINVAL;
3991 bitrate = rrq->value / 100000;
3992 }
3993
David Kilroya94e8422009-02-04 23:05:44 +00003994 if ((bitrate != 10) && (bitrate != 20) &&
3995 (bitrate != 55) && (bitrate != 110))
Linus Torvalds1da177e2005-04-16 15:20:36 -07003996 return -EINVAL;
3997
3998 for (i = 0; i < BITRATE_TABLE_SIZE; i++)
David Kilroya94e8422009-02-04 23:05:44 +00003999 if ((bitrate_table[i].bitrate == bitrate) &&
4000 (bitrate_table[i].automatic == !rrq->fixed)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07004001 ratemode = i;
4002 break;
4003 }
David Kilroy6fe9deb2009-02-04 23:05:43 +00004004
Linus Torvalds1da177e2005-04-16 15:20:36 -07004005 if (ratemode == -1)
4006 return -EINVAL;
4007
4008 if (orinoco_lock(priv, &flags) != 0)
4009 return -EBUSY;
4010 priv->bitratemode = ratemode;
4011 orinoco_unlock(priv, &flags);
4012
Christoph Hellwig620554e2005-06-19 01:27:33 +02004013 return -EINPROGRESS;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004014}
4015
Christoph Hellwig620554e2005-06-19 01:27:33 +02004016static int orinoco_ioctl_getrate(struct net_device *dev,
4017 struct iw_request_info *info,
4018 struct iw_param *rrq,
4019 char *extra)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004020{
4021 struct orinoco_private *priv = netdev_priv(dev);
4022 hermes_t *hw = &priv->hw;
4023 int err = 0;
4024 int ratemode;
4025 int i;
4026 u16 val;
4027 unsigned long flags;
4028
4029 if (orinoco_lock(priv, &flags) != 0)
4030 return -EBUSY;
4031
4032 ratemode = priv->bitratemode;
4033
4034 BUG_ON((ratemode < 0) || (ratemode >= BITRATE_TABLE_SIZE));
4035
4036 rrq->value = bitrate_table[ratemode].bitrate * 100000;
David Kilroya94e8422009-02-04 23:05:44 +00004037 rrq->fixed = !bitrate_table[ratemode].automatic;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004038 rrq->disabled = 0;
4039
4040 /* If the interface is running we try to find more about the
4041 current mode */
4042 if (netif_running(dev)) {
4043 err = hermes_read_wordrec(hw, USER_BAP,
4044 HERMES_RID_CURRENTTXRATE, &val);
4045 if (err)
4046 goto out;
David Kilroy6fe9deb2009-02-04 23:05:43 +00004047
Linus Torvalds1da177e2005-04-16 15:20:36 -07004048 switch (priv->firmware_type) {
4049 case FIRMWARE_TYPE_AGERE: /* Lucent style rate */
4050 /* Note : in Lucent firmware, the return value of
4051 * HERMES_RID_CURRENTTXRATE is the bitrate in Mb/s,
4052 * and therefore is totally different from the
4053 * encoding of HERMES_RID_CNFTXRATECONTROL.
4054 * Don't forget that 6Mb/s is really 5.5Mb/s */
4055 if (val == 6)
4056 rrq->value = 5500000;
4057 else
4058 rrq->value = val * 1000000;
4059 break;
4060 case FIRMWARE_TYPE_INTERSIL: /* Intersil style rate */
4061 case FIRMWARE_TYPE_SYMBOL: /* Symbol style rate */
4062 for (i = 0; i < BITRATE_TABLE_SIZE; i++)
4063 if (bitrate_table[i].intersil_txratectrl == val) {
4064 ratemode = i;
4065 break;
4066 }
4067 if (i >= BITRATE_TABLE_SIZE)
4068 printk(KERN_INFO "%s: Unable to determine current bitrate (0x%04hx)\n",
4069 dev->name, val);
4070
4071 rrq->value = bitrate_table[ratemode].bitrate * 100000;
4072 break;
4073 default:
4074 BUG();
4075 }
4076 }
4077
4078 out:
4079 orinoco_unlock(priv, &flags);
4080
4081 return err;
4082}
4083
Christoph Hellwig620554e2005-06-19 01:27:33 +02004084static int orinoco_ioctl_setpower(struct net_device *dev,
4085 struct iw_request_info *info,
4086 struct iw_param *prq,
4087 char *extra)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004088{
4089 struct orinoco_private *priv = netdev_priv(dev);
Christoph Hellwig620554e2005-06-19 01:27:33 +02004090 int err = -EINPROGRESS; /* Call commit handler */
Linus Torvalds1da177e2005-04-16 15:20:36 -07004091 unsigned long flags;
4092
4093 if (orinoco_lock(priv, &flags) != 0)
4094 return -EBUSY;
4095
4096 if (prq->disabled) {
4097 priv->pm_on = 0;
4098 } else {
4099 switch (prq->flags & IW_POWER_MODE) {
4100 case IW_POWER_UNICAST_R:
4101 priv->pm_mcast = 0;
4102 priv->pm_on = 1;
4103 break;
4104 case IW_POWER_ALL_R:
4105 priv->pm_mcast = 1;
4106 priv->pm_on = 1;
4107 break;
4108 case IW_POWER_ON:
4109 /* No flags : but we may have a value - Jean II */
4110 break;
4111 default:
4112 err = -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004113 goto out;
Pavel Roskinc08ad1e2005-11-29 02:59:27 -05004114 }
David Kilroy6fe9deb2009-02-04 23:05:43 +00004115
Linus Torvalds1da177e2005-04-16 15:20:36 -07004116 if (prq->flags & IW_POWER_TIMEOUT) {
4117 priv->pm_on = 1;
4118 priv->pm_timeout = prq->value / 1000;
4119 }
4120 if (prq->flags & IW_POWER_PERIOD) {
4121 priv->pm_on = 1;
4122 priv->pm_period = prq->value / 1000;
4123 }
4124 /* It's valid to not have a value if we are just toggling
4125 * the flags... Jean II */
David Kilroya94e8422009-02-04 23:05:44 +00004126 if (!priv->pm_on) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07004127 err = -EINVAL;
4128 goto out;
David Kilroy6fe9deb2009-02-04 23:05:43 +00004129 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07004130 }
4131
4132 out:
4133 orinoco_unlock(priv, &flags);
4134
4135 return err;
4136}
4137
Christoph Hellwig620554e2005-06-19 01:27:33 +02004138static int orinoco_ioctl_getpower(struct net_device *dev,
4139 struct iw_request_info *info,
4140 struct iw_param *prq,
4141 char *extra)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004142{
4143 struct orinoco_private *priv = netdev_priv(dev);
4144 hermes_t *hw = &priv->hw;
4145 int err = 0;
4146 u16 enable, period, timeout, mcast;
4147 unsigned long flags;
4148
4149 if (orinoco_lock(priv, &flags) != 0)
4150 return -EBUSY;
David Kilroy6fe9deb2009-02-04 23:05:43 +00004151
David Kilroyb2f30a02009-02-04 23:05:46 +00004152 err = hermes_read_wordrec(hw, USER_BAP,
4153 HERMES_RID_CNFPMENABLED, &enable);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004154 if (err)
4155 goto out;
4156
4157 err = hermes_read_wordrec(hw, USER_BAP,
4158 HERMES_RID_CNFMAXSLEEPDURATION, &period);
4159 if (err)
4160 goto out;
4161
David Kilroyb2f30a02009-02-04 23:05:46 +00004162 err = hermes_read_wordrec(hw, USER_BAP,
4163 HERMES_RID_CNFPMHOLDOVERDURATION, &timeout);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004164 if (err)
4165 goto out;
4166
David Kilroyb2f30a02009-02-04 23:05:46 +00004167 err = hermes_read_wordrec(hw, USER_BAP,
4168 HERMES_RID_CNFMULTICASTRECEIVE, &mcast);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004169 if (err)
4170 goto out;
4171
4172 prq->disabled = !enable;
4173 /* Note : by default, display the period */
4174 if ((prq->flags & IW_POWER_TYPE) == IW_POWER_TIMEOUT) {
4175 prq->flags = IW_POWER_TIMEOUT;
4176 prq->value = timeout * 1000;
4177 } else {
4178 prq->flags = IW_POWER_PERIOD;
4179 prq->value = period * 1000;
4180 }
4181 if (mcast)
4182 prq->flags |= IW_POWER_ALL_R;
4183 else
4184 prq->flags |= IW_POWER_UNICAST_R;
4185
4186 out:
4187 orinoco_unlock(priv, &flags);
4188
4189 return err;
4190}
4191
David Kilroyd03032a2008-08-21 23:28:02 +01004192static int orinoco_ioctl_set_encodeext(struct net_device *dev,
4193 struct iw_request_info *info,
4194 union iwreq_data *wrqu,
4195 char *extra)
4196{
4197 struct orinoco_private *priv = netdev_priv(dev);
4198 struct iw_point *encoding = &wrqu->encoding;
4199 struct iw_encode_ext *ext = (struct iw_encode_ext *)extra;
4200 int idx, alg = ext->alg, set_key = 1;
4201 unsigned long flags;
4202 int err = -EINVAL;
4203 u16 key_len;
4204
4205 if (orinoco_lock(priv, &flags) != 0)
4206 return -EBUSY;
4207
4208 /* Determine and validate the key index */
4209 idx = encoding->flags & IW_ENCODE_INDEX;
4210 if (idx) {
Johannes Berg2c7060022008-10-30 22:09:54 +01004211 if ((idx < 1) || (idx > 4))
David Kilroyd03032a2008-08-21 23:28:02 +01004212 goto out;
4213 idx--;
4214 } else
4215 idx = priv->tx_key;
4216
4217 if (encoding->flags & IW_ENCODE_DISABLED)
David Kilroy6fe9deb2009-02-04 23:05:43 +00004218 alg = IW_ENCODE_ALG_NONE;
David Kilroyd03032a2008-08-21 23:28:02 +01004219
4220 if (priv->has_wpa && (alg != IW_ENCODE_ALG_TKIP)) {
4221 /* Clear any TKIP TX key we had */
4222 (void) orinoco_clear_tkip_key(priv, priv->tx_key);
4223 }
4224
4225 if (ext->ext_flags & IW_ENCODE_EXT_SET_TX_KEY) {
4226 priv->tx_key = idx;
4227 set_key = ((alg == IW_ENCODE_ALG_TKIP) ||
4228 (ext->key_len > 0)) ? 1 : 0;
4229 }
4230
4231 if (set_key) {
4232 /* Set the requested key first */
4233 switch (alg) {
4234 case IW_ENCODE_ALG_NONE:
4235 priv->encode_alg = alg;
4236 priv->keys[idx].len = 0;
4237 break;
4238
4239 case IW_ENCODE_ALG_WEP:
4240 if (ext->key_len > SMALL_KEY_SIZE)
4241 key_len = LARGE_KEY_SIZE;
4242 else if (ext->key_len > 0)
4243 key_len = SMALL_KEY_SIZE;
4244 else
4245 goto out;
4246
4247 priv->encode_alg = alg;
4248 priv->keys[idx].len = cpu_to_le16(key_len);
4249
4250 key_len = min(ext->key_len, key_len);
4251
4252 memset(priv->keys[idx].data, 0, ORINOCO_MAX_KEY_SIZE);
4253 memcpy(priv->keys[idx].data, ext->key, key_len);
4254 break;
4255
4256 case IW_ENCODE_ALG_TKIP:
4257 {
4258 hermes_t *hw = &priv->hw;
4259 u8 *tkip_iv = NULL;
4260
4261 if (!priv->has_wpa ||
4262 (ext->key_len > sizeof(priv->tkip_key[0])))
4263 goto out;
4264
4265 priv->encode_alg = alg;
4266 memset(&priv->tkip_key[idx], 0,
4267 sizeof(priv->tkip_key[idx]));
4268 memcpy(&priv->tkip_key[idx], ext->key, ext->key_len);
4269
4270 if (ext->ext_flags & IW_ENCODE_EXT_RX_SEQ_VALID)
4271 tkip_iv = &ext->rx_seq[0];
4272
4273 err = __orinoco_hw_set_tkip_key(hw, idx,
4274 ext->ext_flags & IW_ENCODE_EXT_SET_TX_KEY,
4275 (u8 *) &priv->tkip_key[idx],
4276 tkip_iv, NULL);
4277 if (err)
4278 printk(KERN_ERR "%s: Error %d setting TKIP key"
4279 "\n", dev->name, err);
4280
4281 goto out;
4282 }
4283 default:
4284 goto out;
4285 }
4286 }
4287 err = -EINPROGRESS;
4288 out:
4289 orinoco_unlock(priv, &flags);
4290
4291 return err;
4292}
4293
4294static int orinoco_ioctl_get_encodeext(struct net_device *dev,
4295 struct iw_request_info *info,
4296 union iwreq_data *wrqu,
4297 char *extra)
4298{
4299 struct orinoco_private *priv = netdev_priv(dev);
4300 struct iw_point *encoding = &wrqu->encoding;
4301 struct iw_encode_ext *ext = (struct iw_encode_ext *)extra;
4302 int idx, max_key_len;
4303 unsigned long flags;
4304 int err;
4305
4306 if (orinoco_lock(priv, &flags) != 0)
4307 return -EBUSY;
4308
4309 err = -EINVAL;
4310 max_key_len = encoding->length - sizeof(*ext);
4311 if (max_key_len < 0)
4312 goto out;
4313
4314 idx = encoding->flags & IW_ENCODE_INDEX;
4315 if (idx) {
Johannes Berg2c7060022008-10-30 22:09:54 +01004316 if ((idx < 1) || (idx > 4))
David Kilroyd03032a2008-08-21 23:28:02 +01004317 goto out;
4318 idx--;
4319 } else
4320 idx = priv->tx_key;
4321
4322 encoding->flags = idx + 1;
4323 memset(ext, 0, sizeof(*ext));
4324
4325 ext->alg = priv->encode_alg;
4326 switch (priv->encode_alg) {
4327 case IW_ENCODE_ALG_NONE:
4328 ext->key_len = 0;
4329 encoding->flags |= IW_ENCODE_DISABLED;
4330 break;
4331 case IW_ENCODE_ALG_WEP:
David Kilroy75d31cf2008-09-12 22:28:18 +01004332 ext->key_len = min_t(u16, le16_to_cpu(priv->keys[idx].len),
4333 max_key_len);
David Kilroyd03032a2008-08-21 23:28:02 +01004334 memcpy(ext->key, priv->keys[idx].data, ext->key_len);
4335 encoding->flags |= IW_ENCODE_ENABLED;
4336 break;
4337 case IW_ENCODE_ALG_TKIP:
David Kilroy75d31cf2008-09-12 22:28:18 +01004338 ext->key_len = min_t(u16, sizeof(struct orinoco_tkip_key),
4339 max_key_len);
David Kilroyd03032a2008-08-21 23:28:02 +01004340 memcpy(ext->key, &priv->tkip_key[idx], ext->key_len);
4341 encoding->flags |= IW_ENCODE_ENABLED;
4342 break;
4343 }
4344
4345 err = 0;
4346 out:
4347 orinoco_unlock(priv, &flags);
4348
4349 return err;
4350}
4351
4352static int orinoco_ioctl_set_auth(struct net_device *dev,
4353 struct iw_request_info *info,
4354 union iwreq_data *wrqu, char *extra)
4355{
4356 struct orinoco_private *priv = netdev_priv(dev);
4357 hermes_t *hw = &priv->hw;
4358 struct iw_param *param = &wrqu->param;
4359 unsigned long flags;
4360 int ret = -EINPROGRESS;
4361
4362 if (orinoco_lock(priv, &flags) != 0)
4363 return -EBUSY;
4364
4365 switch (param->flags & IW_AUTH_INDEX) {
4366 case IW_AUTH_WPA_VERSION:
4367 case IW_AUTH_CIPHER_PAIRWISE:
4368 case IW_AUTH_CIPHER_GROUP:
4369 case IW_AUTH_RX_UNENCRYPTED_EAPOL:
4370 case IW_AUTH_PRIVACY_INVOKED:
4371 case IW_AUTH_DROP_UNENCRYPTED:
4372 /*
4373 * orinoco does not use these parameters
4374 */
4375 break;
4376
4377 case IW_AUTH_KEY_MGMT:
4378 /* wl_lkm implies value 2 == PSK for Hermes I
4379 * which ties in with WEXT
4380 * no other hints tho :(
4381 */
4382 priv->key_mgmt = param->value;
4383 break;
4384
4385 case IW_AUTH_TKIP_COUNTERMEASURES:
4386 /* When countermeasures are enabled, shut down the
4387 * card; when disabled, re-enable the card. This must
4388 * take effect immediately.
4389 *
4390 * TODO: Make sure that the EAPOL message is getting
4391 * out before card disabled
4392 */
4393 if (param->value) {
4394 priv->tkip_cm_active = 1;
4395 ret = hermes_enable_port(hw, 0);
4396 } else {
4397 priv->tkip_cm_active = 0;
4398 ret = hermes_disable_port(hw, 0);
4399 }
4400 break;
4401
4402 case IW_AUTH_80211_AUTH_ALG:
4403 if (param->value & IW_AUTH_ALG_SHARED_KEY)
4404 priv->wep_restrict = 1;
4405 else if (param->value & IW_AUTH_ALG_OPEN_SYSTEM)
4406 priv->wep_restrict = 0;
4407 else
4408 ret = -EINVAL;
4409 break;
4410
4411 case IW_AUTH_WPA_ENABLED:
4412 if (priv->has_wpa) {
4413 priv->wpa_enabled = param->value ? 1 : 0;
4414 } else {
4415 if (param->value)
4416 ret = -EOPNOTSUPP;
4417 /* else silently accept disable of WPA */
4418 priv->wpa_enabled = 0;
4419 }
4420 break;
4421
4422 default:
4423 ret = -EOPNOTSUPP;
4424 }
4425
4426 orinoco_unlock(priv, &flags);
4427 return ret;
4428}
4429
4430static int orinoco_ioctl_get_auth(struct net_device *dev,
4431 struct iw_request_info *info,
4432 union iwreq_data *wrqu, char *extra)
4433{
4434 struct orinoco_private *priv = netdev_priv(dev);
4435 struct iw_param *param = &wrqu->param;
4436 unsigned long flags;
4437 int ret = 0;
4438
4439 if (orinoco_lock(priv, &flags) != 0)
4440 return -EBUSY;
4441
4442 switch (param->flags & IW_AUTH_INDEX) {
4443 case IW_AUTH_KEY_MGMT:
4444 param->value = priv->key_mgmt;
4445 break;
4446
4447 case IW_AUTH_TKIP_COUNTERMEASURES:
4448 param->value = priv->tkip_cm_active;
4449 break;
4450
4451 case IW_AUTH_80211_AUTH_ALG:
4452 if (priv->wep_restrict)
4453 param->value = IW_AUTH_ALG_SHARED_KEY;
4454 else
4455 param->value = IW_AUTH_ALG_OPEN_SYSTEM;
4456 break;
4457
4458 case IW_AUTH_WPA_ENABLED:
4459 param->value = priv->wpa_enabled;
4460 break;
4461
4462 default:
4463 ret = -EOPNOTSUPP;
4464 }
4465
4466 orinoco_unlock(priv, &flags);
4467 return ret;
4468}
4469
4470static int orinoco_ioctl_set_genie(struct net_device *dev,
4471 struct iw_request_info *info,
4472 union iwreq_data *wrqu, char *extra)
4473{
4474 struct orinoco_private *priv = netdev_priv(dev);
4475 u8 *buf;
4476 unsigned long flags;
David Kilroyd03032a2008-08-21 23:28:02 +01004477
Johannes Berg2c7060022008-10-30 22:09:54 +01004478 /* cut off at IEEE80211_MAX_DATA_LEN */
4479 if ((wrqu->data.length > IEEE80211_MAX_DATA_LEN) ||
David Kilroyd03032a2008-08-21 23:28:02 +01004480 (wrqu->data.length && (extra == NULL)))
4481 return -EINVAL;
4482
David Kilroyd03032a2008-08-21 23:28:02 +01004483 if (wrqu->data.length) {
4484 buf = kmalloc(wrqu->data.length, GFP_KERNEL);
Andrey Borzenkov7fe99c42009-01-20 20:26:46 +03004485 if (buf == NULL)
4486 return -ENOMEM;
David Kilroyd03032a2008-08-21 23:28:02 +01004487
4488 memcpy(buf, extra, wrqu->data.length);
Andrey Borzenkov7fe99c42009-01-20 20:26:46 +03004489 } else
4490 buf = NULL;
4491
4492 if (orinoco_lock(priv, &flags) != 0) {
4493 kfree(buf);
4494 return -EBUSY;
David Kilroyd03032a2008-08-21 23:28:02 +01004495 }
4496
Andrey Borzenkov7fe99c42009-01-20 20:26:46 +03004497 kfree(priv->wpa_ie);
4498 priv->wpa_ie = buf;
4499 priv->wpa_ie_len = wrqu->data.length;
4500
David Kilroyd03032a2008-08-21 23:28:02 +01004501 if (priv->wpa_ie) {
4502 /* Looks like wl_lkm wants to check the auth alg, and
4503 * somehow pass it to the firmware.
4504 * Instead it just calls the key mgmt rid
4505 * - we do this in set auth.
4506 */
4507 }
4508
David Kilroyd03032a2008-08-21 23:28:02 +01004509 orinoco_unlock(priv, &flags);
Andrey Borzenkov7fe99c42009-01-20 20:26:46 +03004510 return 0;
David Kilroyd03032a2008-08-21 23:28:02 +01004511}
4512
4513static int orinoco_ioctl_get_genie(struct net_device *dev,
4514 struct iw_request_info *info,
4515 union iwreq_data *wrqu, char *extra)
4516{
4517 struct orinoco_private *priv = netdev_priv(dev);
4518 unsigned long flags;
4519 int err = 0;
4520
4521 if (orinoco_lock(priv, &flags) != 0)
4522 return -EBUSY;
4523
4524 if ((priv->wpa_ie_len == 0) || (priv->wpa_ie == NULL)) {
4525 wrqu->data.length = 0;
4526 goto out;
4527 }
4528
4529 if (wrqu->data.length < priv->wpa_ie_len) {
4530 err = -E2BIG;
4531 goto out;
4532 }
4533
4534 wrqu->data.length = priv->wpa_ie_len;
4535 memcpy(extra, priv->wpa_ie, priv->wpa_ie_len);
4536
4537out:
4538 orinoco_unlock(priv, &flags);
4539 return err;
4540}
4541
4542static int orinoco_ioctl_set_mlme(struct net_device *dev,
4543 struct iw_request_info *info,
4544 union iwreq_data *wrqu, char *extra)
4545{
4546 struct orinoco_private *priv = netdev_priv(dev);
4547 hermes_t *hw = &priv->hw;
4548 struct iw_mlme *mlme = (struct iw_mlme *)extra;
4549 unsigned long flags;
4550 int ret = 0;
4551
4552 if (orinoco_lock(priv, &flags) != 0)
4553 return -EBUSY;
4554
4555 switch (mlme->cmd) {
4556 case IW_MLME_DEAUTH:
4557 /* silently ignore */
4558 break;
4559
4560 case IW_MLME_DISASSOC:
4561 {
4562 struct {
4563 u8 addr[ETH_ALEN];
4564 __le16 reason_code;
4565 } __attribute__ ((packed)) buf;
4566
4567 memcpy(buf.addr, mlme->addr.sa_data, ETH_ALEN);
4568 buf.reason_code = cpu_to_le16(mlme->reason_code);
4569 ret = HERMES_WRITE_RECORD(hw, USER_BAP,
4570 HERMES_RID_CNFDISASSOCIATE,
4571 &buf);
4572 break;
4573 }
4574 default:
4575 ret = -EOPNOTSUPP;
4576 }
4577
4578 orinoco_unlock(priv, &flags);
4579 return ret;
4580}
4581
Christoph Hellwig620554e2005-06-19 01:27:33 +02004582static int orinoco_ioctl_getretry(struct net_device *dev,
4583 struct iw_request_info *info,
4584 struct iw_param *rrq,
4585 char *extra)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004586{
4587 struct orinoco_private *priv = netdev_priv(dev);
4588 hermes_t *hw = &priv->hw;
4589 int err = 0;
4590 u16 short_limit, long_limit, lifetime;
4591 unsigned long flags;
4592
4593 if (orinoco_lock(priv, &flags) != 0)
4594 return -EBUSY;
David Kilroy6fe9deb2009-02-04 23:05:43 +00004595
Linus Torvalds1da177e2005-04-16 15:20:36 -07004596 err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_SHORTRETRYLIMIT,
4597 &short_limit);
4598 if (err)
4599 goto out;
4600
4601 err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_LONGRETRYLIMIT,
4602 &long_limit);
4603 if (err)
4604 goto out;
4605
4606 err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_MAXTRANSMITLIFETIME,
4607 &lifetime);
4608 if (err)
4609 goto out;
4610
4611 rrq->disabled = 0; /* Can't be disabled */
4612
4613 /* Note : by default, display the retry number */
4614 if ((rrq->flags & IW_RETRY_TYPE) == IW_RETRY_LIFETIME) {
4615 rrq->flags = IW_RETRY_LIFETIME;
4616 rrq->value = lifetime * 1000; /* ??? */
4617 } else {
4618 /* By default, display the min number */
Jean Tourrilheseeec9f12006-08-29 18:02:31 -07004619 if ((rrq->flags & IW_RETRY_LONG)) {
4620 rrq->flags = IW_RETRY_LIMIT | IW_RETRY_LONG;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004621 rrq->value = long_limit;
4622 } else {
4623 rrq->flags = IW_RETRY_LIMIT;
4624 rrq->value = short_limit;
David Kilroya94e8422009-02-04 23:05:44 +00004625 if (short_limit != long_limit)
Jean Tourrilheseeec9f12006-08-29 18:02:31 -07004626 rrq->flags |= IW_RETRY_SHORT;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004627 }
4628 }
4629
4630 out:
4631 orinoco_unlock(priv, &flags);
4632
4633 return err;
4634}
4635
Christoph Hellwig620554e2005-06-19 01:27:33 +02004636static int orinoco_ioctl_reset(struct net_device *dev,
4637 struct iw_request_info *info,
4638 void *wrqu,
4639 char *extra)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004640{
4641 struct orinoco_private *priv = netdev_priv(dev);
Christoph Hellwig620554e2005-06-19 01:27:33 +02004642
David Kilroya94e8422009-02-04 23:05:44 +00004643 if (!capable(CAP_NET_ADMIN))
Christoph Hellwig620554e2005-06-19 01:27:33 +02004644 return -EPERM;
4645
4646 if (info->cmd == (SIOCIWFIRSTPRIV + 0x1)) {
4647 printk(KERN_DEBUG "%s: Forcing reset!\n", dev->name);
4648
4649 /* Firmware reset */
David Howellsc4028952006-11-22 14:57:56 +00004650 orinoco_reset(&priv->reset_work);
Christoph Hellwig620554e2005-06-19 01:27:33 +02004651 } else {
4652 printk(KERN_DEBUG "%s: Force scheduling reset!\n", dev->name);
4653
4654 schedule_work(&priv->reset_work);
4655 }
4656
4657 return 0;
4658}
4659
4660static int orinoco_ioctl_setibssport(struct net_device *dev,
4661 struct iw_request_info *info,
4662 void *wrqu,
4663 char *extra)
4664
4665{
4666 struct orinoco_private *priv = netdev_priv(dev);
David Kilroya94e8422009-02-04 23:05:44 +00004667 int val = *((int *) extra);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004668 unsigned long flags;
4669
4670 if (orinoco_lock(priv, &flags) != 0)
4671 return -EBUSY;
4672
4673 priv->ibss_port = val ;
4674
4675 /* Actually update the mode we are using */
4676 set_port_type(priv);
4677
4678 orinoco_unlock(priv, &flags);
Christoph Hellwig620554e2005-06-19 01:27:33 +02004679 return -EINPROGRESS; /* Call commit handler */
Linus Torvalds1da177e2005-04-16 15:20:36 -07004680}
4681
Christoph Hellwig620554e2005-06-19 01:27:33 +02004682static int orinoco_ioctl_getibssport(struct net_device *dev,
4683 struct iw_request_info *info,
4684 void *wrqu,
4685 char *extra)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004686{
4687 struct orinoco_private *priv = netdev_priv(dev);
Christoph Hellwig620554e2005-06-19 01:27:33 +02004688 int *val = (int *) extra;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004689
4690 *val = priv->ibss_port;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004691 return 0;
4692}
4693
Christoph Hellwig620554e2005-06-19 01:27:33 +02004694static int orinoco_ioctl_setport3(struct net_device *dev,
4695 struct iw_request_info *info,
4696 void *wrqu,
4697 char *extra)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004698{
4699 struct orinoco_private *priv = netdev_priv(dev);
David Kilroya94e8422009-02-04 23:05:44 +00004700 int val = *((int *) extra);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004701 int err = 0;
4702 unsigned long flags;
4703
4704 if (orinoco_lock(priv, &flags) != 0)
4705 return -EBUSY;
4706
4707 switch (val) {
4708 case 0: /* Try to do IEEE ad-hoc mode */
David Kilroya94e8422009-02-04 23:05:44 +00004709 if (!priv->has_ibss) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07004710 err = -EINVAL;
4711 break;
4712 }
4713 priv->prefer_port3 = 0;
David Kilroy6fe9deb2009-02-04 23:05:43 +00004714
Linus Torvalds1da177e2005-04-16 15:20:36 -07004715 break;
4716
4717 case 1: /* Try to do Lucent proprietary ad-hoc mode */
David Kilroya94e8422009-02-04 23:05:44 +00004718 if (!priv->has_port3) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07004719 err = -EINVAL;
4720 break;
4721 }
4722 priv->prefer_port3 = 1;
4723 break;
4724
4725 default:
4726 err = -EINVAL;
4727 }
4728
David Kilroya94e8422009-02-04 23:05:44 +00004729 if (!err) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07004730 /* Actually update the mode we are using */
4731 set_port_type(priv);
Christoph Hellwig620554e2005-06-19 01:27:33 +02004732 err = -EINPROGRESS;
4733 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07004734
4735 orinoco_unlock(priv, &flags);
4736
4737 return err;
4738}
4739
Christoph Hellwig620554e2005-06-19 01:27:33 +02004740static int orinoco_ioctl_getport3(struct net_device *dev,
4741 struct iw_request_info *info,
4742 void *wrqu,
4743 char *extra)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004744{
4745 struct orinoco_private *priv = netdev_priv(dev);
Christoph Hellwig620554e2005-06-19 01:27:33 +02004746 int *val = (int *) extra;
4747
4748 *val = priv->prefer_port3;
4749 return 0;
4750}
4751
4752static int orinoco_ioctl_setpreamble(struct net_device *dev,
4753 struct iw_request_info *info,
4754 void *wrqu,
4755 char *extra)
4756{
4757 struct orinoco_private *priv = netdev_priv(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004758 unsigned long flags;
Christoph Hellwig620554e2005-06-19 01:27:33 +02004759 int val;
4760
David Kilroya94e8422009-02-04 23:05:44 +00004761 if (!priv->has_preamble)
Christoph Hellwig620554e2005-06-19 01:27:33 +02004762 return -EOPNOTSUPP;
4763
4764 /* 802.11b has recently defined some short preamble.
4765 * Basically, the Phy header has been reduced in size.
4766 * This increase performance, especially at high rates
4767 * (the preamble is transmitted at 1Mb/s), unfortunately
4768 * this give compatibility troubles... - Jean II */
David Kilroya94e8422009-02-04 23:05:44 +00004769 val = *((int *) extra);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004770
4771 if (orinoco_lock(priv, &flags) != 0)
4772 return -EBUSY;
4773
Christoph Hellwig620554e2005-06-19 01:27:33 +02004774 if (val)
4775 priv->preamble = 1;
4776 else
4777 priv->preamble = 0;
4778
Linus Torvalds1da177e2005-04-16 15:20:36 -07004779 orinoco_unlock(priv, &flags);
Christoph Hellwig620554e2005-06-19 01:27:33 +02004780
4781 return -EINPROGRESS; /* Call commit handler */
4782}
4783
4784static int orinoco_ioctl_getpreamble(struct net_device *dev,
4785 struct iw_request_info *info,
4786 void *wrqu,
4787 char *extra)
4788{
4789 struct orinoco_private *priv = netdev_priv(dev);
4790 int *val = (int *) extra;
4791
David Kilroya94e8422009-02-04 23:05:44 +00004792 if (!priv->has_preamble)
Christoph Hellwig620554e2005-06-19 01:27:33 +02004793 return -EOPNOTSUPP;
4794
4795 *val = priv->preamble;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004796 return 0;
4797}
4798
Christoph Hellwig620554e2005-06-19 01:27:33 +02004799/* ioctl interface to hermes_read_ltv()
4800 * To use with iwpriv, pass the RID as the token argument, e.g.
4801 * iwpriv get_rid [0xfc00]
4802 * At least Wireless Tools 25 is required to use iwpriv.
4803 * For Wireless Tools 25 and 26 append "dummy" are the end. */
4804static int orinoco_ioctl_getrid(struct net_device *dev,
4805 struct iw_request_info *info,
4806 struct iw_point *data,
4807 char *extra)
4808{
4809 struct orinoco_private *priv = netdev_priv(dev);
4810 hermes_t *hw = &priv->hw;
4811 int rid = data->flags;
4812 u16 length;
4813 int err;
4814 unsigned long flags;
4815
4816 /* It's a "get" function, but we don't want users to access the
4817 * WEP key and other raw firmware data */
David Kilroya94e8422009-02-04 23:05:44 +00004818 if (!capable(CAP_NET_ADMIN))
Christoph Hellwig620554e2005-06-19 01:27:33 +02004819 return -EPERM;
4820
4821 if (rid < 0xfc00 || rid > 0xffff)
4822 return -EINVAL;
4823
4824 if (orinoco_lock(priv, &flags) != 0)
4825 return -EBUSY;
4826
4827 err = hermes_read_ltv(hw, USER_BAP, rid, MAX_RID_LEN, &length,
4828 extra);
4829 if (err)
4830 goto out;
4831
4832 data->length = min_t(u16, HERMES_RECLEN_TO_BYTES(length),
4833 MAX_RID_LEN);
4834
4835 out:
4836 orinoco_unlock(priv, &flags);
4837 return err;
4838}
4839
David Kilroy17a1a882008-08-21 23:27:47 +01004840/* Trigger a scan (look for other cells in the vicinity) */
Christoph Hellwig95dd91f2005-06-19 01:27:56 +02004841static int orinoco_ioctl_setscan(struct net_device *dev,
4842 struct iw_request_info *info,
David Kilroy9930cce2008-09-13 12:22:05 +01004843 struct iw_point *srq,
Christoph Hellwig95dd91f2005-06-19 01:27:56 +02004844 char *extra)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004845{
4846 struct orinoco_private *priv = netdev_priv(dev);
Christoph Hellwig95dd91f2005-06-19 01:27:56 +02004847 hermes_t *hw = &priv->hw;
David Kilroy0753bba2008-08-21 23:27:46 +01004848 struct iw_scan_req *si = (struct iw_scan_req *) extra;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004849 int err = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004850 unsigned long flags;
4851
Christoph Hellwig95dd91f2005-06-19 01:27:56 +02004852 /* Note : you may have realised that, as this is a SET operation,
Alexey Dobriyan7f927fc2006-03-28 01:56:53 -08004853 * this is privileged and therefore a normal user can't
Christoph Hellwig95dd91f2005-06-19 01:27:56 +02004854 * perform scanning.
4855 * This is not an error, while the device perform scanning,
4856 * traffic doesn't flow, so it's a perfect DoS...
4857 * Jean II */
Linus Torvalds1da177e2005-04-16 15:20:36 -07004858
Christoph Hellwig95dd91f2005-06-19 01:27:56 +02004859 if (orinoco_lock(priv, &flags) != 0)
4860 return -EBUSY;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004861
Christoph Hellwig95dd91f2005-06-19 01:27:56 +02004862 /* Scanning with port 0 disabled would fail */
4863 if (!netif_running(dev)) {
4864 err = -ENETDOWN;
4865 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004866 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07004867
Christoph Hellwig95dd91f2005-06-19 01:27:56 +02004868 /* In monitor mode, the scan results are always empty.
4869 * Probe responses are passed to the driver as received
4870 * frames and could be processed in software. */
4871 if (priv->iw_mode == IW_MODE_MONITOR) {
4872 err = -EOPNOTSUPP;
4873 goto out;
4874 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07004875
Christoph Hellwig95dd91f2005-06-19 01:27:56 +02004876 /* Note : because we don't lock out the irq handler, the way
4877 * we access scan variables in priv is critical.
4878 * o scan_inprogress : not touched by irq handler
4879 * o scan_mode : not touched by irq handler
Christoph Hellwig95dd91f2005-06-19 01:27:56 +02004880 * Before modifying anything on those variables, please think hard !
4881 * Jean II */
4882
Christoph Hellwig95dd91f2005-06-19 01:27:56 +02004883 /* Save flags */
4884 priv->scan_mode = srq->flags;
4885
4886 /* Always trigger scanning, even if it's in progress.
4887 * This way, if the info frame get lost, we will recover somewhat
4888 * gracefully - Jean II */
4889
4890 if (priv->has_hostscan) {
4891 switch (priv->firmware_type) {
4892 case FIRMWARE_TYPE_SYMBOL:
4893 err = hermes_write_wordrec(hw, USER_BAP,
David Kilroyb2f30a02009-02-04 23:05:46 +00004894 HERMES_RID_CNFHOSTSCAN_SYMBOL,
4895 HERMES_HOSTSCAN_SYMBOL_ONCE |
4896 HERMES_HOSTSCAN_SYMBOL_BCAST);
Christoph Hellwig95dd91f2005-06-19 01:27:56 +02004897 break;
4898 case FIRMWARE_TYPE_INTERSIL: {
Pavel Roskind133ae42005-09-23 04:18:06 -04004899 __le16 req[3];
Christoph Hellwig95dd91f2005-06-19 01:27:56 +02004900
4901 req[0] = cpu_to_le16(0x3fff); /* All channels */
4902 req[1] = cpu_to_le16(0x0001); /* rate 1 Mbps */
4903 req[2] = 0; /* Any ESSID */
4904 err = HERMES_WRITE_RECORD(hw, USER_BAP,
4905 HERMES_RID_CNFHOSTSCAN, &req);
4906 }
4907 break;
4908 case FIRMWARE_TYPE_AGERE:
David Kilroy0753bba2008-08-21 23:27:46 +01004909 if (priv->scan_mode & IW_SCAN_THIS_ESSID) {
4910 struct hermes_idstring idbuf;
4911 size_t len = min(sizeof(idbuf.val),
4912 (size_t) si->essid_len);
4913 idbuf.len = cpu_to_le16(len);
4914 memcpy(idbuf.val, si->essid, len);
4915
4916 err = hermes_write_ltv(hw, USER_BAP,
4917 HERMES_RID_CNFSCANSSID_AGERE,
4918 HERMES_BYTES_TO_RECLEN(len + 2),
4919 &idbuf);
4920 } else
4921 err = hermes_write_wordrec(hw, USER_BAP,
Christoph Hellwig95dd91f2005-06-19 01:27:56 +02004922 HERMES_RID_CNFSCANSSID_AGERE,
4923 0); /* Any ESSID */
4924 if (err)
4925 break;
4926
David Kilroy01632fa2008-08-21 23:27:58 +01004927 if (priv->has_ext_scan) {
4928 /* Clear scan results at the start of
4929 * an extended scan */
4930 orinoco_clear_scan_results(priv,
4931 msecs_to_jiffies(15000));
4932
4933 /* TODO: Is this available on older firmware?
4934 * Can we use it to scan specific channels
4935 * for IW_SCAN_THIS_FREQ? */
4936 err = hermes_write_wordrec(hw, USER_BAP,
4937 HERMES_RID_CNFSCANCHANNELS2GHZ,
4938 0x7FFF);
4939 if (err)
4940 goto out;
4941
4942 err = hermes_inquire(hw,
4943 HERMES_INQ_CHANNELINFO);
4944 } else
4945 err = hermes_inquire(hw, HERMES_INQ_SCAN);
Christoph Hellwig95dd91f2005-06-19 01:27:56 +02004946 break;
4947 }
4948 } else
4949 err = hermes_inquire(hw, HERMES_INQ_SCAN);
4950
4951 /* One more client */
David Kilroya94e8422009-02-04 23:05:44 +00004952 if (!err)
Christoph Hellwig95dd91f2005-06-19 01:27:56 +02004953 priv->scan_inprogress = 1;
4954
4955 out:
4956 orinoco_unlock(priv, &flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004957 return err;
4958}
4959
Dan Williams1e3428e2007-10-10 23:56:25 -04004960#define MAX_CUSTOM_LEN 64
4961
Christoph Hellwig95dd91f2005-06-19 01:27:56 +02004962/* Translate scan data returned from the card to a card independant
David Kilroy17a1a882008-08-21 23:27:47 +01004963 * format that the Wireless Tools will understand - Jean II */
Dan Williams1e3428e2007-10-10 23:56:25 -04004964static inline char *orinoco_translate_scan(struct net_device *dev,
David S. Millerccc58052008-06-16 18:50:49 -07004965 struct iw_request_info *info,
Dan Williams1e3428e2007-10-10 23:56:25 -04004966 char *current_ev,
4967 char *end_buf,
4968 union hermes_scan_info *bss,
Pavel Roskindfe1baf2008-11-10 09:25:53 -05004969 unsigned long last_scanned)
Christoph Hellwig95dd91f2005-06-19 01:27:56 +02004970{
4971 struct orinoco_private *priv = netdev_priv(dev);
Christoph Hellwig95dd91f2005-06-19 01:27:56 +02004972 u16 capabilities;
4973 u16 channel;
4974 struct iw_event iwe; /* Temporary buffer */
Dan Williams1e3428e2007-10-10 23:56:25 -04004975 char custom[MAX_CUSTOM_LEN];
Christoph Hellwig95dd91f2005-06-19 01:27:56 +02004976
David Kilroy17a1a882008-08-21 23:27:47 +01004977 memset(&iwe, 0, sizeof(iwe));
4978
Dan Williams1e3428e2007-10-10 23:56:25 -04004979 /* First entry *MUST* be the AP MAC address */
4980 iwe.cmd = SIOCGIWAP;
4981 iwe.u.ap_addr.sa_family = ARPHRD_ETHER;
4982 memcpy(iwe.u.ap_addr.sa_data, bss->a.bssid, ETH_ALEN);
David S. Millerccc58052008-06-16 18:50:49 -07004983 current_ev = iwe_stream_add_event(info, current_ev, end_buf,
4984 &iwe, IW_EV_ADDR_LEN);
Dan Williams1e3428e2007-10-10 23:56:25 -04004985
4986 /* Other entries will be displayed in the order we give them */
4987
4988 /* Add the ESSID */
4989 iwe.u.data.length = le16_to_cpu(bss->a.essid_len);
4990 if (iwe.u.data.length > 32)
4991 iwe.u.data.length = 32;
4992 iwe.cmd = SIOCGIWESSID;
4993 iwe.u.data.flags = 1;
David S. Millerccc58052008-06-16 18:50:49 -07004994 current_ev = iwe_stream_add_point(info, current_ev, end_buf,
4995 &iwe, bss->a.essid);
Dan Williams1e3428e2007-10-10 23:56:25 -04004996
4997 /* Add mode */
4998 iwe.cmd = SIOCGIWMODE;
4999 capabilities = le16_to_cpu(bss->a.capabilities);
David Kilroy17a1a882008-08-21 23:27:47 +01005000 if (capabilities & (WLAN_CAPABILITY_ESS | WLAN_CAPABILITY_IBSS)) {
5001 if (capabilities & WLAN_CAPABILITY_ESS)
Dan Williams1e3428e2007-10-10 23:56:25 -04005002 iwe.u.mode = IW_MODE_MASTER;
Christoph Hellwig95dd91f2005-06-19 01:27:56 +02005003 else
Dan Williams1e3428e2007-10-10 23:56:25 -04005004 iwe.u.mode = IW_MODE_ADHOC;
David S. Millerccc58052008-06-16 18:50:49 -07005005 current_ev = iwe_stream_add_event(info, current_ev, end_buf,
5006 &iwe, IW_EV_UINT_LEN);
Christoph Hellwig95dd91f2005-06-19 01:27:56 +02005007 }
5008
Dan Williams1e3428e2007-10-10 23:56:25 -04005009 channel = bss->s.channel;
5010 if ((channel >= 1) && (channel <= NUM_CHANNELS)) {
David Kilroy17a1a882008-08-21 23:27:47 +01005011 /* Add channel and frequency */
Dan Williams1e3428e2007-10-10 23:56:25 -04005012 iwe.cmd = SIOCGIWFREQ;
David Kilroy17a1a882008-08-21 23:27:47 +01005013 iwe.u.freq.m = channel;
5014 iwe.u.freq.e = 0;
5015 current_ev = iwe_stream_add_event(info, current_ev, end_buf,
5016 &iwe, IW_EV_FREQ_LEN);
5017
David Kilroy9ee677c2008-12-23 14:03:38 +00005018 iwe.u.freq.m = ieee80211_dsss_chan_to_freq(channel) * 100000;
Dan Williams1e3428e2007-10-10 23:56:25 -04005019 iwe.u.freq.e = 1;
David S. Millerccc58052008-06-16 18:50:49 -07005020 current_ev = iwe_stream_add_event(info, current_ev, end_buf,
Dan Williams1e3428e2007-10-10 23:56:25 -04005021 &iwe, IW_EV_FREQ_LEN);
Christoph Hellwig95dd91f2005-06-19 01:27:56 +02005022 }
5023
David Kilroy17a1a882008-08-21 23:27:47 +01005024 /* Add quality statistics. level and noise in dB. No link quality */
Dan Williams1e3428e2007-10-10 23:56:25 -04005025 iwe.cmd = IWEVQUAL;
David Kilroy17a1a882008-08-21 23:27:47 +01005026 iwe.u.qual.updated = IW_QUAL_DBM | IW_QUAL_QUAL_INVALID;
Dan Williams1e3428e2007-10-10 23:56:25 -04005027 iwe.u.qual.level = (__u8) le16_to_cpu(bss->a.level) - 0x95;
5028 iwe.u.qual.noise = (__u8) le16_to_cpu(bss->a.noise) - 0x95;
5029 /* Wireless tools prior to 27.pre22 will show link quality
5030 * anyway, so we provide a reasonable value. */
5031 if (iwe.u.qual.level > iwe.u.qual.noise)
5032 iwe.u.qual.qual = iwe.u.qual.level - iwe.u.qual.noise;
5033 else
5034 iwe.u.qual.qual = 0;
David S. Millerccc58052008-06-16 18:50:49 -07005035 current_ev = iwe_stream_add_event(info, current_ev, end_buf,
5036 &iwe, IW_EV_QUAL_LEN);
Christoph Hellwig95dd91f2005-06-19 01:27:56 +02005037
Dan Williams1e3428e2007-10-10 23:56:25 -04005038 /* Add encryption capability */
5039 iwe.cmd = SIOCGIWENCODE;
David Kilroy17a1a882008-08-21 23:27:47 +01005040 if (capabilities & WLAN_CAPABILITY_PRIVACY)
Dan Williams1e3428e2007-10-10 23:56:25 -04005041 iwe.u.data.flags = IW_ENCODE_ENABLED | IW_ENCODE_NOKEY;
5042 else
5043 iwe.u.data.flags = IW_ENCODE_DISABLED;
5044 iwe.u.data.length = 0;
David S. Millerccc58052008-06-16 18:50:49 -07005045 current_ev = iwe_stream_add_point(info, current_ev, end_buf,
David Kilroy17a1a882008-08-21 23:27:47 +01005046 &iwe, NULL);
Christoph Hellwig95dd91f2005-06-19 01:27:56 +02005047
Dan Williams1e3428e2007-10-10 23:56:25 -04005048 /* Bit rate is not available in Lucent/Agere firmwares */
5049 if (priv->firmware_type != FIRMWARE_TYPE_AGERE) {
David S. Millerccc58052008-06-16 18:50:49 -07005050 char *current_val = current_ev + iwe_stream_lcp_len(info);
Dan Williams1e3428e2007-10-10 23:56:25 -04005051 int i;
5052 int step;
Christoph Hellwig95dd91f2005-06-19 01:27:56 +02005053
Dan Williams1e3428e2007-10-10 23:56:25 -04005054 if (priv->firmware_type == FIRMWARE_TYPE_SYMBOL)
5055 step = 2;
Christoph Hellwig95dd91f2005-06-19 01:27:56 +02005056 else
Dan Williams1e3428e2007-10-10 23:56:25 -04005057 step = 1;
Christoph Hellwig95dd91f2005-06-19 01:27:56 +02005058
Dan Williams1e3428e2007-10-10 23:56:25 -04005059 iwe.cmd = SIOCGIWRATE;
5060 /* Those two flags are ignored... */
5061 iwe.u.bitrate.fixed = iwe.u.bitrate.disabled = 0;
5062 /* Max 10 values */
5063 for (i = 0; i < 10; i += step) {
5064 /* NULL terminated */
5065 if (bss->p.rates[i] == 0x0)
5066 break;
5067 /* Bit rate given in 500 kb/s units (+ 0x80) */
David Kilroy17a1a882008-08-21 23:27:47 +01005068 iwe.u.bitrate.value =
5069 ((bss->p.rates[i] & 0x7f) * 500000);
David S. Millerccc58052008-06-16 18:50:49 -07005070 current_val = iwe_stream_add_value(info, current_ev,
5071 current_val,
Dan Williams1e3428e2007-10-10 23:56:25 -04005072 end_buf, &iwe,
5073 IW_EV_PARAM_LEN);
Christoph Hellwig95dd91f2005-06-19 01:27:56 +02005074 }
Dan Williams1e3428e2007-10-10 23:56:25 -04005075 /* Check if we added any event */
David S. Millerccc58052008-06-16 18:50:49 -07005076 if ((current_val - current_ev) > iwe_stream_lcp_len(info))
Dan Williams1e3428e2007-10-10 23:56:25 -04005077 current_ev = current_val;
Christoph Hellwig95dd91f2005-06-19 01:27:56 +02005078 }
Dan Williams1e3428e2007-10-10 23:56:25 -04005079
David Kilroy17a1a882008-08-21 23:27:47 +01005080 /* Beacon interval */
5081 iwe.cmd = IWEVCUSTOM;
5082 iwe.u.data.length = snprintf(custom, MAX_CUSTOM_LEN,
5083 "bcn_int=%d",
5084 le16_to_cpu(bss->a.beacon_interv));
5085 if (iwe.u.data.length)
5086 current_ev = iwe_stream_add_point(info, current_ev, end_buf,
5087 &iwe, custom);
5088
5089 /* Capabilites */
5090 iwe.cmd = IWEVCUSTOM;
5091 iwe.u.data.length = snprintf(custom, MAX_CUSTOM_LEN,
5092 "capab=0x%04x",
5093 capabilities);
5094 if (iwe.u.data.length)
5095 current_ev = iwe_stream_add_point(info, current_ev, end_buf,
5096 &iwe, custom);
5097
5098 /* Add EXTRA: Age to display seconds since last beacon/probe response
5099 * for given network. */
5100 iwe.cmd = IWEVCUSTOM;
5101 iwe.u.data.length = snprintf(custom, MAX_CUSTOM_LEN,
5102 " Last beacon: %dms ago",
5103 jiffies_to_msecs(jiffies - last_scanned));
5104 if (iwe.u.data.length)
5105 current_ev = iwe_stream_add_point(info, current_ev, end_buf,
5106 &iwe, custom);
5107
Dan Williams1e3428e2007-10-10 23:56:25 -04005108 return current_ev;
Christoph Hellwig95dd91f2005-06-19 01:27:56 +02005109}
5110
David Kilroy01632fa2008-08-21 23:27:58 +01005111static inline char *orinoco_translate_ext_scan(struct net_device *dev,
5112 struct iw_request_info *info,
5113 char *current_ev,
5114 char *end_buf,
5115 struct agere_ext_scan_info *bss,
Pavel Roskindfe1baf2008-11-10 09:25:53 -05005116 unsigned long last_scanned)
David Kilroy01632fa2008-08-21 23:27:58 +01005117{
5118 u16 capabilities;
5119 u16 channel;
5120 struct iw_event iwe; /* Temporary buffer */
5121 char custom[MAX_CUSTOM_LEN];
5122 u8 *ie;
5123
5124 memset(&iwe, 0, sizeof(iwe));
5125
5126 /* First entry *MUST* be the AP MAC address */
5127 iwe.cmd = SIOCGIWAP;
5128 iwe.u.ap_addr.sa_family = ARPHRD_ETHER;
5129 memcpy(iwe.u.ap_addr.sa_data, bss->bssid, ETH_ALEN);
5130 current_ev = iwe_stream_add_event(info, current_ev, end_buf,
5131 &iwe, IW_EV_ADDR_LEN);
5132
5133 /* Other entries will be displayed in the order we give them */
5134
5135 /* Add the ESSID */
5136 ie = bss->data;
5137 iwe.u.data.length = ie[1];
5138 if (iwe.u.data.length) {
5139 if (iwe.u.data.length > 32)
5140 iwe.u.data.length = 32;
5141 iwe.cmd = SIOCGIWESSID;
5142 iwe.u.data.flags = 1;
5143 current_ev = iwe_stream_add_point(info, current_ev, end_buf,
5144 &iwe, &ie[2]);
5145 }
5146
5147 /* Add mode */
5148 capabilities = le16_to_cpu(bss->capabilities);
5149 if (capabilities & (WLAN_CAPABILITY_ESS | WLAN_CAPABILITY_IBSS)) {
5150 iwe.cmd = SIOCGIWMODE;
5151 if (capabilities & WLAN_CAPABILITY_ESS)
5152 iwe.u.mode = IW_MODE_MASTER;
5153 else
5154 iwe.u.mode = IW_MODE_ADHOC;
5155 current_ev = iwe_stream_add_event(info, current_ev, end_buf,
5156 &iwe, IW_EV_UINT_LEN);
5157 }
5158
Johannes Berg2c7060022008-10-30 22:09:54 +01005159 ie = orinoco_get_ie(bss->data, sizeof(bss->data), WLAN_EID_DS_PARAMS);
David Kilroy01632fa2008-08-21 23:27:58 +01005160 channel = ie ? ie[2] : 0;
5161 if ((channel >= 1) && (channel <= NUM_CHANNELS)) {
5162 /* Add channel and frequency */
5163 iwe.cmd = SIOCGIWFREQ;
5164 iwe.u.freq.m = channel;
5165 iwe.u.freq.e = 0;
5166 current_ev = iwe_stream_add_event(info, current_ev, end_buf,
5167 &iwe, IW_EV_FREQ_LEN);
5168
David Kilroy9ee677c2008-12-23 14:03:38 +00005169 iwe.u.freq.m = ieee80211_dsss_chan_to_freq(channel) * 100000;
David Kilroy01632fa2008-08-21 23:27:58 +01005170 iwe.u.freq.e = 1;
5171 current_ev = iwe_stream_add_event(info, current_ev, end_buf,
5172 &iwe, IW_EV_FREQ_LEN);
5173 }
5174
5175 /* Add quality statistics. level and noise in dB. No link quality */
5176 iwe.cmd = IWEVQUAL;
5177 iwe.u.qual.updated = IW_QUAL_DBM | IW_QUAL_QUAL_INVALID;
5178 iwe.u.qual.level = bss->level - 0x95;
5179 iwe.u.qual.noise = bss->noise - 0x95;
5180 /* Wireless tools prior to 27.pre22 will show link quality
5181 * anyway, so we provide a reasonable value. */
5182 if (iwe.u.qual.level > iwe.u.qual.noise)
5183 iwe.u.qual.qual = iwe.u.qual.level - iwe.u.qual.noise;
5184 else
5185 iwe.u.qual.qual = 0;
5186 current_ev = iwe_stream_add_event(info, current_ev, end_buf,
5187 &iwe, IW_EV_QUAL_LEN);
5188
5189 /* Add encryption capability */
5190 iwe.cmd = SIOCGIWENCODE;
5191 if (capabilities & WLAN_CAPABILITY_PRIVACY)
5192 iwe.u.data.flags = IW_ENCODE_ENABLED | IW_ENCODE_NOKEY;
5193 else
5194 iwe.u.data.flags = IW_ENCODE_DISABLED;
5195 iwe.u.data.length = 0;
5196 current_ev = iwe_stream_add_point(info, current_ev, end_buf,
5197 &iwe, NULL);
5198
5199 /* WPA IE */
5200 ie = orinoco_get_wpa_ie(bss->data, sizeof(bss->data));
5201 if (ie) {
5202 iwe.cmd = IWEVGENIE;
5203 iwe.u.data.length = ie[1] + 2;
5204 current_ev = iwe_stream_add_point(info, current_ev, end_buf,
5205 &iwe, ie);
5206 }
5207
5208 /* RSN IE */
Johannes Berg2c7060022008-10-30 22:09:54 +01005209 ie = orinoco_get_ie(bss->data, sizeof(bss->data), WLAN_EID_RSN);
David Kilroy01632fa2008-08-21 23:27:58 +01005210 if (ie) {
5211 iwe.cmd = IWEVGENIE;
5212 iwe.u.data.length = ie[1] + 2;
5213 current_ev = iwe_stream_add_point(info, current_ev, end_buf,
5214 &iwe, ie);
5215 }
5216
Johannes Berg2c7060022008-10-30 22:09:54 +01005217 ie = orinoco_get_ie(bss->data, sizeof(bss->data), WLAN_EID_SUPP_RATES);
David Kilroy01632fa2008-08-21 23:27:58 +01005218 if (ie) {
5219 char *p = current_ev + iwe_stream_lcp_len(info);
5220 int i;
5221
5222 iwe.cmd = SIOCGIWRATE;
5223 /* Those two flags are ignored... */
5224 iwe.u.bitrate.fixed = iwe.u.bitrate.disabled = 0;
5225
5226 for (i = 2; i < (ie[1] + 2); i++) {
5227 iwe.u.bitrate.value = ((ie[i] & 0x7F) * 500000);
5228 p = iwe_stream_add_value(info, current_ev, p, end_buf,
5229 &iwe, IW_EV_PARAM_LEN);
5230 }
5231 /* Check if we added any event */
5232 if (p > (current_ev + iwe_stream_lcp_len(info)))
5233 current_ev = p;
5234 }
5235
5236 /* Timestamp */
5237 iwe.cmd = IWEVCUSTOM;
David Kilroy75d31cf2008-09-12 22:28:18 +01005238 iwe.u.data.length =
5239 snprintf(custom, MAX_CUSTOM_LEN, "tsf=%016llx",
5240 (unsigned long long) le64_to_cpu(bss->timestamp));
David Kilroy01632fa2008-08-21 23:27:58 +01005241 if (iwe.u.data.length)
5242 current_ev = iwe_stream_add_point(info, current_ev, end_buf,
5243 &iwe, custom);
5244
5245 /* Beacon interval */
5246 iwe.cmd = IWEVCUSTOM;
5247 iwe.u.data.length = snprintf(custom, MAX_CUSTOM_LEN,
5248 "bcn_int=%d",
5249 le16_to_cpu(bss->beacon_interval));
5250 if (iwe.u.data.length)
5251 current_ev = iwe_stream_add_point(info, current_ev, end_buf,
5252 &iwe, custom);
5253
5254 /* Capabilites */
5255 iwe.cmd = IWEVCUSTOM;
5256 iwe.u.data.length = snprintf(custom, MAX_CUSTOM_LEN,
5257 "capab=0x%04x",
5258 capabilities);
5259 if (iwe.u.data.length)
5260 current_ev = iwe_stream_add_point(info, current_ev, end_buf,
5261 &iwe, custom);
5262
5263 /* Add EXTRA: Age to display seconds since last beacon/probe response
5264 * for given network. */
5265 iwe.cmd = IWEVCUSTOM;
5266 iwe.u.data.length = snprintf(custom, MAX_CUSTOM_LEN,
5267 " Last beacon: %dms ago",
5268 jiffies_to_msecs(jiffies - last_scanned));
5269 if (iwe.u.data.length)
5270 current_ev = iwe_stream_add_point(info, current_ev, end_buf,
5271 &iwe, custom);
5272
5273 return current_ev;
5274}
5275
Christoph Hellwig95dd91f2005-06-19 01:27:56 +02005276/* Return results of a scan */
5277static int orinoco_ioctl_getscan(struct net_device *dev,
5278 struct iw_request_info *info,
5279 struct iw_point *srq,
5280 char *extra)
5281{
5282 struct orinoco_private *priv = netdev_priv(dev);
5283 int err = 0;
5284 unsigned long flags;
Dan Williams1e3428e2007-10-10 23:56:25 -04005285 char *current_ev = extra;
Christoph Hellwig95dd91f2005-06-19 01:27:56 +02005286
5287 if (orinoco_lock(priv, &flags) != 0)
5288 return -EBUSY;
5289
Dan Williams1e3428e2007-10-10 23:56:25 -04005290 if (priv->scan_inprogress) {
5291 /* Important note : we don't want to block the caller
5292 * until results are ready for various reasons.
5293 * First, managing wait queues is complex and racy.
5294 * Second, we grab some rtnetlink lock before comming
5295 * here (in dev_ioctl()).
5296 * Third, we generate an Wireless Event, so the
5297 * caller can wait itself on that - Jean II */
5298 err = -EAGAIN;
5299 goto out;
Christoph Hellwig95dd91f2005-06-19 01:27:56 +02005300 }
Dan Williams1e3428e2007-10-10 23:56:25 -04005301
David Kilroy01632fa2008-08-21 23:27:58 +01005302 if (priv->has_ext_scan) {
5303 struct xbss_element *bss;
Dan Williams1e3428e2007-10-10 23:56:25 -04005304
David Kilroy01632fa2008-08-21 23:27:58 +01005305 list_for_each_entry(bss, &priv->bss_list, list) {
5306 /* Translate this entry to WE format */
5307 current_ev =
5308 orinoco_translate_ext_scan(dev, info,
5309 current_ev,
5310 extra + srq->length,
5311 &bss->bss,
5312 bss->last_scanned);
5313
5314 /* Check if there is space for one more entry */
5315 if ((extra + srq->length - current_ev)
5316 <= IW_EV_ADDR_LEN) {
5317 /* Ask user space to try again with a
5318 * bigger buffer */
5319 err = -E2BIG;
5320 goto out;
5321 }
5322 }
5323
5324 } else {
5325 struct bss_element *bss;
5326
5327 list_for_each_entry(bss, &priv->bss_list, list) {
5328 /* Translate this entry to WE format */
5329 current_ev = orinoco_translate_scan(dev, info,
5330 current_ev,
5331 extra + srq->length,
5332 &bss->bss,
5333 bss->last_scanned);
5334
5335 /* Check if there is space for one more entry */
5336 if ((extra + srq->length - current_ev)
5337 <= IW_EV_ADDR_LEN) {
5338 /* Ask user space to try again with a
5339 * bigger buffer */
5340 err = -E2BIG;
5341 goto out;
5342 }
Dan Williams1e3428e2007-10-10 23:56:25 -04005343 }
5344 }
5345
5346 srq->length = (current_ev - extra);
5347 srq->flags = (__u16) priv->scan_mode;
5348
5349out:
Christoph Hellwig95dd91f2005-06-19 01:27:56 +02005350 orinoco_unlock(priv, &flags);
5351 return err;
5352}
5353
Christoph Hellwig620554e2005-06-19 01:27:33 +02005354/* Commit handler, called after set operations */
5355static int orinoco_ioctl_commit(struct net_device *dev,
5356 struct iw_request_info *info,
5357 void *wrqu,
5358 char *extra)
Linus Torvalds1da177e2005-04-16 15:20:36 -07005359{
5360 struct orinoco_private *priv = netdev_priv(dev);
Christoph Hellwig620554e2005-06-19 01:27:33 +02005361 struct hermes *hw = &priv->hw;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005362 unsigned long flags;
Christoph Hellwig620554e2005-06-19 01:27:33 +02005363 int err = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005364
Christoph Hellwig620554e2005-06-19 01:27:33 +02005365 if (!priv->open)
5366 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005367
Christoph Hellwig620554e2005-06-19 01:27:33 +02005368 if (priv->broken_disableport) {
David Howellsc4028952006-11-22 14:57:56 +00005369 orinoco_reset(&priv->reset_work);
Christoph Hellwig620554e2005-06-19 01:27:33 +02005370 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005371 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07005372
Christoph Hellwig620554e2005-06-19 01:27:33 +02005373 if (orinoco_lock(priv, &flags) != 0)
5374 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005375
Christoph Hellwig620554e2005-06-19 01:27:33 +02005376 err = hermes_disable_port(hw, 0);
5377 if (err) {
5378 printk(KERN_WARNING "%s: Unable to disable port "
5379 "while reconfiguring card\n", dev->name);
5380 priv->broken_disableport = 1;
5381 goto out;
5382 }
5383
5384 err = __orinoco_program_rids(dev);
5385 if (err) {
5386 printk(KERN_WARNING "%s: Unable to reconfigure card\n",
5387 dev->name);
5388 goto out;
5389 }
5390
5391 err = hermes_enable_port(hw, 0);
5392 if (err) {
5393 printk(KERN_WARNING "%s: Unable to enable port while reconfiguring card\n",
5394 dev->name);
5395 goto out;
5396 }
5397
5398 out:
5399 if (err) {
5400 printk(KERN_WARNING "%s: Resetting instead...\n", dev->name);
5401 schedule_work(&priv->reset_work);
5402 err = 0;
5403 }
5404
5405 orinoco_unlock(priv, &flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005406 return err;
5407}
5408
Christoph Hellwig620554e2005-06-19 01:27:33 +02005409static const struct iw_priv_args orinoco_privtab[] = {
5410 { SIOCIWFIRSTPRIV + 0x0, 0, 0, "force_reset" },
5411 { SIOCIWFIRSTPRIV + 0x1, 0, 0, "card_reset" },
5412 { SIOCIWFIRSTPRIV + 0x2, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
5413 0, "set_port3" },
5414 { SIOCIWFIRSTPRIV + 0x3, 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
5415 "get_port3" },
5416 { SIOCIWFIRSTPRIV + 0x4, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
5417 0, "set_preamble" },
5418 { SIOCIWFIRSTPRIV + 0x5, 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
5419 "get_preamble" },
5420 { SIOCIWFIRSTPRIV + 0x6, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
5421 0, "set_ibssport" },
5422 { SIOCIWFIRSTPRIV + 0x7, 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
5423 "get_ibssport" },
5424 { SIOCIWFIRSTPRIV + 0x9, 0, IW_PRIV_TYPE_BYTE | MAX_RID_LEN,
5425 "get_rid" },
5426};
5427
5428
5429/*
5430 * Structures to export the Wireless Handlers
5431 */
5432
David Kilroy409644a2008-08-21 23:28:01 +01005433#define STD_IW_HANDLER(id, func) \
5434 [IW_IOCTL_IDX(id)] = (iw_handler) func
Christoph Hellwig620554e2005-06-19 01:27:33 +02005435static const iw_handler orinoco_handler[] = {
David Kilroy409644a2008-08-21 23:28:01 +01005436 STD_IW_HANDLER(SIOCSIWCOMMIT, orinoco_ioctl_commit),
5437 STD_IW_HANDLER(SIOCGIWNAME, orinoco_ioctl_getname),
5438 STD_IW_HANDLER(SIOCSIWFREQ, orinoco_ioctl_setfreq),
5439 STD_IW_HANDLER(SIOCGIWFREQ, orinoco_ioctl_getfreq),
5440 STD_IW_HANDLER(SIOCSIWMODE, orinoco_ioctl_setmode),
5441 STD_IW_HANDLER(SIOCGIWMODE, orinoco_ioctl_getmode),
5442 STD_IW_HANDLER(SIOCSIWSENS, orinoco_ioctl_setsens),
5443 STD_IW_HANDLER(SIOCGIWSENS, orinoco_ioctl_getsens),
5444 STD_IW_HANDLER(SIOCGIWRANGE, orinoco_ioctl_getiwrange),
5445 STD_IW_HANDLER(SIOCSIWSPY, iw_handler_set_spy),
5446 STD_IW_HANDLER(SIOCGIWSPY, iw_handler_get_spy),
5447 STD_IW_HANDLER(SIOCSIWTHRSPY, iw_handler_set_thrspy),
5448 STD_IW_HANDLER(SIOCGIWTHRSPY, iw_handler_get_thrspy),
5449 STD_IW_HANDLER(SIOCSIWAP, orinoco_ioctl_setwap),
5450 STD_IW_HANDLER(SIOCGIWAP, orinoco_ioctl_getwap),
5451 STD_IW_HANDLER(SIOCSIWSCAN, orinoco_ioctl_setscan),
5452 STD_IW_HANDLER(SIOCGIWSCAN, orinoco_ioctl_getscan),
5453 STD_IW_HANDLER(SIOCSIWESSID, orinoco_ioctl_setessid),
5454 STD_IW_HANDLER(SIOCGIWESSID, orinoco_ioctl_getessid),
5455 STD_IW_HANDLER(SIOCSIWNICKN, orinoco_ioctl_setnick),
5456 STD_IW_HANDLER(SIOCGIWNICKN, orinoco_ioctl_getnick),
5457 STD_IW_HANDLER(SIOCSIWRATE, orinoco_ioctl_setrate),
5458 STD_IW_HANDLER(SIOCGIWRATE, orinoco_ioctl_getrate),
5459 STD_IW_HANDLER(SIOCSIWRTS, orinoco_ioctl_setrts),
5460 STD_IW_HANDLER(SIOCGIWRTS, orinoco_ioctl_getrts),
5461 STD_IW_HANDLER(SIOCSIWFRAG, orinoco_ioctl_setfrag),
5462 STD_IW_HANDLER(SIOCGIWFRAG, orinoco_ioctl_getfrag),
5463 STD_IW_HANDLER(SIOCGIWRETRY, orinoco_ioctl_getretry),
5464 STD_IW_HANDLER(SIOCSIWENCODE, orinoco_ioctl_setiwencode),
5465 STD_IW_HANDLER(SIOCGIWENCODE, orinoco_ioctl_getiwencode),
5466 STD_IW_HANDLER(SIOCSIWPOWER, orinoco_ioctl_setpower),
5467 STD_IW_HANDLER(SIOCGIWPOWER, orinoco_ioctl_getpower),
David Kilroyd03032a2008-08-21 23:28:02 +01005468 STD_IW_HANDLER(SIOCSIWGENIE, orinoco_ioctl_set_genie),
5469 STD_IW_HANDLER(SIOCGIWGENIE, orinoco_ioctl_get_genie),
5470 STD_IW_HANDLER(SIOCSIWMLME, orinoco_ioctl_set_mlme),
5471 STD_IW_HANDLER(SIOCSIWAUTH, orinoco_ioctl_set_auth),
5472 STD_IW_HANDLER(SIOCGIWAUTH, orinoco_ioctl_get_auth),
5473 STD_IW_HANDLER(SIOCSIWENCODEEXT, orinoco_ioctl_set_encodeext),
5474 STD_IW_HANDLER(SIOCGIWENCODEEXT, orinoco_ioctl_get_encodeext),
Christoph Hellwig620554e2005-06-19 01:27:33 +02005475};
5476
5477
5478/*
5479 Added typecasting since we no longer use iwreq_data -- Moustafa
5480 */
5481static const iw_handler orinoco_private_handler[] = {
Peter Hagervall6b9b97c2005-07-27 01:14:46 -07005482 [0] = (iw_handler) orinoco_ioctl_reset,
5483 [1] = (iw_handler) orinoco_ioctl_reset,
5484 [2] = (iw_handler) orinoco_ioctl_setport3,
5485 [3] = (iw_handler) orinoco_ioctl_getport3,
5486 [4] = (iw_handler) orinoco_ioctl_setpreamble,
5487 [5] = (iw_handler) orinoco_ioctl_getpreamble,
5488 [6] = (iw_handler) orinoco_ioctl_setibssport,
5489 [7] = (iw_handler) orinoco_ioctl_getibssport,
5490 [9] = (iw_handler) orinoco_ioctl_getrid,
Christoph Hellwig620554e2005-06-19 01:27:33 +02005491};
5492
5493static const struct iw_handler_def orinoco_handler_def = {
5494 .num_standard = ARRAY_SIZE(orinoco_handler),
5495 .num_private = ARRAY_SIZE(orinoco_private_handler),
5496 .num_private_args = ARRAY_SIZE(orinoco_privtab),
5497 .standard = orinoco_handler,
5498 .private = orinoco_private_handler,
5499 .private_args = orinoco_privtab,
Pavel Roskin343c6862005-09-09 18:43:02 -04005500 .get_wireless_stats = orinoco_get_wireless_stats,
Christoph Hellwig620554e2005-06-19 01:27:33 +02005501};
Linus Torvalds1da177e2005-04-16 15:20:36 -07005502
Christoph Hellwig1fab2e82005-06-19 01:27:40 +02005503static void orinoco_get_drvinfo(struct net_device *dev,
5504 struct ethtool_drvinfo *info)
5505{
5506 struct orinoco_private *priv = netdev_priv(dev);
5507
5508 strncpy(info->driver, DRIVER_NAME, sizeof(info->driver) - 1);
5509 strncpy(info->version, DRIVER_VERSION, sizeof(info->version) - 1);
5510 strncpy(info->fw_version, priv->fw_name, sizeof(info->fw_version) - 1);
Greg Kroah-Hartman43cb76d2002-04-09 12:14:34 -07005511 if (dev->dev.parent)
Kay Sieversfb28ad32008-11-10 13:55:14 -08005512 strncpy(info->bus_info, dev_name(dev->dev.parent),
Christoph Hellwig1fab2e82005-06-19 01:27:40 +02005513 sizeof(info->bus_info) - 1);
5514 else
5515 snprintf(info->bus_info, sizeof(info->bus_info) - 1,
5516 "PCMCIA %p", priv->hw.iobase);
5517}
5518
Jeff Garzik7282d492006-09-13 14:30:00 -04005519static const struct ethtool_ops orinoco_ethtool_ops = {
Christoph Hellwig1fab2e82005-06-19 01:27:40 +02005520 .get_drvinfo = orinoco_get_drvinfo,
5521 .get_link = ethtool_op_get_link,
5522};
Linus Torvalds1da177e2005-04-16 15:20:36 -07005523
5524/********************************************************************/
Linus Torvalds1da177e2005-04-16 15:20:36 -07005525/* Module initialization */
5526/********************************************************************/
5527
Linus Torvalds1da177e2005-04-16 15:20:36 -07005528/* Can't be declared "const" or the whole __initdata section will
5529 * become const */
5530static char version[] __initdata = DRIVER_NAME " " DRIVER_VERSION
5531 " (David Gibson <hermes@gibson.dropbear.id.au>, "
5532 "Pavel Roskin <proski@gnu.org>, et al)";
5533
5534static int __init init_orinoco(void)
5535{
5536 printk(KERN_DEBUG "%s\n", version);
5537 return 0;
5538}
5539
5540static void __exit exit_orinoco(void)
5541{
5542}
5543
5544module_init(init_orinoco);
5545module_exit(exit_orinoco);