blob: 3e20df7b65709a7bc0bf21a97f608fa3058ff99a [file] [log] [blame]
David Kilroy47445cb2009-02-04 23:05:48 +00001/* main.c - (formerly known as dldwd_cs.c, orinoco_cs.c and orinoco.c)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002 *
3 * A driver for Hermes or Prism 2 chipset based PCMCIA wireless
4 * adaptors, with Lucent/Agere, Intersil or Symbol firmware.
5 *
6 * Current maintainers (as of 29 September 2003) are:
7 * Pavel Roskin <proski AT gnu.org>
8 * and David Gibson <hermes AT gibson.dropbear.id.au>
9 *
10 * (C) Copyright David Gibson, IBM Corporation 2001-2003.
11 * Copyright (C) 2000 David Gibson, Linuxcare Australia.
12 * With some help from :
13 * Copyright (C) 2001 Jean Tourrilhes, HP Labs
14 * Copyright (C) 2001 Benjamin Herrenschmidt
15 *
16 * Based on dummy_cs.c 1.27 2000/06/12 21:27:25
17 *
18 * Portions based on wvlan_cs.c 1.0.6, Copyright Andreas Neuhaus <andy
19 * AT fasta.fh-dortmund.de>
20 * http://www.stud.fh-dortmund.de/~andy/wvlan/
21 *
22 * The contents of this file are subject to the Mozilla Public License
23 * Version 1.1 (the "License"); you may not use this file except in
24 * compliance with the License. You may obtain a copy of the License
25 * at http://www.mozilla.org/MPL/
26 *
27 * Software distributed under the License is distributed on an "AS IS"
28 * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
29 * the License for the specific language governing rights and
30 * limitations under the License.
31 *
32 * The initial developer of the original code is David A. Hinds
33 * <dahinds AT users.sourceforge.net>. Portions created by David
34 * A. Hinds are Copyright (C) 1999 David A. Hinds. All Rights
35 * Reserved.
36 *
37 * Alternatively, the contents of this file may be used under the
38 * terms of the GNU General Public License version 2 (the "GPL"), in
39 * which case the provisions of the GPL are applicable instead of the
40 * above. If you wish to allow the use of your version of this file
41 * only under the terms of the GPL and not to allow others to use your
42 * version of this file under the MPL, indicate your decision by
43 * deleting the provisions above and replace them with the notice and
44 * other provisions required by the GPL. If you do not delete the
45 * provisions above, a recipient may use your version of this file
46 * under either the MPL or the GPL. */
47
48/*
Linus Torvalds1da177e2005-04-16 15:20:36 -070049 * TODO
Linus Torvalds1da177e2005-04-16 15:20:36 -070050 * o Handle de-encapsulation within network layer, provide 802.11
51 * headers (patch from Thomas 'Dent' Mirlacher)
Linus Torvalds1da177e2005-04-16 15:20:36 -070052 * o Fix possible races in SPY handling.
53 * o Disconnect wireless extensions from fundamental configuration.
54 * o (maybe) Software WEP support (patch from Stano Meduna).
55 * o (maybe) Use multiple Tx buffers - driver handling queue
56 * rather than firmware.
57 */
58
59/* Locking and synchronization:
60 *
61 * The basic principle is that everything is serialized through a
62 * single spinlock, priv->lock. The lock is used in user, bh and irq
63 * context, so when taken outside hardirq context it should always be
64 * taken with interrupts disabled. The lock protects both the
65 * hardware and the struct orinoco_private.
66 *
67 * Another flag, priv->hw_unavailable indicates that the hardware is
68 * unavailable for an extended period of time (e.g. suspended, or in
69 * the middle of a hard reset). This flag is protected by the
70 * spinlock. All code which touches the hardware should check the
71 * flag after taking the lock, and if it is set, give up on whatever
72 * they are doing and drop the lock again. The orinoco_lock()
73 * function handles this (it unlocks and returns -EBUSY if
74 * hw_unavailable is non-zero).
75 */
76
77#define DRIVER_NAME "orinoco"
78
Linus Torvalds1da177e2005-04-16 15:20:36 -070079#include <linux/module.h>
80#include <linux/kernel.h>
81#include <linux/init.h>
David Kilroyd03032a2008-08-21 23:28:02 +010082#include <linux/delay.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070083#include <linux/netdevice.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070084#include <linux/etherdevice.h>
Christoph Hellwig1fab2e82005-06-19 01:27:40 +020085#include <linux/ethtool.h>
David Kilroy39d1ffe2008-11-22 10:37:28 +000086#include <linux/suspend.h>
Pavel Roskin9c974fb2006-08-15 20:45:03 -040087#include <linux/if_arp.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070088#include <linux/wireless.h>
Johannes Berg2c7060022008-10-30 22:09:54 +010089#include <linux/ieee80211.h>
Christoph Hellwig620554e2005-06-19 01:27:33 +020090#include <net/iw_handler.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070091
Linus Torvalds1da177e2005-04-16 15:20:36 -070092#include "hermes_rid.h"
David Kilroy3994d502008-08-21 23:27:54 +010093#include "hermes_dld.h"
David Kilroyfb791b12009-02-04 23:05:50 +000094#include "scan.h"
David Kilroy4adb4742009-02-04 23:05:51 +000095#include "mic.h"
David Kilroy37a2e562009-02-04 23:05:52 +000096#include "fw.h"
David Kilroyfb791b12009-02-04 23:05:50 +000097
Linus Torvalds1da177e2005-04-16 15:20:36 -070098#include "orinoco.h"
Linus Torvalds1da177e2005-04-16 15:20:36 -070099
100/********************************************************************/
101/* Module information */
102/********************************************************************/
103
David Kilroyb2f30a02009-02-04 23:05:46 +0000104MODULE_AUTHOR("Pavel Roskin <proski@gnu.org> & "
105 "David Gibson <hermes@gibson.dropbear.id.au>");
106MODULE_DESCRIPTION("Driver for Lucent Orinoco, Prism II based "
107 "and similar wireless cards");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700108MODULE_LICENSE("Dual MPL/GPL");
109
110/* Level of debugging. Used in the macros in orinoco.h */
111#ifdef ORINOCO_DEBUG
112int orinoco_debug = ORINOCO_DEBUG;
David Kilroy21312662009-02-04 23:05:47 +0000113EXPORT_SYMBOL(orinoco_debug);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700114module_param(orinoco_debug, int, 0644);
115MODULE_PARM_DESC(orinoco_debug, "Debug level");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700116#endif
117
118static int suppress_linkstatus; /* = 0 */
119module_param(suppress_linkstatus, bool, 0644);
120MODULE_PARM_DESC(suppress_linkstatus, "Don't log link status changes");
David Kilroyb2f30a02009-02-04 23:05:46 +0000121
David Gibson7bb7c3a2005-05-12 20:02:10 -0400122static int ignore_disconnect; /* = 0 */
123module_param(ignore_disconnect, int, 0644);
David Kilroyb2f30a02009-02-04 23:05:46 +0000124MODULE_PARM_DESC(ignore_disconnect,
125 "Don't report lost link to the network layer");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700126
Christoph Hellwig98c4cae2005-06-19 01:28:06 +0200127static int force_monitor; /* = 0 */
128module_param(force_monitor, int, 0644);
129MODULE_PARM_DESC(force_monitor, "Allow monitor mode for all firmware versions");
130
Linus Torvalds1da177e2005-04-16 15:20:36 -0700131/********************************************************************/
132/* Compile time configuration and compatibility stuff */
133/********************************************************************/
134
135/* We do this this way to avoid ifdefs in the actual code */
136#ifdef WIRELESS_SPY
Pavel Roskin343c6862005-09-09 18:43:02 -0400137#define SPY_NUMBER(priv) (priv->spy_data.spy_number)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700138#else
139#define SPY_NUMBER(priv) 0
140#endif /* WIRELESS_SPY */
141
142/********************************************************************/
143/* Internal constants */
144/********************************************************************/
145
Christoph Hellwig95dd91f2005-06-19 01:27:56 +0200146/* 802.2 LLC/SNAP header used for Ethernet encapsulation over 802.11 */
147static const u8 encaps_hdr[] = {0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00};
148#define ENCAPS_OVERHEAD (sizeof(encaps_hdr) + 2)
149
Linus Torvalds1da177e2005-04-16 15:20:36 -0700150#define ORINOCO_MIN_MTU 256
Johannes Berg2c7060022008-10-30 22:09:54 +0100151#define ORINOCO_MAX_MTU (IEEE80211_MAX_DATA_LEN - ENCAPS_OVERHEAD)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700152
153#define SYMBOL_MAX_VER_LEN (14)
154#define USER_BAP 0
155#define IRQ_BAP 1
156#define MAX_IRQLOOPS_PER_IRQ 10
157#define MAX_IRQLOOPS_PER_JIFFY (20000/HZ) /* Based on a guestimate of
158 * how many events the
159 * device could
160 * legitimately generate */
161#define SMALL_KEY_SIZE 5
162#define LARGE_KEY_SIZE 13
163#define TX_NICBUF_SIZE_BUG 1585 /* Bug in Symbol firmware */
164
165#define DUMMY_FID 0xFFFF
166
167/*#define MAX_MULTICAST(priv) (priv->firmware_type == FIRMWARE_TYPE_AGERE ? \
168 HERMES_MAX_MULTICAST : 0)*/
169#define MAX_MULTICAST(priv) (HERMES_MAX_MULTICAST)
170
171#define ORINOCO_INTEN (HERMES_EV_RX | HERMES_EV_ALLOC \
172 | HERMES_EV_TX | HERMES_EV_TXEXC \
173 | HERMES_EV_WTERR | HERMES_EV_INFO \
David Kilroya94e8422009-02-04 23:05:44 +0000174 | HERMES_EV_INFDROP)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700175
Christoph Hellwig620554e2005-06-19 01:27:33 +0200176#define MAX_RID_LEN 1024
177
178static const struct iw_handler_def orinoco_handler_def;
Jeff Garzik7282d492006-09-13 14:30:00 -0400179static const struct ethtool_ops orinoco_ethtool_ops;
Christoph Hellwig620554e2005-06-19 01:27:33 +0200180
Linus Torvalds1da177e2005-04-16 15:20:36 -0700181/********************************************************************/
182/* Data tables */
183/********************************************************************/
184
David Kilroy9ee677c2008-12-23 14:03:38 +0000185#define NUM_CHANNELS 14
Linus Torvalds1da177e2005-04-16 15:20:36 -0700186
187/* This tables gives the actual meanings of the bitrate IDs returned
188 * by the firmware. */
189static struct {
190 int bitrate; /* in 100s of kilobits */
191 int automatic;
192 u16 agere_txratectrl;
193 u16 intersil_txratectrl;
194} bitrate_table[] = {
195 {110, 1, 3, 15}, /* Entry 0 is the default */
196 {10, 0, 1, 1},
197 {10, 1, 1, 1},
198 {20, 0, 2, 2},
199 {20, 1, 6, 3},
200 {55, 0, 4, 4},
201 {55, 1, 7, 7},
202 {110, 0, 5, 8},
203};
204#define BITRATE_TABLE_SIZE ARRAY_SIZE(bitrate_table)
205
206/********************************************************************/
207/* Data types */
208/********************************************************************/
209
Pavel Roskin30c2d3b2006-04-07 04:10:34 -0400210/* Beginning of the Tx descriptor, used in TxExc handling */
211struct hermes_txexc_data {
212 struct hermes_tx_descriptor desc;
Pavel Roskind133ae42005-09-23 04:18:06 -0400213 __le16 frame_ctl;
214 __le16 duration_id;
Christoph Hellwig95dd91f2005-06-19 01:27:56 +0200215 u8 addr1[ETH_ALEN];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700216} __attribute__ ((packed));
217
Christoph Hellwig8f2abf42005-06-19 01:28:02 +0200218/* Rx frame header except compatibility 802.3 header */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700219struct hermes_rx_descriptor {
Christoph Hellwig8f2abf42005-06-19 01:28:02 +0200220 /* Control */
Pavel Roskind133ae42005-09-23 04:18:06 -0400221 __le16 status;
222 __le32 time;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700223 u8 silence;
224 u8 signal;
225 u8 rate;
226 u8 rxflow;
Pavel Roskind133ae42005-09-23 04:18:06 -0400227 __le32 reserved;
Christoph Hellwig8f2abf42005-06-19 01:28:02 +0200228
229 /* 802.11 header */
Pavel Roskind133ae42005-09-23 04:18:06 -0400230 __le16 frame_ctl;
231 __le16 duration_id;
Christoph Hellwig8f2abf42005-06-19 01:28:02 +0200232 u8 addr1[ETH_ALEN];
233 u8 addr2[ETH_ALEN];
234 u8 addr3[ETH_ALEN];
Pavel Roskind133ae42005-09-23 04:18:06 -0400235 __le16 seq_ctl;
Christoph Hellwig8f2abf42005-06-19 01:28:02 +0200236 u8 addr4[ETH_ALEN];
237
238 /* Data length */
Pavel Roskind133ae42005-09-23 04:18:06 -0400239 __le16 data_len;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700240} __attribute__ ((packed));
241
David Kilroy47166792009-01-07 00:43:54 +0000242struct orinoco_rx_data {
243 struct hermes_rx_descriptor *desc;
244 struct sk_buff *skb;
245 struct list_head list;
246};
247
Linus Torvalds1da177e2005-04-16 15:20:36 -0700248/********************************************************************/
249/* Function prototypes */
250/********************************************************************/
251
Linus Torvalds1da177e2005-04-16 15:20:36 -0700252static int __orinoco_program_rids(struct net_device *dev);
253static void __orinoco_set_multicast_list(struct net_device *dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700254
255/********************************************************************/
256/* Internal helper functions */
257/********************************************************************/
258
259static inline void set_port_type(struct orinoco_private *priv)
260{
261 switch (priv->iw_mode) {
262 case IW_MODE_INFRA:
263 priv->port_type = 1;
264 priv->createibss = 0;
265 break;
266 case IW_MODE_ADHOC:
267 if (priv->prefer_port3) {
268 priv->port_type = 3;
269 priv->createibss = 0;
270 } else {
271 priv->port_type = priv->ibss_port;
272 priv->createibss = 1;
273 }
274 break;
Christoph Hellwig98c4cae2005-06-19 01:28:06 +0200275 case IW_MODE_MONITOR:
276 priv->port_type = 3;
277 priv->createibss = 0;
278 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700279 default:
280 printk(KERN_ERR "%s: Invalid priv->iw_mode in set_port_type()\n",
281 priv->ndev->name);
282 }
283}
284
David Kilroycfeb1db2009-02-04 23:05:53 +0000285static int orinoco_get_bitratemode(int bitrate, int automatic)
286{
287 int ratemode = -1;
288 int i;
289
290 if ((bitrate != 10) && (bitrate != 20) &&
291 (bitrate != 55) && (bitrate != 110))
292 return ratemode;
293
294 for (i = 0; i < BITRATE_TABLE_SIZE; i++) {
295 if ((bitrate_table[i].bitrate == bitrate) &&
296 (bitrate_table[i].automatic == automatic)) {
297 ratemode = i;
298 break;
299 }
300 }
301 return ratemode;
302}
303
304static void orinoco_get_ratemode_cfg(int ratemode, int *bitrate, int *automatic)
305{
306 BUG_ON((ratemode < 0) || (ratemode >= BITRATE_TABLE_SIZE));
307
308 *bitrate = bitrate_table[ratemode].bitrate * 100000;
309 *automatic = bitrate_table[ratemode].automatic;
310}
311
David Kilroy01632fa2008-08-21 23:27:58 +0100312static inline u8 *orinoco_get_ie(u8 *data, size_t len,
Johannes Berg2c7060022008-10-30 22:09:54 +0100313 enum ieee80211_eid eid)
David Kilroy01632fa2008-08-21 23:27:58 +0100314{
315 u8 *p = data;
316 while ((p + 2) < (data + len)) {
317 if (p[0] == eid)
318 return p;
319 p += p[1] + 2;
320 }
321 return NULL;
322}
323
324#define WPA_OUI_TYPE "\x00\x50\xF2\x01"
325#define WPA_SELECTOR_LEN 4
326static inline u8 *orinoco_get_wpa_ie(u8 *data, size_t len)
327{
328 u8 *p = data;
329 while ((p + 2 + WPA_SELECTOR_LEN) < (data + len)) {
Johannes Berg2c7060022008-10-30 22:09:54 +0100330 if ((p[0] == WLAN_EID_GENERIC) &&
David Kilroy01632fa2008-08-21 23:27:58 +0100331 (memcmp(&p[2], WPA_OUI_TYPE, WPA_SELECTOR_LEN) == 0))
332 return p;
333 p += p[1] + 2;
334 }
335 return NULL;
Dan Williams1e3428e2007-10-10 23:56:25 -0400336}
337
David Kilroy3994d502008-08-21 23:27:54 +0100338
339/********************************************************************/
Linus Torvalds1da177e2005-04-16 15:20:36 -0700340/* Device methods */
341/********************************************************************/
342
343static int orinoco_open(struct net_device *dev)
344{
345 struct orinoco_private *priv = netdev_priv(dev);
346 unsigned long flags;
347 int err;
348
349 if (orinoco_lock(priv, &flags) != 0)
350 return -EBUSY;
351
352 err = __orinoco_up(dev);
353
David Kilroya94e8422009-02-04 23:05:44 +0000354 if (!err)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700355 priv->open = 1;
356
357 orinoco_unlock(priv, &flags);
358
359 return err;
360}
361
Christoph Hellwigad8f4512005-05-14 17:30:17 +0200362static int orinoco_stop(struct net_device *dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700363{
364 struct orinoco_private *priv = netdev_priv(dev);
365 int err = 0;
366
367 /* We mustn't use orinoco_lock() here, because we need to be
368 able to close the interface even if hw_unavailable is set
369 (e.g. as we're released after a PC Card removal) */
370 spin_lock_irq(&priv->lock);
371
372 priv->open = 0;
373
374 err = __orinoco_down(dev);
375
376 spin_unlock_irq(&priv->lock);
377
378 return err;
379}
380
381static struct net_device_stats *orinoco_get_stats(struct net_device *dev)
382{
383 struct orinoco_private *priv = netdev_priv(dev);
David Kilroy6fe9deb2009-02-04 23:05:43 +0000384
Linus Torvalds1da177e2005-04-16 15:20:36 -0700385 return &priv->stats;
386}
387
388static struct iw_statistics *orinoco_get_wireless_stats(struct net_device *dev)
389{
390 struct orinoco_private *priv = netdev_priv(dev);
391 hermes_t *hw = &priv->hw;
392 struct iw_statistics *wstats = &priv->wstats;
David Gibsone67d9d92005-05-12 20:01:22 -0400393 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700394 unsigned long flags;
395
David Kilroya94e8422009-02-04 23:05:44 +0000396 if (!netif_device_present(dev)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700397 printk(KERN_WARNING "%s: get_wireless_stats() called while device not present\n",
398 dev->name);
399 return NULL; /* FIXME: Can we do better than this? */
400 }
401
David Gibsone67d9d92005-05-12 20:01:22 -0400402 /* If busy, return the old stats. Returning NULL may cause
403 * the interface to disappear from /proc/net/wireless */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700404 if (orinoco_lock(priv, &flags) != 0)
David Gibsone67d9d92005-05-12 20:01:22 -0400405 return wstats;
406
407 /* We can't really wait for the tallies inquiry command to
408 * complete, so we just use the previous results and trigger
409 * a new tallies inquiry command for next time - Jean II */
410 /* FIXME: Really we should wait for the inquiry to come back -
411 * as it is the stats we give don't make a whole lot of sense.
412 * Unfortunately, it's not clear how to do that within the
413 * wireless extensions framework: I think we're in user
414 * context, but a lock seems to be held by the time we get in
415 * here so we're not safe to sleep here. */
416 hermes_inquire(hw, HERMES_INQ_TALLIES);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700417
418 if (priv->iw_mode == IW_MODE_ADHOC) {
419 memset(&wstats->qual, 0, sizeof(wstats->qual));
420 /* If a spy address is defined, we report stats of the
421 * first spy address - Jean II */
422 if (SPY_NUMBER(priv)) {
Pavel Roskin343c6862005-09-09 18:43:02 -0400423 wstats->qual.qual = priv->spy_data.spy_stat[0].qual;
424 wstats->qual.level = priv->spy_data.spy_stat[0].level;
425 wstats->qual.noise = priv->spy_data.spy_stat[0].noise;
David Kilroyb2f30a02009-02-04 23:05:46 +0000426 wstats->qual.updated =
427 priv->spy_data.spy_stat[0].updated;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700428 }
429 } else {
430 struct {
Pavel Roskina208c4e2006-04-07 04:10:26 -0400431 __le16 qual, signal, noise, unused;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700432 } __attribute__ ((packed)) cq;
433
434 err = HERMES_READ_RECORD(hw, USER_BAP,
435 HERMES_RID_COMMSQUALITY, &cq);
David Gibsone67d9d92005-05-12 20:01:22 -0400436
437 if (!err) {
438 wstats->qual.qual = (int)le16_to_cpu(cq.qual);
439 wstats->qual.level = (int)le16_to_cpu(cq.signal) - 0x95;
440 wstats->qual.noise = (int)le16_to_cpu(cq.noise) - 0x95;
David Kilroyb2f30a02009-02-04 23:05:46 +0000441 wstats->qual.updated =
442 IW_QUAL_ALL_UPDATED | IW_QUAL_DBM;
David Gibsone67d9d92005-05-12 20:01:22 -0400443 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700444 }
445
Linus Torvalds1da177e2005-04-16 15:20:36 -0700446 orinoco_unlock(priv, &flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700447 return wstats;
448}
449
450static void orinoco_set_multicast_list(struct net_device *dev)
451{
452 struct orinoco_private *priv = netdev_priv(dev);
453 unsigned long flags;
454
455 if (orinoco_lock(priv, &flags) != 0) {
456 printk(KERN_DEBUG "%s: orinoco_set_multicast_list() "
457 "called when hw_unavailable\n", dev->name);
458 return;
459 }
460
461 __orinoco_set_multicast_list(dev);
462 orinoco_unlock(priv, &flags);
463}
464
465static int orinoco_change_mtu(struct net_device *dev, int new_mtu)
466{
467 struct orinoco_private *priv = netdev_priv(dev);
468
David Kilroya94e8422009-02-04 23:05:44 +0000469 if ((new_mtu < ORINOCO_MIN_MTU) || (new_mtu > ORINOCO_MAX_MTU))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700470 return -EINVAL;
471
Johannes Berg2c7060022008-10-30 22:09:54 +0100472 /* MTU + encapsulation + header length */
David Kilroya94e8422009-02-04 23:05:44 +0000473 if ((new_mtu + ENCAPS_OVERHEAD + sizeof(struct ieee80211_hdr)) >
474 (priv->nicbuf_size - ETH_HLEN))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700475 return -EINVAL;
476
477 dev->mtu = new_mtu;
478
479 return 0;
480}
481
482/********************************************************************/
483/* Tx path */
484/********************************************************************/
485
486static int orinoco_xmit(struct sk_buff *skb, struct net_device *dev)
487{
488 struct orinoco_private *priv = netdev_priv(dev);
489 struct net_device_stats *stats = &priv->stats;
490 hermes_t *hw = &priv->hw;
491 int err = 0;
492 u16 txfid = priv->txfid;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700493 struct ethhdr *eh;
David Kilroy6eecad72008-08-21 23:27:56 +0100494 int tx_control;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700495 unsigned long flags;
496
David Kilroya94e8422009-02-04 23:05:44 +0000497 if (!netif_running(dev)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700498 printk(KERN_ERR "%s: Tx on stopped device!\n",
499 dev->name);
Pavel Roskinb34b8672006-04-07 04:10:36 -0400500 return NETDEV_TX_BUSY;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700501 }
David Kilroy6fe9deb2009-02-04 23:05:43 +0000502
Linus Torvalds1da177e2005-04-16 15:20:36 -0700503 if (netif_queue_stopped(dev)) {
David Kilroy6fe9deb2009-02-04 23:05:43 +0000504 printk(KERN_DEBUG "%s: Tx while transmitter busy!\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700505 dev->name);
Pavel Roskinb34b8672006-04-07 04:10:36 -0400506 return NETDEV_TX_BUSY;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700507 }
David Kilroy6fe9deb2009-02-04 23:05:43 +0000508
Linus Torvalds1da177e2005-04-16 15:20:36 -0700509 if (orinoco_lock(priv, &flags) != 0) {
510 printk(KERN_ERR "%s: orinoco_xmit() called while hw_unavailable\n",
511 dev->name);
Pavel Roskinb34b8672006-04-07 04:10:36 -0400512 return NETDEV_TX_BUSY;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700513 }
514
David Kilroya94e8422009-02-04 23:05:44 +0000515 if (!netif_carrier_ok(dev) || (priv->iw_mode == IW_MODE_MONITOR)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700516 /* Oops, the firmware hasn't established a connection,
David Kilroy6fe9deb2009-02-04 23:05:43 +0000517 silently drop the packet (this seems to be the
518 safest approach). */
Pavel Roskin470e2aa2006-04-07 04:10:43 -0400519 goto drop;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700520 }
521
Pavel Roskin8d5be082006-04-07 04:10:41 -0400522 /* Check packet length */
Pavel Roskina28dc812006-04-07 04:10:45 -0400523 if (skb->len < ETH_HLEN)
Pavel Roskin470e2aa2006-04-07 04:10:43 -0400524 goto drop;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700525
David Kilroy6eecad72008-08-21 23:27:56 +0100526 tx_control = HERMES_TXCTRL_TX_OK | HERMES_TXCTRL_TX_EX;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700527
David Kilroy23edcc42008-08-21 23:28:05 +0100528 if (priv->encode_alg == IW_ENCODE_ALG_TKIP)
529 tx_control |= (priv->tx_key << HERMES_MIC_KEY_ID_SHIFT) |
530 HERMES_TXCTRL_MIC;
531
David Kilroy6eecad72008-08-21 23:27:56 +0100532 if (priv->has_alt_txcntl) {
533 /* WPA enabled firmwares have tx_cntl at the end of
534 * the 802.11 header. So write zeroed descriptor and
535 * 802.11 header at the same time
536 */
537 char desc[HERMES_802_3_OFFSET];
538 __le16 *txcntl = (__le16 *) &desc[HERMES_TXCNTL2_OFFSET];
539
540 memset(&desc, 0, sizeof(desc));
541
542 *txcntl = cpu_to_le16(tx_control);
543 err = hermes_bap_pwrite(hw, USER_BAP, &desc, sizeof(desc),
544 txfid, 0);
545 if (err) {
546 if (net_ratelimit())
547 printk(KERN_ERR "%s: Error %d writing Tx "
548 "descriptor to BAP\n", dev->name, err);
549 goto busy;
550 }
551 } else {
552 struct hermes_tx_descriptor desc;
553
554 memset(&desc, 0, sizeof(desc));
555
556 desc.tx_control = cpu_to_le16(tx_control);
557 err = hermes_bap_pwrite(hw, USER_BAP, &desc, sizeof(desc),
558 txfid, 0);
559 if (err) {
560 if (net_ratelimit())
561 printk(KERN_ERR "%s: Error %d writing Tx "
562 "descriptor to BAP\n", dev->name, err);
563 goto busy;
564 }
565
566 /* Clear the 802.11 header and data length fields - some
567 * firmwares (e.g. Lucent/Agere 8.xx) appear to get confused
568 * if this isn't done. */
569 hermes_clear_words(hw, HERMES_DATA0,
570 HERMES_802_3_OFFSET - HERMES_802_11_OFFSET);
571 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700572
David Kilroy23edcc42008-08-21 23:28:05 +0100573 eh = (struct ethhdr *)skb->data;
574
Linus Torvalds1da177e2005-04-16 15:20:36 -0700575 /* Encapsulate Ethernet-II frames */
576 if (ntohs(eh->h_proto) > ETH_DATA_LEN) { /* Ethernet-II frame */
Pavel Roskina28dc812006-04-07 04:10:45 -0400577 struct header_struct {
578 struct ethhdr eth; /* 802.3 header */
579 u8 encap[6]; /* 802.2 header */
580 } __attribute__ ((packed)) hdr;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700581
Pavel Roskina28dc812006-04-07 04:10:45 -0400582 /* Strip destination and source from the data */
583 skb_pull(skb, 2 * ETH_ALEN);
Pavel Roskina28dc812006-04-07 04:10:45 -0400584
585 /* And move them to a separate header */
586 memcpy(&hdr.eth, eh, 2 * ETH_ALEN);
587 hdr.eth.h_proto = htons(sizeof(encaps_hdr) + skb->len);
588 memcpy(hdr.encap, encaps_hdr, sizeof(encaps_hdr));
589
David Kilroy23edcc42008-08-21 23:28:05 +0100590 /* Insert the SNAP header */
591 if (skb_headroom(skb) < sizeof(hdr)) {
592 printk(KERN_ERR
593 "%s: Not enough headroom for 802.2 headers %d\n",
594 dev->name, skb_headroom(skb));
595 goto drop;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700596 }
David Kilroy23edcc42008-08-21 23:28:05 +0100597 eh = (struct ethhdr *) skb_push(skb, sizeof(hdr));
598 memcpy(eh, &hdr, sizeof(hdr));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700599 }
600
Pavel Roskina28dc812006-04-07 04:10:45 -0400601 err = hermes_bap_pwrite(hw, USER_BAP, skb->data, skb->len,
David Kilroy23edcc42008-08-21 23:28:05 +0100602 txfid, HERMES_802_3_OFFSET);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700603 if (err) {
604 printk(KERN_ERR "%s: Error %d writing packet to BAP\n",
605 dev->name, err);
Pavel Roskin470e2aa2006-04-07 04:10:43 -0400606 goto busy;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700607 }
608
David Kilroy23edcc42008-08-21 23:28:05 +0100609 /* Calculate Michael MIC */
610 if (priv->encode_alg == IW_ENCODE_ALG_TKIP) {
611 u8 mic_buf[MICHAEL_MIC_LEN + 1];
612 u8 *mic;
613 size_t offset;
614 size_t len;
615
616 if (skb->len % 2) {
617 /* MIC start is on an odd boundary */
618 mic_buf[0] = skb->data[skb->len - 1];
619 mic = &mic_buf[1];
620 offset = skb->len - 1;
621 len = MICHAEL_MIC_LEN + 1;
622 } else {
623 mic = &mic_buf[0];
624 offset = skb->len;
625 len = MICHAEL_MIC_LEN;
626 }
627
David Kilroy4adb4742009-02-04 23:05:51 +0000628 orinoco_mic(priv->tx_tfm_mic,
David Kilroy23edcc42008-08-21 23:28:05 +0100629 priv->tkip_key[priv->tx_key].tx_mic,
630 eh->h_dest, eh->h_source, 0 /* priority */,
631 skb->data + ETH_HLEN, skb->len - ETH_HLEN, mic);
632
633 /* Write the MIC */
634 err = hermes_bap_pwrite(hw, USER_BAP, &mic_buf[0], len,
635 txfid, HERMES_802_3_OFFSET + offset);
636 if (err) {
637 printk(KERN_ERR "%s: Error %d writing MIC to BAP\n",
638 dev->name, err);
639 goto busy;
640 }
641 }
642
Linus Torvalds1da177e2005-04-16 15:20:36 -0700643 /* Finally, we actually initiate the send */
644 netif_stop_queue(dev);
645
646 err = hermes_docmd_wait(hw, HERMES_CMD_TX | HERMES_CMD_RECL,
647 txfid, NULL);
648 if (err) {
649 netif_start_queue(dev);
Andrew Mortonc367c212005-10-19 21:23:44 -0700650 if (net_ratelimit())
651 printk(KERN_ERR "%s: Error %d transmitting packet\n",
652 dev->name, err);
Pavel Roskin470e2aa2006-04-07 04:10:43 -0400653 goto busy;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700654 }
655
656 dev->trans_start = jiffies;
David Kilroy23edcc42008-08-21 23:28:05 +0100657 stats->tx_bytes += HERMES_802_3_OFFSET + skb->len;
Pavel Roskin470e2aa2006-04-07 04:10:43 -0400658 goto ok;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700659
Pavel Roskin470e2aa2006-04-07 04:10:43 -0400660 drop:
661 stats->tx_errors++;
662 stats->tx_dropped++;
663
664 ok:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700665 orinoco_unlock(priv, &flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700666 dev_kfree_skb(skb);
Pavel Roskinb34b8672006-04-07 04:10:36 -0400667 return NETDEV_TX_OK;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700668
Pavel Roskin470e2aa2006-04-07 04:10:43 -0400669 busy:
Jiri Benc2c1bd262006-04-07 04:10:47 -0400670 if (err == -EIO)
671 schedule_work(&priv->reset_work);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700672 orinoco_unlock(priv, &flags);
Pavel Roskinb34b8672006-04-07 04:10:36 -0400673 return NETDEV_TX_BUSY;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700674}
675
676static void __orinoco_ev_alloc(struct net_device *dev, hermes_t *hw)
677{
678 struct orinoco_private *priv = netdev_priv(dev);
679 u16 fid = hermes_read_regn(hw, ALLOCFID);
680
681 if (fid != priv->txfid) {
682 if (fid != DUMMY_FID)
683 printk(KERN_WARNING "%s: Allocate event on unexpected fid (%04X)\n",
684 dev->name, fid);
685 return;
686 }
687
688 hermes_write_regn(hw, ALLOCFID, DUMMY_FID);
689}
690
691static void __orinoco_ev_tx(struct net_device *dev, hermes_t *hw)
692{
693 struct orinoco_private *priv = netdev_priv(dev);
694 struct net_device_stats *stats = &priv->stats;
695
696 stats->tx_packets++;
697
698 netif_wake_queue(dev);
699
700 hermes_write_regn(hw, TXCOMPLFID, DUMMY_FID);
701}
702
703static void __orinoco_ev_txexc(struct net_device *dev, hermes_t *hw)
704{
705 struct orinoco_private *priv = netdev_priv(dev);
706 struct net_device_stats *stats = &priv->stats;
707 u16 fid = hermes_read_regn(hw, TXCOMPLFID);
Pavel Roskind133ae42005-09-23 04:18:06 -0400708 u16 status;
Pavel Roskin30c2d3b2006-04-07 04:10:34 -0400709 struct hermes_txexc_data hdr;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700710 int err = 0;
711
712 if (fid == DUMMY_FID)
713 return; /* Nothing's really happened */
714
Pavel Roskin48ca7032005-09-23 04:18:06 -0400715 /* Read part of the frame header - we need status and addr1 */
Christoph Hellwig95dd91f2005-06-19 01:27:56 +0200716 err = hermes_bap_pread(hw, IRQ_BAP, &hdr,
Pavel Roskin30c2d3b2006-04-07 04:10:34 -0400717 sizeof(struct hermes_txexc_data),
Christoph Hellwig95dd91f2005-06-19 01:27:56 +0200718 fid, 0);
719
720 hermes_write_regn(hw, TXCOMPLFID, DUMMY_FID);
721 stats->tx_errors++;
722
Linus Torvalds1da177e2005-04-16 15:20:36 -0700723 if (err) {
724 printk(KERN_WARNING "%s: Unable to read descriptor on Tx error "
725 "(FID=%04X error %d)\n",
726 dev->name, fid, err);
Christoph Hellwig95dd91f2005-06-19 01:27:56 +0200727 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700728 }
David Kilroy6fe9deb2009-02-04 23:05:43 +0000729
Christoph Hellwig95dd91f2005-06-19 01:27:56 +0200730 DEBUG(1, "%s: Tx error, err %d (FID=%04X)\n", dev->name,
731 err, fid);
David Kilroy6fe9deb2009-02-04 23:05:43 +0000732
Christoph Hellwig95dd91f2005-06-19 01:27:56 +0200733 /* We produce a TXDROP event only for retry or lifetime
734 * exceeded, because that's the only status that really mean
735 * that this particular node went away.
736 * Other errors means that *we* screwed up. - Jean II */
Pavel Roskin30c2d3b2006-04-07 04:10:34 -0400737 status = le16_to_cpu(hdr.desc.status);
Pavel Roskind133ae42005-09-23 04:18:06 -0400738 if (status & (HERMES_TXSTAT_RETRYERR | HERMES_TXSTAT_AGEDERR)) {
Christoph Hellwig95dd91f2005-06-19 01:27:56 +0200739 union iwreq_data wrqu;
740
741 /* Copy 802.11 dest address.
742 * We use the 802.11 header because the frame may
743 * not be 802.3 or may be mangled...
744 * In Ad-Hoc mode, it will be the node address.
745 * In managed mode, it will be most likely the AP addr
746 * User space will figure out how to convert it to
747 * whatever it needs (IP address or else).
748 * - Jean II */
749 memcpy(wrqu.addr.sa_data, hdr.addr1, ETH_ALEN);
750 wrqu.addr.sa_family = ARPHRD_ETHER;
751
752 /* Send event to user space */
753 wireless_send_event(dev, IWEVTXDROP, &wrqu, NULL);
754 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700755
756 netif_wake_queue(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700757}
758
759static void orinoco_tx_timeout(struct net_device *dev)
760{
761 struct orinoco_private *priv = netdev_priv(dev);
762 struct net_device_stats *stats = &priv->stats;
763 struct hermes *hw = &priv->hw;
764
765 printk(KERN_WARNING "%s: Tx timeout! "
766 "ALLOCFID=%04x, TXCOMPLFID=%04x, EVSTAT=%04x\n",
767 dev->name, hermes_read_regn(hw, ALLOCFID),
768 hermes_read_regn(hw, TXCOMPLFID), hermes_read_regn(hw, EVSTAT));
769
770 stats->tx_errors++;
771
772 schedule_work(&priv->reset_work);
773}
774
775/********************************************************************/
776/* Rx path (data frames) */
777/********************************************************************/
778
779/* Does the frame have a SNAP header indicating it should be
780 * de-encapsulated to Ethernet-II? */
781static inline int is_ethersnap(void *_hdr)
782{
783 u8 *hdr = _hdr;
784
785 /* We de-encapsulate all packets which, a) have SNAP headers
786 * (i.e. SSAP=DSAP=0xaa and CTRL=0x3 in the 802.2 LLC header
787 * and where b) the OUI of the SNAP header is 00:00:00 or
788 * 00:00:f8 - we need both because different APs appear to use
789 * different OUIs for some reason */
790 return (memcmp(hdr, &encaps_hdr, 5) == 0)
David Kilroya94e8422009-02-04 23:05:44 +0000791 && ((hdr[5] == 0x00) || (hdr[5] == 0xf8));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700792}
793
794static inline void orinoco_spy_gather(struct net_device *dev, u_char *mac,
795 int level, int noise)
796{
Pavel Roskin343c6862005-09-09 18:43:02 -0400797 struct iw_quality wstats;
798 wstats.level = level - 0x95;
799 wstats.noise = noise - 0x95;
800 wstats.qual = (level > noise) ? (level - noise) : 0;
Andrey Borzenkovf941f852008-11-15 17:15:09 +0300801 wstats.updated = IW_QUAL_ALL_UPDATED | IW_QUAL_DBM;
Pavel Roskin343c6862005-09-09 18:43:02 -0400802 /* Update spy records */
803 wireless_spy_update(dev, mac, &wstats);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700804}
805
806static void orinoco_stat_gather(struct net_device *dev,
807 struct sk_buff *skb,
808 struct hermes_rx_descriptor *desc)
809{
810 struct orinoco_private *priv = netdev_priv(dev);
811
812 /* Using spy support with lots of Rx packets, like in an
813 * infrastructure (AP), will really slow down everything, because
814 * the MAC address must be compared to each entry of the spy list.
815 * If the user really asks for it (set some address in the
816 * spy list), we do it, but he will pay the price.
817 * Note that to get here, you need both WIRELESS_SPY
818 * compiled in AND some addresses in the list !!!
819 */
820 /* Note : gcc will optimise the whole section away if
821 * WIRELESS_SPY is not defined... - Jean II */
822 if (SPY_NUMBER(priv)) {
Arnaldo Carvalho de Melo98e399f2007-03-19 15:33:04 -0700823 orinoco_spy_gather(dev, skb_mac_header(skb) + ETH_ALEN,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700824 desc->signal, desc->silence);
825 }
826}
827
Christoph Hellwig98c4cae2005-06-19 01:28:06 +0200828/*
829 * orinoco_rx_monitor - handle received monitor frames.
830 *
831 * Arguments:
832 * dev network device
833 * rxfid received FID
834 * desc rx descriptor of the frame
835 *
836 * Call context: interrupt
837 */
838static void orinoco_rx_monitor(struct net_device *dev, u16 rxfid,
839 struct hermes_rx_descriptor *desc)
840{
841 u32 hdrlen = 30; /* return full header by default */
842 u32 datalen = 0;
843 u16 fc;
844 int err;
845 int len;
846 struct sk_buff *skb;
847 struct orinoco_private *priv = netdev_priv(dev);
848 struct net_device_stats *stats = &priv->stats;
849 hermes_t *hw = &priv->hw;
850
851 len = le16_to_cpu(desc->data_len);
852
853 /* Determine the size of the header and the data */
854 fc = le16_to_cpu(desc->frame_ctl);
855 switch (fc & IEEE80211_FCTL_FTYPE) {
856 case IEEE80211_FTYPE_DATA:
857 if ((fc & IEEE80211_FCTL_TODS)
858 && (fc & IEEE80211_FCTL_FROMDS))
859 hdrlen = 30;
860 else
861 hdrlen = 24;
862 datalen = len;
863 break;
864 case IEEE80211_FTYPE_MGMT:
865 hdrlen = 24;
866 datalen = len;
867 break;
868 case IEEE80211_FTYPE_CTL:
869 switch (fc & IEEE80211_FCTL_STYPE) {
870 case IEEE80211_STYPE_PSPOLL:
871 case IEEE80211_STYPE_RTS:
872 case IEEE80211_STYPE_CFEND:
873 case IEEE80211_STYPE_CFENDACK:
874 hdrlen = 16;
875 break;
876 case IEEE80211_STYPE_CTS:
877 case IEEE80211_STYPE_ACK:
878 hdrlen = 10;
879 break;
880 }
881 break;
882 default:
883 /* Unknown frame type */
884 break;
885 }
886
887 /* sanity check the length */
Johannes Berg2c7060022008-10-30 22:09:54 +0100888 if (datalen > IEEE80211_MAX_DATA_LEN + 12) {
Christoph Hellwig98c4cae2005-06-19 01:28:06 +0200889 printk(KERN_DEBUG "%s: oversized monitor frame, "
890 "data length = %d\n", dev->name, datalen);
Christoph Hellwig98c4cae2005-06-19 01:28:06 +0200891 stats->rx_length_errors++;
892 goto update_stats;
893 }
894
895 skb = dev_alloc_skb(hdrlen + datalen);
896 if (!skb) {
897 printk(KERN_WARNING "%s: Cannot allocate skb for monitor frame\n",
898 dev->name);
Florin Malitabb6e0932006-05-22 22:35:30 -0700899 goto update_stats;
Christoph Hellwig98c4cae2005-06-19 01:28:06 +0200900 }
901
902 /* Copy the 802.11 header to the skb */
903 memcpy(skb_put(skb, hdrlen), &(desc->frame_ctl), hdrlen);
Arnaldo Carvalho de Melo459a98e2007-03-19 15:30:44 -0700904 skb_reset_mac_header(skb);
Christoph Hellwig98c4cae2005-06-19 01:28:06 +0200905
906 /* If any, copy the data from the card to the skb */
907 if (datalen > 0) {
908 err = hermes_bap_pread(hw, IRQ_BAP, skb_put(skb, datalen),
909 ALIGN(datalen, 2), rxfid,
910 HERMES_802_2_OFFSET);
911 if (err) {
912 printk(KERN_ERR "%s: error %d reading monitor frame\n",
913 dev->name, err);
914 goto drop;
915 }
916 }
917
918 skb->dev = dev;
919 skb->ip_summed = CHECKSUM_NONE;
920 skb->pkt_type = PACKET_OTHERHOST;
Harvey Harrisonc1b4aa32009-01-29 13:26:44 -0800921 skb->protocol = cpu_to_be16(ETH_P_802_2);
David Kilroy6fe9deb2009-02-04 23:05:43 +0000922
Christoph Hellwig98c4cae2005-06-19 01:28:06 +0200923 stats->rx_packets++;
924 stats->rx_bytes += skb->len;
925
926 netif_rx(skb);
927 return;
928
929 drop:
930 dev_kfree_skb_irq(skb);
931 update_stats:
932 stats->rx_errors++;
933 stats->rx_dropped++;
934}
935
David Kilroy23edcc42008-08-21 23:28:05 +0100936/* Get tsc from the firmware */
937static int orinoco_hw_get_tkip_iv(struct orinoco_private *priv, int key,
938 u8 *tsc)
939{
940 hermes_t *hw = &priv->hw;
941 int err = 0;
942 u8 tsc_arr[4][IW_ENCODE_SEQ_MAX_SIZE];
943
944 if ((key < 0) || (key > 4))
945 return -EINVAL;
946
947 err = hermes_read_ltv(hw, USER_BAP, HERMES_RID_CURRENT_TKIP_IV,
948 sizeof(tsc_arr), NULL, &tsc_arr);
949 if (!err)
950 memcpy(tsc, &tsc_arr[key][0], sizeof(tsc_arr[0]));
951
952 return err;
953}
954
Linus Torvalds1da177e2005-04-16 15:20:36 -0700955static void __orinoco_ev_rx(struct net_device *dev, hermes_t *hw)
956{
957 struct orinoco_private *priv = netdev_priv(dev);
958 struct net_device_stats *stats = &priv->stats;
959 struct iw_statistics *wstats = &priv->wstats;
960 struct sk_buff *skb = NULL;
David Kilroy31afcef2008-08-21 23:28:04 +0100961 u16 rxfid, status;
Christoph Hellwig8f2abf42005-06-19 01:28:02 +0200962 int length;
David Kilroy31afcef2008-08-21 23:28:04 +0100963 struct hermes_rx_descriptor *desc;
964 struct orinoco_rx_data *rx_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700965 int err;
966
David Kilroy31afcef2008-08-21 23:28:04 +0100967 desc = kmalloc(sizeof(*desc), GFP_ATOMIC);
968 if (!desc) {
969 printk(KERN_WARNING
970 "%s: Can't allocate space for RX descriptor\n",
971 dev->name);
972 goto update_stats;
973 }
974
Linus Torvalds1da177e2005-04-16 15:20:36 -0700975 rxfid = hermes_read_regn(hw, RXFID);
976
David Kilroy31afcef2008-08-21 23:28:04 +0100977 err = hermes_bap_pread(hw, IRQ_BAP, desc, sizeof(*desc),
Linus Torvalds1da177e2005-04-16 15:20:36 -0700978 rxfid, 0);
979 if (err) {
980 printk(KERN_ERR "%s: error %d reading Rx descriptor. "
981 "Frame dropped.\n", dev->name, err);
Christoph Hellwig98c4cae2005-06-19 01:28:06 +0200982 goto update_stats;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700983 }
984
David Kilroy31afcef2008-08-21 23:28:04 +0100985 status = le16_to_cpu(desc->status);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700986
Christoph Hellwig98c4cae2005-06-19 01:28:06 +0200987 if (status & HERMES_RXSTAT_BADCRC) {
988 DEBUG(1, "%s: Bad CRC on Rx. Frame dropped.\n",
989 dev->name);
990 stats->rx_crc_errors++;
991 goto update_stats;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700992 }
993
Christoph Hellwig98c4cae2005-06-19 01:28:06 +0200994 /* Handle frames in monitor mode */
995 if (priv->iw_mode == IW_MODE_MONITOR) {
David Kilroy31afcef2008-08-21 23:28:04 +0100996 orinoco_rx_monitor(dev, rxfid, desc);
997 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700998 }
999
Christoph Hellwig98c4cae2005-06-19 01:28:06 +02001000 if (status & HERMES_RXSTAT_UNDECRYPTABLE) {
1001 DEBUG(1, "%s: Undecryptable frame on Rx. Frame dropped.\n",
1002 dev->name);
1003 wstats->discard.code++;
1004 goto update_stats;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001005 }
1006
David Kilroy31afcef2008-08-21 23:28:04 +01001007 length = le16_to_cpu(desc->data_len);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001008
Linus Torvalds1da177e2005-04-16 15:20:36 -07001009 /* Sanity checks */
1010 if (length < 3) { /* No for even an 802.2 LLC header */
1011 /* At least on Symbol firmware with PCF we get quite a
David Kilroy6fe9deb2009-02-04 23:05:43 +00001012 lot of these legitimately - Poll frames with no
1013 data. */
David Kilroy31afcef2008-08-21 23:28:04 +01001014 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001015 }
Johannes Berg2c7060022008-10-30 22:09:54 +01001016 if (length > IEEE80211_MAX_DATA_LEN) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001017 printk(KERN_WARNING "%s: Oversized frame received (%d bytes)\n",
1018 dev->name, length);
1019 stats->rx_length_errors++;
Christoph Hellwig98c4cae2005-06-19 01:28:06 +02001020 goto update_stats;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001021 }
1022
David Kilroy23edcc42008-08-21 23:28:05 +01001023 /* Payload size does not include Michael MIC. Increase payload
1024 * size to read it together with the data. */
1025 if (status & HERMES_RXSTAT_MIC)
1026 length += MICHAEL_MIC_LEN;
1027
Linus Torvalds1da177e2005-04-16 15:20:36 -07001028 /* We need space for the packet data itself, plus an ethernet
1029 header, plus 2 bytes so we can align the IP header on a
1030 32bit boundary, plus 1 byte so we can read in odd length
1031 packets from the card, which has an IO granularity of 16
David Kilroy6fe9deb2009-02-04 23:05:43 +00001032 bits */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001033 skb = dev_alloc_skb(length+ETH_HLEN+2+1);
1034 if (!skb) {
1035 printk(KERN_WARNING "%s: Can't allocate skb for Rx\n",
1036 dev->name);
Christoph Hellwig98c4cae2005-06-19 01:28:06 +02001037 goto update_stats;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001038 }
1039
Christoph Hellwig8f2abf42005-06-19 01:28:02 +02001040 /* We'll prepend the header, so reserve space for it. The worst
1041 case is no decapsulation, when 802.3 header is prepended and
1042 nothing is removed. 2 is for aligning the IP header. */
1043 skb_reserve(skb, ETH_HLEN + 2);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001044
Christoph Hellwig8f2abf42005-06-19 01:28:02 +02001045 err = hermes_bap_pread(hw, IRQ_BAP, skb_put(skb, length),
1046 ALIGN(length, 2), rxfid,
1047 HERMES_802_2_OFFSET);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001048 if (err) {
1049 printk(KERN_ERR "%s: error %d reading frame. "
1050 "Frame dropped.\n", dev->name, err);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001051 goto drop;
1052 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001053
David Kilroy31afcef2008-08-21 23:28:04 +01001054 /* Add desc and skb to rx queue */
1055 rx_data = kzalloc(sizeof(*rx_data), GFP_ATOMIC);
1056 if (!rx_data) {
1057 printk(KERN_WARNING "%s: Can't allocate RX packet\n",
1058 dev->name);
1059 goto drop;
1060 }
1061 rx_data->desc = desc;
1062 rx_data->skb = skb;
1063 list_add_tail(&rx_data->list, &priv->rx_list);
1064 tasklet_schedule(&priv->rx_tasklet);
1065
1066 return;
1067
1068drop:
1069 dev_kfree_skb_irq(skb);
1070update_stats:
1071 stats->rx_errors++;
1072 stats->rx_dropped++;
1073out:
1074 kfree(desc);
1075}
1076
1077static void orinoco_rx(struct net_device *dev,
1078 struct hermes_rx_descriptor *desc,
1079 struct sk_buff *skb)
1080{
1081 struct orinoco_private *priv = netdev_priv(dev);
1082 struct net_device_stats *stats = &priv->stats;
1083 u16 status, fc;
1084 int length;
1085 struct ethhdr *hdr;
1086
1087 status = le16_to_cpu(desc->status);
1088 length = le16_to_cpu(desc->data_len);
1089 fc = le16_to_cpu(desc->frame_ctl);
1090
David Kilroy23edcc42008-08-21 23:28:05 +01001091 /* Calculate and check MIC */
1092 if (status & HERMES_RXSTAT_MIC) {
1093 int key_id = ((status & HERMES_RXSTAT_MIC_KEY_ID) >>
1094 HERMES_MIC_KEY_ID_SHIFT);
1095 u8 mic[MICHAEL_MIC_LEN];
1096 u8 *rxmic;
1097 u8 *src = (fc & IEEE80211_FCTL_FROMDS) ?
1098 desc->addr3 : desc->addr2;
1099
1100 /* Extract Michael MIC from payload */
1101 rxmic = skb->data + skb->len - MICHAEL_MIC_LEN;
1102
1103 skb_trim(skb, skb->len - MICHAEL_MIC_LEN);
1104 length -= MICHAEL_MIC_LEN;
1105
David Kilroy4adb4742009-02-04 23:05:51 +00001106 orinoco_mic(priv->rx_tfm_mic,
David Kilroy23edcc42008-08-21 23:28:05 +01001107 priv->tkip_key[key_id].rx_mic,
1108 desc->addr1,
1109 src,
1110 0, /* priority or QoS? */
1111 skb->data,
1112 skb->len,
1113 &mic[0]);
1114
1115 if (memcmp(mic, rxmic,
1116 MICHAEL_MIC_LEN)) {
1117 union iwreq_data wrqu;
1118 struct iw_michaelmicfailure wxmic;
David Kilroy23edcc42008-08-21 23:28:05 +01001119
1120 printk(KERN_WARNING "%s: "
Johannes Berge1749612008-10-27 15:59:26 -07001121 "Invalid Michael MIC in data frame from %pM, "
David Kilroy23edcc42008-08-21 23:28:05 +01001122 "using key %i\n",
Johannes Berge1749612008-10-27 15:59:26 -07001123 dev->name, src, key_id);
David Kilroy23edcc42008-08-21 23:28:05 +01001124
1125 /* TODO: update stats */
1126
1127 /* Notify userspace */
1128 memset(&wxmic, 0, sizeof(wxmic));
1129 wxmic.flags = key_id & IW_MICFAILURE_KEY_ID;
1130 wxmic.flags |= (desc->addr1[0] & 1) ?
1131 IW_MICFAILURE_GROUP : IW_MICFAILURE_PAIRWISE;
1132 wxmic.src_addr.sa_family = ARPHRD_ETHER;
1133 memcpy(wxmic.src_addr.sa_data, src, ETH_ALEN);
1134
1135 (void) orinoco_hw_get_tkip_iv(priv, key_id,
1136 &wxmic.tsc[0]);
1137
1138 memset(&wrqu, 0, sizeof(wrqu));
1139 wrqu.data.length = sizeof(wxmic);
1140 wireless_send_event(dev, IWEVMICHAELMICFAILURE, &wrqu,
1141 (char *) &wxmic);
1142
1143 goto drop;
1144 }
1145 }
1146
Linus Torvalds1da177e2005-04-16 15:20:36 -07001147 /* Handle decapsulation
1148 * In most cases, the firmware tell us about SNAP frames.
1149 * For some reason, the SNAP frames sent by LinkSys APs
1150 * are not properly recognised by most firmwares.
1151 * So, check ourselves */
Christoph Hellwig8f2abf42005-06-19 01:28:02 +02001152 if (length >= ENCAPS_OVERHEAD &&
1153 (((status & HERMES_RXSTAT_MSGTYPE) == HERMES_RXSTAT_1042) ||
1154 ((status & HERMES_RXSTAT_MSGTYPE) == HERMES_RXSTAT_TUNNEL) ||
1155 is_ethersnap(skb->data))) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001156 /* These indicate a SNAP within 802.2 LLC within
1157 802.11 frame which we'll need to de-encapsulate to
1158 the original EthernetII frame. */
David Kilroyb2f30a02009-02-04 23:05:46 +00001159 hdr = (struct ethhdr *)skb_push(skb,
1160 ETH_HLEN - ENCAPS_OVERHEAD);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001161 } else {
Christoph Hellwig8f2abf42005-06-19 01:28:02 +02001162 /* 802.3 frame - prepend 802.3 header as is */
1163 hdr = (struct ethhdr *)skb_push(skb, ETH_HLEN);
1164 hdr->h_proto = htons(length);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001165 }
David Kilroy31afcef2008-08-21 23:28:04 +01001166 memcpy(hdr->h_dest, desc->addr1, ETH_ALEN);
Christoph Hellwig8f2abf42005-06-19 01:28:02 +02001167 if (fc & IEEE80211_FCTL_FROMDS)
David Kilroy31afcef2008-08-21 23:28:04 +01001168 memcpy(hdr->h_source, desc->addr3, ETH_ALEN);
Christoph Hellwig8f2abf42005-06-19 01:28:02 +02001169 else
David Kilroy31afcef2008-08-21 23:28:04 +01001170 memcpy(hdr->h_source, desc->addr2, ETH_ALEN);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001171
Linus Torvalds1da177e2005-04-16 15:20:36 -07001172 skb->protocol = eth_type_trans(skb, dev);
1173 skb->ip_summed = CHECKSUM_NONE;
Christoph Hellwig8f2abf42005-06-19 01:28:02 +02001174 if (fc & IEEE80211_FCTL_TODS)
1175 skb->pkt_type = PACKET_OTHERHOST;
David Kilroy6fe9deb2009-02-04 23:05:43 +00001176
Linus Torvalds1da177e2005-04-16 15:20:36 -07001177 /* Process the wireless stats if needed */
David Kilroy31afcef2008-08-21 23:28:04 +01001178 orinoco_stat_gather(dev, skb, desc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001179
1180 /* Pass the packet to the networking stack */
1181 netif_rx(skb);
1182 stats->rx_packets++;
1183 stats->rx_bytes += length;
1184
1185 return;
David Kilroy23edcc42008-08-21 23:28:05 +01001186
1187 drop:
1188 dev_kfree_skb(skb);
1189 stats->rx_errors++;
1190 stats->rx_dropped++;
David Kilroy31afcef2008-08-21 23:28:04 +01001191}
Linus Torvalds1da177e2005-04-16 15:20:36 -07001192
David Kilroy31afcef2008-08-21 23:28:04 +01001193static void orinoco_rx_isr_tasklet(unsigned long data)
1194{
1195 struct net_device *dev = (struct net_device *) data;
1196 struct orinoco_private *priv = netdev_priv(dev);
1197 struct orinoco_rx_data *rx_data, *temp;
1198 struct hermes_rx_descriptor *desc;
1199 struct sk_buff *skb;
David Kilroy20953ad2009-01-07 00:23:55 +00001200 unsigned long flags;
1201
1202 /* orinoco_rx requires the driver lock, and we also need to
1203 * protect priv->rx_list, so just hold the lock over the
1204 * lot.
1205 *
1206 * If orinoco_lock fails, we've unplugged the card. In this
1207 * case just abort. */
1208 if (orinoco_lock(priv, &flags) != 0)
1209 return;
David Kilroy31afcef2008-08-21 23:28:04 +01001210
1211 /* extract desc and skb from queue */
1212 list_for_each_entry_safe(rx_data, temp, &priv->rx_list, list) {
1213 desc = rx_data->desc;
1214 skb = rx_data->skb;
1215 list_del(&rx_data->list);
1216 kfree(rx_data);
1217
1218 orinoco_rx(dev, desc, skb);
1219
1220 kfree(desc);
1221 }
David Kilroy20953ad2009-01-07 00:23:55 +00001222
1223 orinoco_unlock(priv, &flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001224}
1225
1226/********************************************************************/
1227/* Rx path (info frames) */
1228/********************************************************************/
1229
1230static void print_linkstatus(struct net_device *dev, u16 status)
1231{
David Kilroy21312662009-02-04 23:05:47 +00001232 char *s;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001233
1234 if (suppress_linkstatus)
1235 return;
1236
1237 switch (status) {
1238 case HERMES_LINKSTATUS_NOT_CONNECTED:
1239 s = "Not Connected";
1240 break;
1241 case HERMES_LINKSTATUS_CONNECTED:
1242 s = "Connected";
1243 break;
1244 case HERMES_LINKSTATUS_DISCONNECTED:
1245 s = "Disconnected";
1246 break;
1247 case HERMES_LINKSTATUS_AP_CHANGE:
1248 s = "AP Changed";
1249 break;
1250 case HERMES_LINKSTATUS_AP_OUT_OF_RANGE:
1251 s = "AP Out of Range";
1252 break;
1253 case HERMES_LINKSTATUS_AP_IN_RANGE:
1254 s = "AP In Range";
1255 break;
1256 case HERMES_LINKSTATUS_ASSOC_FAILED:
1257 s = "Association Failed";
1258 break;
1259 default:
1260 s = "UNKNOWN";
1261 }
David Kilroy6fe9deb2009-02-04 23:05:43 +00001262
Pavel Roskin11eaea42009-01-18 23:20:58 -05001263 printk(KERN_DEBUG "%s: New link status: %s (%04x)\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -07001264 dev->name, s, status);
1265}
1266
Christoph Hellwig16739b02005-06-19 01:27:51 +02001267/* Search scan results for requested BSSID, join it if found */
David Howellsc4028952006-11-22 14:57:56 +00001268static void orinoco_join_ap(struct work_struct *work)
Christoph Hellwig16739b02005-06-19 01:27:51 +02001269{
David Howellsc4028952006-11-22 14:57:56 +00001270 struct orinoco_private *priv =
1271 container_of(work, struct orinoco_private, join_work);
1272 struct net_device *dev = priv->ndev;
Christoph Hellwig16739b02005-06-19 01:27:51 +02001273 struct hermes *hw = &priv->hw;
1274 int err;
1275 unsigned long flags;
1276 struct join_req {
1277 u8 bssid[ETH_ALEN];
Pavel Roskind133ae42005-09-23 04:18:06 -04001278 __le16 channel;
Christoph Hellwig16739b02005-06-19 01:27:51 +02001279 } __attribute__ ((packed)) req;
1280 const int atom_len = offsetof(struct prism2_scan_apinfo, atim);
Pavel Roskinc89cc222005-09-01 20:06:06 -04001281 struct prism2_scan_apinfo *atom = NULL;
Christoph Hellwig16739b02005-06-19 01:27:51 +02001282 int offset = 4;
Pavel Roskinc89cc222005-09-01 20:06:06 -04001283 int found = 0;
Christoph Hellwig16739b02005-06-19 01:27:51 +02001284 u8 *buf;
1285 u16 len;
1286
1287 /* Allocate buffer for scan results */
1288 buf = kmalloc(MAX_SCAN_LEN, GFP_KERNEL);
David Kilroya94e8422009-02-04 23:05:44 +00001289 if (!buf)
Christoph Hellwig16739b02005-06-19 01:27:51 +02001290 return;
1291
1292 if (orinoco_lock(priv, &flags) != 0)
Pavel Roskinf3cb4cc2005-09-23 04:18:06 -04001293 goto fail_lock;
Christoph Hellwig16739b02005-06-19 01:27:51 +02001294
1295 /* Sanity checks in case user changed something in the meantime */
David Kilroya94e8422009-02-04 23:05:44 +00001296 if (!priv->bssid_fixed)
Christoph Hellwig16739b02005-06-19 01:27:51 +02001297 goto out;
1298
1299 if (strlen(priv->desired_essid) == 0)
1300 goto out;
1301
1302 /* Read scan results from the firmware */
1303 err = hermes_read_ltv(hw, USER_BAP,
1304 HERMES_RID_SCANRESULTSTABLE,
1305 MAX_SCAN_LEN, &len, buf);
1306 if (err) {
1307 printk(KERN_ERR "%s: Cannot read scan results\n",
1308 dev->name);
1309 goto out;
1310 }
1311
1312 len = HERMES_RECLEN_TO_BYTES(len);
1313
1314 /* Go through the scan results looking for the channel of the AP
1315 * we were requested to join */
1316 for (; offset + atom_len <= len; offset += atom_len) {
1317 atom = (struct prism2_scan_apinfo *) (buf + offset);
Pavel Roskinc89cc222005-09-01 20:06:06 -04001318 if (memcmp(&atom->bssid, priv->desired_bssid, ETH_ALEN) == 0) {
1319 found = 1;
1320 break;
1321 }
Christoph Hellwig16739b02005-06-19 01:27:51 +02001322 }
1323
David Kilroya94e8422009-02-04 23:05:44 +00001324 if (!found) {
Pavel Roskinc89cc222005-09-01 20:06:06 -04001325 DEBUG(1, "%s: Requested AP not found in scan results\n",
1326 dev->name);
1327 goto out;
1328 }
Christoph Hellwig16739b02005-06-19 01:27:51 +02001329
Christoph Hellwig16739b02005-06-19 01:27:51 +02001330 memcpy(req.bssid, priv->desired_bssid, ETH_ALEN);
1331 req.channel = atom->channel; /* both are little-endian */
1332 err = HERMES_WRITE_RECORD(hw, USER_BAP, HERMES_RID_CNFJOINREQUEST,
1333 &req);
1334 if (err)
1335 printk(KERN_ERR "%s: Error issuing join request\n", dev->name);
1336
1337 out:
Christoph Hellwig16739b02005-06-19 01:27:51 +02001338 orinoco_unlock(priv, &flags);
Pavel Roskinf3cb4cc2005-09-23 04:18:06 -04001339
1340 fail_lock:
1341 kfree(buf);
Christoph Hellwig16739b02005-06-19 01:27:51 +02001342}
1343
Christoph Hellwig95dd91f2005-06-19 01:27:56 +02001344/* Send new BSSID to userspace */
David Kilroy6cd90b12008-08-21 23:28:00 +01001345static void orinoco_send_bssid_wevent(struct orinoco_private *priv)
Christoph Hellwig95dd91f2005-06-19 01:27:56 +02001346{
David Howellsc4028952006-11-22 14:57:56 +00001347 struct net_device *dev = priv->ndev;
Christoph Hellwig95dd91f2005-06-19 01:27:56 +02001348 struct hermes *hw = &priv->hw;
1349 union iwreq_data wrqu;
1350 int err;
Christoph Hellwig95dd91f2005-06-19 01:27:56 +02001351
David Kilroy499b7022008-12-09 21:46:29 +00001352 err = hermes_read_ltv(hw, USER_BAP, HERMES_RID_CURRENTBSSID,
Christoph Hellwig95dd91f2005-06-19 01:27:56 +02001353 ETH_ALEN, NULL, wrqu.ap_addr.sa_data);
1354 if (err != 0)
David Kilroy6cd90b12008-08-21 23:28:00 +01001355 return;
Christoph Hellwig95dd91f2005-06-19 01:27:56 +02001356
1357 wrqu.ap_addr.sa_family = ARPHRD_ETHER;
1358
1359 /* Send event to user space */
1360 wireless_send_event(dev, SIOCGIWAP, &wrqu, NULL);
Christoph Hellwig95dd91f2005-06-19 01:27:56 +02001361}
1362
David Kilroy06009fd2008-08-21 23:28:03 +01001363static void orinoco_send_assocreqie_wevent(struct orinoco_private *priv)
1364{
1365 struct net_device *dev = priv->ndev;
1366 struct hermes *hw = &priv->hw;
1367 union iwreq_data wrqu;
1368 int err;
1369 u8 buf[88];
1370 u8 *ie;
1371
1372 if (!priv->has_wpa)
1373 return;
1374
David Kilroy499b7022008-12-09 21:46:29 +00001375 err = hermes_read_ltv(hw, USER_BAP, HERMES_RID_CURRENT_ASSOC_REQ_INFO,
David Kilroy06009fd2008-08-21 23:28:03 +01001376 sizeof(buf), NULL, &buf);
1377 if (err != 0)
1378 return;
1379
1380 ie = orinoco_get_wpa_ie(buf, sizeof(buf));
1381 if (ie) {
1382 int rem = sizeof(buf) - (ie - &buf[0]);
1383 wrqu.data.length = ie[1] + 2;
1384 if (wrqu.data.length > rem)
1385 wrqu.data.length = rem;
1386
1387 if (wrqu.data.length)
1388 /* Send event to user space */
1389 wireless_send_event(dev, IWEVASSOCREQIE, &wrqu, ie);
1390 }
1391}
1392
1393static void orinoco_send_assocrespie_wevent(struct orinoco_private *priv)
1394{
1395 struct net_device *dev = priv->ndev;
1396 struct hermes *hw = &priv->hw;
1397 union iwreq_data wrqu;
1398 int err;
1399 u8 buf[88]; /* TODO: verify max size or IW_GENERIC_IE_MAX */
1400 u8 *ie;
1401
1402 if (!priv->has_wpa)
1403 return;
1404
David Kilroy499b7022008-12-09 21:46:29 +00001405 err = hermes_read_ltv(hw, USER_BAP, HERMES_RID_CURRENT_ASSOC_RESP_INFO,
David Kilroy06009fd2008-08-21 23:28:03 +01001406 sizeof(buf), NULL, &buf);
1407 if (err != 0)
1408 return;
1409
1410 ie = orinoco_get_wpa_ie(buf, sizeof(buf));
1411 if (ie) {
1412 int rem = sizeof(buf) - (ie - &buf[0]);
1413 wrqu.data.length = ie[1] + 2;
1414 if (wrqu.data.length > rem)
1415 wrqu.data.length = rem;
1416
1417 if (wrqu.data.length)
1418 /* Send event to user space */
1419 wireless_send_event(dev, IWEVASSOCRESPIE, &wrqu, ie);
1420 }
1421}
1422
David Kilroy6cd90b12008-08-21 23:28:00 +01001423static void orinoco_send_wevents(struct work_struct *work)
1424{
1425 struct orinoco_private *priv =
1426 container_of(work, struct orinoco_private, wevent_work);
1427 unsigned long flags;
1428
1429 if (orinoco_lock(priv, &flags) != 0)
1430 return;
1431
David Kilroy06009fd2008-08-21 23:28:03 +01001432 orinoco_send_assocreqie_wevent(priv);
1433 orinoco_send_assocrespie_wevent(priv);
David Kilroy6cd90b12008-08-21 23:28:00 +01001434 orinoco_send_bssid_wevent(priv);
1435
1436 orinoco_unlock(priv, &flags);
1437}
Dan Williams1e3428e2007-10-10 23:56:25 -04001438
Linus Torvalds1da177e2005-04-16 15:20:36 -07001439static void __orinoco_ev_info(struct net_device *dev, hermes_t *hw)
1440{
1441 struct orinoco_private *priv = netdev_priv(dev);
1442 u16 infofid;
1443 struct {
Pavel Roskind133ae42005-09-23 04:18:06 -04001444 __le16 len;
1445 __le16 type;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001446 } __attribute__ ((packed)) info;
1447 int len, type;
1448 int err;
1449
1450 /* This is an answer to an INQUIRE command that we did earlier,
1451 * or an information "event" generated by the card
1452 * The controller return to us a pseudo frame containing
1453 * the information in question - Jean II */
1454 infofid = hermes_read_regn(hw, INFOFID);
1455
1456 /* Read the info frame header - don't try too hard */
1457 err = hermes_bap_pread(hw, IRQ_BAP, &info, sizeof(info),
1458 infofid, 0);
1459 if (err) {
1460 printk(KERN_ERR "%s: error %d reading info frame. "
1461 "Frame dropped.\n", dev->name, err);
1462 return;
1463 }
David Kilroy6fe9deb2009-02-04 23:05:43 +00001464
Linus Torvalds1da177e2005-04-16 15:20:36 -07001465 len = HERMES_RECLEN_TO_BYTES(le16_to_cpu(info.len));
1466 type = le16_to_cpu(info.type);
1467
1468 switch (type) {
1469 case HERMES_INQ_TALLIES: {
1470 struct hermes_tallies_frame tallies;
1471 struct iw_statistics *wstats = &priv->wstats;
David Kilroy6fe9deb2009-02-04 23:05:43 +00001472
Linus Torvalds1da177e2005-04-16 15:20:36 -07001473 if (len > sizeof(tallies)) {
1474 printk(KERN_WARNING "%s: Tallies frame too long (%d bytes)\n",
1475 dev->name, len);
1476 len = sizeof(tallies);
1477 }
David Kilroy6fe9deb2009-02-04 23:05:43 +00001478
Christoph Hellwig84d8a2f2005-05-14 17:30:22 +02001479 err = hermes_bap_pread(hw, IRQ_BAP, &tallies, len,
1480 infofid, sizeof(info));
1481 if (err)
1482 break;
David Kilroy6fe9deb2009-02-04 23:05:43 +00001483
Linus Torvalds1da177e2005-04-16 15:20:36 -07001484 /* Increment our various counters */
1485 /* wstats->discard.nwid - no wrong BSSID stuff */
1486 wstats->discard.code +=
1487 le16_to_cpu(tallies.RxWEPUndecryptable);
David Kilroy6fe9deb2009-02-04 23:05:43 +00001488 if (len == sizeof(tallies))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001489 wstats->discard.code +=
1490 le16_to_cpu(tallies.RxDiscards_WEPICVError) +
1491 le16_to_cpu(tallies.RxDiscards_WEPExcluded);
1492 wstats->discard.misc +=
1493 le16_to_cpu(tallies.TxDiscardsWrongSA);
1494 wstats->discard.fragment +=
1495 le16_to_cpu(tallies.RxMsgInBadMsgFragments);
1496 wstats->discard.retries +=
1497 le16_to_cpu(tallies.TxRetryLimitExceeded);
1498 /* wstats->miss.beacon - no match */
1499 }
1500 break;
1501 case HERMES_INQ_LINKSTATUS: {
1502 struct hermes_linkstatus linkstatus;
1503 u16 newstatus;
1504 int connected;
1505
Christoph Hellwig8f2abf42005-06-19 01:28:02 +02001506 if (priv->iw_mode == IW_MODE_MONITOR)
1507 break;
1508
Linus Torvalds1da177e2005-04-16 15:20:36 -07001509 if (len != sizeof(linkstatus)) {
1510 printk(KERN_WARNING "%s: Unexpected size for linkstatus frame (%d bytes)\n",
1511 dev->name, len);
1512 break;
1513 }
1514
Christoph Hellwig84d8a2f2005-05-14 17:30:22 +02001515 err = hermes_bap_pread(hw, IRQ_BAP, &linkstatus, len,
1516 infofid, sizeof(info));
1517 if (err)
1518 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001519 newstatus = le16_to_cpu(linkstatus.linkstatus);
1520
Christoph Hellwig95dd91f2005-06-19 01:27:56 +02001521 /* Symbol firmware uses "out of range" to signal that
1522 * the hostscan frame can be requested. */
1523 if (newstatus == HERMES_LINKSTATUS_AP_OUT_OF_RANGE &&
1524 priv->firmware_type == FIRMWARE_TYPE_SYMBOL &&
1525 priv->has_hostscan && priv->scan_inprogress) {
1526 hermes_inquire(hw, HERMES_INQ_HOSTSCAN_SYMBOL);
1527 break;
1528 }
1529
Linus Torvalds1da177e2005-04-16 15:20:36 -07001530 connected = (newstatus == HERMES_LINKSTATUS_CONNECTED)
1531 || (newstatus == HERMES_LINKSTATUS_AP_CHANGE)
1532 || (newstatus == HERMES_LINKSTATUS_AP_IN_RANGE);
1533
1534 if (connected)
1535 netif_carrier_on(dev);
David Gibson7bb7c3a2005-05-12 20:02:10 -04001536 else if (!ignore_disconnect)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001537 netif_carrier_off(dev);
1538
Christoph Hellwig95dd91f2005-06-19 01:27:56 +02001539 if (newstatus != priv->last_linkstatus) {
1540 priv->last_linkstatus = newstatus;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001541 print_linkstatus(dev, newstatus);
Christoph Hellwig95dd91f2005-06-19 01:27:56 +02001542 /* The info frame contains only one word which is the
1543 * status (see hermes.h). The status is pretty boring
1544 * in itself, that's why we export the new BSSID...
1545 * Jean II */
1546 schedule_work(&priv->wevent_work);
1547 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001548 }
1549 break;
Christoph Hellwig95dd91f2005-06-19 01:27:56 +02001550 case HERMES_INQ_SCAN:
1551 if (!priv->scan_inprogress && priv->bssid_fixed &&
1552 priv->firmware_type == FIRMWARE_TYPE_INTERSIL) {
1553 schedule_work(&priv->join_work);
1554 break;
1555 }
1556 /* fall through */
1557 case HERMES_INQ_HOSTSCAN:
1558 case HERMES_INQ_HOSTSCAN_SYMBOL: {
1559 /* Result of a scanning. Contains information about
1560 * cells in the vicinity - Jean II */
1561 union iwreq_data wrqu;
1562 unsigned char *buf;
1563
Dan Williams1e3428e2007-10-10 23:56:25 -04001564 /* Scan is no longer in progress */
1565 priv->scan_inprogress = 0;
1566
Christoph Hellwig95dd91f2005-06-19 01:27:56 +02001567 /* Sanity check */
1568 if (len > 4096) {
1569 printk(KERN_WARNING "%s: Scan results too large (%d bytes)\n",
1570 dev->name, len);
1571 break;
1572 }
1573
Christoph Hellwig95dd91f2005-06-19 01:27:56 +02001574 /* Allocate buffer for results */
1575 buf = kmalloc(len, GFP_ATOMIC);
1576 if (buf == NULL)
1577 /* No memory, so can't printk()... */
1578 break;
1579
1580 /* Read scan data */
1581 err = hermes_bap_pread(hw, IRQ_BAP, (void *) buf, len,
1582 infofid, sizeof(info));
Pavel Roskin708218b2005-09-01 20:05:19 -04001583 if (err) {
1584 kfree(buf);
Christoph Hellwig95dd91f2005-06-19 01:27:56 +02001585 break;
Pavel Roskin708218b2005-09-01 20:05:19 -04001586 }
Christoph Hellwig95dd91f2005-06-19 01:27:56 +02001587
1588#ifdef ORINOCO_DEBUG
1589 {
1590 int i;
1591 printk(KERN_DEBUG "Scan result [%02X", buf[0]);
David Kilroya94e8422009-02-04 23:05:44 +00001592 for (i = 1; i < (len * 2); i++)
Christoph Hellwig95dd91f2005-06-19 01:27:56 +02001593 printk(":%02X", buf[i]);
1594 printk("]\n");
1595 }
1596#endif /* ORINOCO_DEBUG */
1597
David Kilroyaea48b12009-02-04 23:05:49 +00001598 if (orinoco_process_scan_results(priv, buf, len) == 0) {
Dan Williams1e3428e2007-10-10 23:56:25 -04001599 /* Send an empty event to user space.
1600 * We don't send the received data on the event because
1601 * it would require us to do complex transcoding, and
1602 * we want to minimise the work done in the irq handler
1603 * Use a request to extract the data - Jean II */
1604 wrqu.data.length = 0;
1605 wrqu.data.flags = 0;
1606 wireless_send_event(dev, SIOCGIWSCAN, &wrqu, NULL);
1607 }
1608 kfree(buf);
Christoph Hellwig95dd91f2005-06-19 01:27:56 +02001609 }
1610 break;
David Kilroy01632fa2008-08-21 23:27:58 +01001611 case HERMES_INQ_CHANNELINFO:
1612 {
1613 struct agere_ext_scan_info *bss;
1614
1615 if (!priv->scan_inprogress) {
1616 printk(KERN_DEBUG "%s: Got chaninfo without scan, "
1617 "len=%d\n", dev->name, len);
1618 break;
1619 }
1620
1621 /* An empty result indicates that the scan is complete */
1622 if (len == 0) {
1623 union iwreq_data wrqu;
1624
1625 /* Scan is no longer in progress */
1626 priv->scan_inprogress = 0;
1627
1628 wrqu.data.length = 0;
1629 wrqu.data.flags = 0;
1630 wireless_send_event(dev, SIOCGIWSCAN, &wrqu, NULL);
1631 break;
1632 }
1633
1634 /* Sanity check */
1635 else if (len > sizeof(*bss)) {
1636 printk(KERN_WARNING
1637 "%s: Ext scan results too large (%d bytes). "
1638 "Truncating results to %zd bytes.\n",
1639 dev->name, len, sizeof(*bss));
1640 len = sizeof(*bss);
1641 } else if (len < (offsetof(struct agere_ext_scan_info,
1642 data) + 2)) {
1643 /* Drop this result now so we don't have to
1644 * keep checking later */
1645 printk(KERN_WARNING
1646 "%s: Ext scan results too short (%d bytes)\n",
1647 dev->name, len);
1648 break;
1649 }
1650
1651 bss = kmalloc(sizeof(*bss), GFP_ATOMIC);
1652 if (bss == NULL)
1653 break;
1654
1655 /* Read scan data */
1656 err = hermes_bap_pread(hw, IRQ_BAP, (void *) bss, len,
1657 infofid, sizeof(info));
1658 if (err) {
1659 kfree(bss);
1660 break;
1661 }
1662
1663 orinoco_add_ext_scan_result(priv, bss);
1664
1665 kfree(bss);
1666 break;
1667 }
Christoph Hellwig95dd91f2005-06-19 01:27:56 +02001668 case HERMES_INQ_SEC_STAT_AGERE:
1669 /* Security status (Agere specific) */
1670 /* Ignore this frame for now */
1671 if (priv->firmware_type == FIRMWARE_TYPE_AGERE)
1672 break;
1673 /* fall through */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001674 default:
1675 printk(KERN_DEBUG "%s: Unknown information frame received: "
1676 "type 0x%04x, length %d\n", dev->name, type, len);
1677 /* We don't actually do anything about it */
1678 break;
1679 }
1680}
1681
1682static void __orinoco_ev_infdrop(struct net_device *dev, hermes_t *hw)
1683{
1684 if (net_ratelimit())
1685 printk(KERN_DEBUG "%s: Information frame lost.\n", dev->name);
1686}
1687
1688/********************************************************************/
1689/* Internal hardware control routines */
1690/********************************************************************/
1691
1692int __orinoco_up(struct net_device *dev)
1693{
1694 struct orinoco_private *priv = netdev_priv(dev);
1695 struct hermes *hw = &priv->hw;
1696 int err;
1697
Christoph Hellwig84d8a2f2005-05-14 17:30:22 +02001698 netif_carrier_off(dev); /* just to make sure */
1699
Linus Torvalds1da177e2005-04-16 15:20:36 -07001700 err = __orinoco_program_rids(dev);
1701 if (err) {
1702 printk(KERN_ERR "%s: Error %d configuring card\n",
1703 dev->name, err);
1704 return err;
1705 }
1706
1707 /* Fire things up again */
1708 hermes_set_irqmask(hw, ORINOCO_INTEN);
1709 err = hermes_enable_port(hw, 0);
1710 if (err) {
1711 printk(KERN_ERR "%s: Error %d enabling MAC port\n",
1712 dev->name, err);
1713 return err;
1714 }
1715
1716 netif_start_queue(dev);
1717
1718 return 0;
1719}
David Kilroy21312662009-02-04 23:05:47 +00001720EXPORT_SYMBOL(__orinoco_up);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001721
1722int __orinoco_down(struct net_device *dev)
1723{
1724 struct orinoco_private *priv = netdev_priv(dev);
1725 struct hermes *hw = &priv->hw;
1726 int err;
1727
1728 netif_stop_queue(dev);
1729
David Kilroya94e8422009-02-04 23:05:44 +00001730 if (!priv->hw_unavailable) {
1731 if (!priv->broken_disableport) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001732 err = hermes_disable_port(hw, 0);
1733 if (err) {
1734 /* Some firmwares (e.g. Intersil 1.3.x) seem
1735 * to have problems disabling the port, oh
1736 * well, too bad. */
1737 printk(KERN_WARNING "%s: Error %d disabling MAC port\n",
1738 dev->name, err);
1739 priv->broken_disableport = 1;
1740 }
1741 }
1742 hermes_set_irqmask(hw, 0);
1743 hermes_write_regn(hw, EVACK, 0xffff);
1744 }
David Kilroy6fe9deb2009-02-04 23:05:43 +00001745
Linus Torvalds1da177e2005-04-16 15:20:36 -07001746 /* firmware will have to reassociate */
1747 netif_carrier_off(dev);
1748 priv->last_linkstatus = 0xffff;
1749
1750 return 0;
1751}
David Kilroy21312662009-02-04 23:05:47 +00001752EXPORT_SYMBOL(__orinoco_down);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001753
Pavel Roskin37a6c612006-04-07 04:10:49 -04001754static int orinoco_allocate_fid(struct net_device *dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001755{
1756 struct orinoco_private *priv = netdev_priv(dev);
1757 struct hermes *hw = &priv->hw;
1758 int err;
1759
Linus Torvalds1da177e2005-04-16 15:20:36 -07001760 err = hermes_allocate(hw, priv->nicbuf_size, &priv->txfid);
David Gibsonb24d4582005-05-12 20:04:16 -04001761 if (err == -EIO && priv->nicbuf_size > TX_NICBUF_SIZE_BUG) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001762 /* Try workaround for old Symbol firmware bug */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001763 priv->nicbuf_size = TX_NICBUF_SIZE_BUG;
1764 err = hermes_allocate(hw, priv->nicbuf_size, &priv->txfid);
David Kilroy21312662009-02-04 23:05:47 +00001765
1766 printk(KERN_WARNING "%s: firmware ALLOC bug detected "
1767 "(old Symbol firmware?). Work around %s\n",
1768 dev->name, err ? "failed!" : "ok.");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001769 }
1770
1771 return err;
1772}
1773
Pavel Roskin37a6c612006-04-07 04:10:49 -04001774int orinoco_reinit_firmware(struct net_device *dev)
1775{
1776 struct orinoco_private *priv = netdev_priv(dev);
1777 struct hermes *hw = &priv->hw;
1778 int err;
1779
1780 err = hermes_init(hw);
Andrey Borzenkov0df6cbb2008-10-12 20:15:43 +04001781 if (priv->do_fw_download && !err) {
1782 err = orinoco_download(priv);
1783 if (err)
1784 priv->do_fw_download = 0;
1785 }
Pavel Roskin37a6c612006-04-07 04:10:49 -04001786 if (!err)
1787 err = orinoco_allocate_fid(dev);
1788
1789 return err;
1790}
David Kilroy21312662009-02-04 23:05:47 +00001791EXPORT_SYMBOL(orinoco_reinit_firmware);
Pavel Roskin37a6c612006-04-07 04:10:49 -04001792
Linus Torvalds1da177e2005-04-16 15:20:36 -07001793static int __orinoco_hw_set_bitrate(struct orinoco_private *priv)
1794{
1795 hermes_t *hw = &priv->hw;
David Kilroyb2f30a02009-02-04 23:05:46 +00001796 int ratemode = priv->bitratemode;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001797 int err = 0;
1798
David Kilroyb2f30a02009-02-04 23:05:46 +00001799 if (ratemode >= BITRATE_TABLE_SIZE) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001800 printk(KERN_ERR "%s: BUG: Invalid bitrate mode %d\n",
David Kilroyb2f30a02009-02-04 23:05:46 +00001801 priv->ndev->name, ratemode);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001802 return -EINVAL;
1803 }
1804
1805 switch (priv->firmware_type) {
1806 case FIRMWARE_TYPE_AGERE:
1807 err = hermes_write_wordrec(hw, USER_BAP,
David Kilroyb2f30a02009-02-04 23:05:46 +00001808 HERMES_RID_CNFTXRATECONTROL,
1809 bitrate_table[ratemode].agere_txratectrl);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001810 break;
1811 case FIRMWARE_TYPE_INTERSIL:
1812 case FIRMWARE_TYPE_SYMBOL:
1813 err = hermes_write_wordrec(hw, USER_BAP,
David Kilroyb2f30a02009-02-04 23:05:46 +00001814 HERMES_RID_CNFTXRATECONTROL,
1815 bitrate_table[ratemode].intersil_txratectrl);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001816 break;
1817 default:
1818 BUG();
1819 }
1820
1821 return err;
1822}
1823
David Kilroycfeb1db2009-02-04 23:05:53 +00001824static int orinoco_hw_get_act_bitrate(struct orinoco_private *priv,
1825 int *bitrate)
1826{
1827 hermes_t *hw = &priv->hw;
1828 int i;
1829 int err = 0;
1830 u16 val;
1831
1832 err = hermes_read_wordrec(hw, USER_BAP,
1833 HERMES_RID_CURRENTTXRATE, &val);
1834 if (err)
1835 return err;
1836
1837 switch (priv->firmware_type) {
1838 case FIRMWARE_TYPE_AGERE: /* Lucent style rate */
1839 /* Note : in Lucent firmware, the return value of
1840 * HERMES_RID_CURRENTTXRATE is the bitrate in Mb/s,
1841 * and therefore is totally different from the
1842 * encoding of HERMES_RID_CNFTXRATECONTROL.
1843 * Don't forget that 6Mb/s is really 5.5Mb/s */
1844 if (val == 6)
1845 *bitrate = 5500000;
1846 else
1847 *bitrate = val * 1000000;
1848 break;
1849 case FIRMWARE_TYPE_INTERSIL: /* Intersil style rate */
1850 case FIRMWARE_TYPE_SYMBOL: /* Symbol style rate */
1851 for (i = 0; i < BITRATE_TABLE_SIZE; i++)
1852 if (bitrate_table[i].intersil_txratectrl == val)
1853 break;
1854
1855 if (i >= BITRATE_TABLE_SIZE)
1856 printk(KERN_INFO "%s: Unable to determine current bitrate (0x%04hx)\n",
1857 priv->ndev->name, val);
1858
1859 *bitrate = bitrate_table[i].bitrate * 100000;
1860 break;
1861 default:
1862 BUG();
1863 }
1864
1865 return err;
1866}
1867
Christoph Hellwig16739b02005-06-19 01:27:51 +02001868/* Set fixed AP address */
1869static int __orinoco_hw_set_wap(struct orinoco_private *priv)
1870{
1871 int roaming_flag;
1872 int err = 0;
1873 hermes_t *hw = &priv->hw;
1874
1875 switch (priv->firmware_type) {
1876 case FIRMWARE_TYPE_AGERE:
1877 /* not supported */
1878 break;
1879 case FIRMWARE_TYPE_INTERSIL:
1880 if (priv->bssid_fixed)
1881 roaming_flag = 2;
1882 else
1883 roaming_flag = 1;
1884
1885 err = hermes_write_wordrec(hw, USER_BAP,
1886 HERMES_RID_CNFROAMINGMODE,
1887 roaming_flag);
1888 break;
1889 case FIRMWARE_TYPE_SYMBOL:
1890 err = HERMES_WRITE_RECORD(hw, USER_BAP,
1891 HERMES_RID_CNFMANDATORYBSSID_SYMBOL,
1892 &priv->desired_bssid);
1893 break;
1894 }
1895 return err;
1896}
1897
Linus Torvalds1da177e2005-04-16 15:20:36 -07001898/* Change the WEP keys and/or the current keys. Can be called
David Kilroyd03032a2008-08-21 23:28:02 +01001899 * either from __orinoco_hw_setup_enc() or directly from
Linus Torvalds1da177e2005-04-16 15:20:36 -07001900 * orinoco_ioctl_setiwencode(). In the later case the association
1901 * with the AP is not broken (if the firmware can handle it),
1902 * which is needed for 802.1x implementations. */
1903static int __orinoco_hw_setup_wepkeys(struct orinoco_private *priv)
1904{
1905 hermes_t *hw = &priv->hw;
1906 int err = 0;
1907
1908 switch (priv->firmware_type) {
1909 case FIRMWARE_TYPE_AGERE:
1910 err = HERMES_WRITE_RECORD(hw, USER_BAP,
1911 HERMES_RID_CNFWEPKEYS_AGERE,
1912 &priv->keys);
1913 if (err)
1914 return err;
1915 err = hermes_write_wordrec(hw, USER_BAP,
1916 HERMES_RID_CNFTXKEY_AGERE,
1917 priv->tx_key);
1918 if (err)
1919 return err;
1920 break;
1921 case FIRMWARE_TYPE_INTERSIL:
1922 case FIRMWARE_TYPE_SYMBOL:
1923 {
1924 int keylen;
1925 int i;
1926
David Kilroyb2f30a02009-02-04 23:05:46 +00001927 /* Force uniform key length to work around
1928 * firmware bugs */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001929 keylen = le16_to_cpu(priv->keys[priv->tx_key].len);
David Kilroy6fe9deb2009-02-04 23:05:43 +00001930
Linus Torvalds1da177e2005-04-16 15:20:36 -07001931 if (keylen > LARGE_KEY_SIZE) {
1932 printk(KERN_ERR "%s: BUG: Key %d has oversize length %d.\n",
1933 priv->ndev->name, priv->tx_key, keylen);
1934 return -E2BIG;
1935 }
1936
1937 /* Write all 4 keys */
David Kilroya94e8422009-02-04 23:05:44 +00001938 for (i = 0; i < ORINOCO_MAX_KEYS; i++) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001939 err = hermes_write_ltv(hw, USER_BAP,
David Kilroyb2f30a02009-02-04 23:05:46 +00001940 HERMES_RID_CNFDEFAULTKEY0 + i,
1941 HERMES_BYTES_TO_RECLEN(keylen),
1942 priv->keys[i].data);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001943 if (err)
1944 return err;
1945 }
1946
1947 /* Write the index of the key used in transmission */
1948 err = hermes_write_wordrec(hw, USER_BAP,
David Kilroyb2f30a02009-02-04 23:05:46 +00001949 HERMES_RID_CNFWEPDEFAULTKEYID,
1950 priv->tx_key);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001951 if (err)
1952 return err;
1953 }
1954 break;
1955 }
1956
1957 return 0;
1958}
1959
David Kilroyd03032a2008-08-21 23:28:02 +01001960static int __orinoco_hw_setup_enc(struct orinoco_private *priv)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001961{
1962 hermes_t *hw = &priv->hw;
1963 int err = 0;
1964 int master_wep_flag;
1965 int auth_flag;
David Kilroy4ae6ee22008-08-21 23:27:59 +01001966 int enc_flag;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001967
David Kilroyd03032a2008-08-21 23:28:02 +01001968 /* Setup WEP keys for WEP and WPA */
1969 if (priv->encode_alg)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001970 __orinoco_hw_setup_wepkeys(priv);
1971
1972 if (priv->wep_restrict)
1973 auth_flag = HERMES_AUTH_SHARED_KEY;
1974 else
1975 auth_flag = HERMES_AUTH_OPEN;
1976
David Kilroyd03032a2008-08-21 23:28:02 +01001977 if (priv->wpa_enabled)
1978 enc_flag = 2;
1979 else if (priv->encode_alg == IW_ENCODE_ALG_WEP)
David Kilroy4ae6ee22008-08-21 23:27:59 +01001980 enc_flag = 1;
1981 else
1982 enc_flag = 0;
1983
Linus Torvalds1da177e2005-04-16 15:20:36 -07001984 switch (priv->firmware_type) {
1985 case FIRMWARE_TYPE_AGERE: /* Agere style WEP */
David Kilroy4ae6ee22008-08-21 23:27:59 +01001986 if (priv->encode_alg == IW_ENCODE_ALG_WEP) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001987 /* Enable the shared-key authentication. */
1988 err = hermes_write_wordrec(hw, USER_BAP,
David Kilroyb2f30a02009-02-04 23:05:46 +00001989 HERMES_RID_CNFAUTHENTICATION_AGERE,
1990 auth_flag);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001991 }
1992 err = hermes_write_wordrec(hw, USER_BAP,
1993 HERMES_RID_CNFWEPENABLED_AGERE,
David Kilroy4ae6ee22008-08-21 23:27:59 +01001994 enc_flag);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001995 if (err)
1996 return err;
David Kilroyd03032a2008-08-21 23:28:02 +01001997
1998 if (priv->has_wpa) {
1999 /* Set WPA key management */
2000 err = hermes_write_wordrec(hw, USER_BAP,
2001 HERMES_RID_CNFSETWPAAUTHMGMTSUITE_AGERE,
2002 priv->key_mgmt);
2003 if (err)
2004 return err;
2005 }
2006
Linus Torvalds1da177e2005-04-16 15:20:36 -07002007 break;
2008
2009 case FIRMWARE_TYPE_INTERSIL: /* Intersil style WEP */
2010 case FIRMWARE_TYPE_SYMBOL: /* Symbol style WEP */
David Kilroy4ae6ee22008-08-21 23:27:59 +01002011 if (priv->encode_alg == IW_ENCODE_ALG_WEP) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002012 if (priv->wep_restrict ||
2013 (priv->firmware_type == FIRMWARE_TYPE_SYMBOL))
2014 master_wep_flag = HERMES_WEP_PRIVACY_INVOKED |
2015 HERMES_WEP_EXCL_UNENCRYPTED;
2016 else
2017 master_wep_flag = HERMES_WEP_PRIVACY_INVOKED;
2018
2019 err = hermes_write_wordrec(hw, USER_BAP,
2020 HERMES_RID_CNFAUTHENTICATION,
2021 auth_flag);
2022 if (err)
2023 return err;
2024 } else
2025 master_wep_flag = 0;
2026
2027 if (priv->iw_mode == IW_MODE_MONITOR)
2028 master_wep_flag |= HERMES_WEP_HOST_DECRYPT;
2029
2030 /* Master WEP setting : on/off */
2031 err = hermes_write_wordrec(hw, USER_BAP,
2032 HERMES_RID_CNFWEPFLAGS_INTERSIL,
2033 master_wep_flag);
2034 if (err)
David Kilroy6fe9deb2009-02-04 23:05:43 +00002035 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002036
2037 break;
2038 }
2039
2040 return 0;
2041}
2042
David Kilroyd03032a2008-08-21 23:28:02 +01002043/* key must be 32 bytes, including the tx and rx MIC keys.
2044 * rsc must be 8 bytes
2045 * tsc must be 8 bytes or NULL
2046 */
2047static int __orinoco_hw_set_tkip_key(hermes_t *hw, int key_idx, int set_tx,
2048 u8 *key, u8 *rsc, u8 *tsc)
2049{
2050 struct {
2051 __le16 idx;
2052 u8 rsc[IW_ENCODE_SEQ_MAX_SIZE];
2053 u8 key[TKIP_KEYLEN];
2054 u8 tx_mic[MIC_KEYLEN];
2055 u8 rx_mic[MIC_KEYLEN];
2056 u8 tsc[IW_ENCODE_SEQ_MAX_SIZE];
2057 } __attribute__ ((packed)) buf;
2058 int ret;
2059 int err;
2060 int k;
2061 u16 xmitting;
2062
2063 key_idx &= 0x3;
2064
2065 if (set_tx)
2066 key_idx |= 0x8000;
2067
2068 buf.idx = cpu_to_le16(key_idx);
2069 memcpy(buf.key, key,
2070 sizeof(buf.key) + sizeof(buf.tx_mic) + sizeof(buf.rx_mic));
2071
2072 if (rsc == NULL)
2073 memset(buf.rsc, 0, sizeof(buf.rsc));
2074 else
2075 memcpy(buf.rsc, rsc, sizeof(buf.rsc));
2076
2077 if (tsc == NULL) {
2078 memset(buf.tsc, 0, sizeof(buf.tsc));
2079 buf.tsc[4] = 0x10;
2080 } else {
2081 memcpy(buf.tsc, tsc, sizeof(buf.tsc));
2082 }
2083
2084 /* Wait upto 100ms for tx queue to empty */
2085 k = 100;
2086 do {
2087 k--;
2088 udelay(1000);
2089 ret = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_TXQUEUEEMPTY,
2090 &xmitting);
2091 if (ret)
2092 break;
2093 } while ((k > 0) && xmitting);
2094
2095 if (k == 0)
2096 ret = -ETIMEDOUT;
2097
2098 err = HERMES_WRITE_RECORD(hw, USER_BAP,
2099 HERMES_RID_CNFADDDEFAULTTKIPKEY_AGERE,
2100 &buf);
2101
2102 return ret ? ret : err;
2103}
2104
2105static int orinoco_clear_tkip_key(struct orinoco_private *priv,
2106 int key_idx)
2107{
2108 hermes_t *hw = &priv->hw;
2109 int err;
2110
2111 memset(&priv->tkip_key[key_idx], 0, sizeof(priv->tkip_key[key_idx]));
2112 err = hermes_write_wordrec(hw, USER_BAP,
2113 HERMES_RID_CNFREMDEFAULTTKIPKEY_AGERE,
2114 key_idx);
2115 if (err)
2116 printk(KERN_WARNING "%s: Error %d clearing TKIP key %d\n",
2117 priv->ndev->name, err, key_idx);
2118 return err;
2119}
2120
Linus Torvalds1da177e2005-04-16 15:20:36 -07002121static int __orinoco_program_rids(struct net_device *dev)
2122{
2123 struct orinoco_private *priv = netdev_priv(dev);
2124 hermes_t *hw = &priv->hw;
2125 int err;
2126 struct hermes_idstring idbuf;
2127
2128 /* Set the MAC address */
2129 err = hermes_write_ltv(hw, USER_BAP, HERMES_RID_CNFOWNMACADDR,
2130 HERMES_BYTES_TO_RECLEN(ETH_ALEN), dev->dev_addr);
2131 if (err) {
2132 printk(KERN_ERR "%s: Error %d setting MAC address\n",
2133 dev->name, err);
2134 return err;
2135 }
2136
2137 /* Set up the link mode */
2138 err = hermes_write_wordrec(hw, USER_BAP, HERMES_RID_CNFPORTTYPE,
2139 priv->port_type);
2140 if (err) {
2141 printk(KERN_ERR "%s: Error %d setting port type\n",
2142 dev->name, err);
2143 return err;
2144 }
2145 /* Set the channel/frequency */
David Gibsond51d8b12005-05-12 20:03:36 -04002146 if (priv->channel != 0 && priv->iw_mode != IW_MODE_INFRA) {
2147 err = hermes_write_wordrec(hw, USER_BAP,
2148 HERMES_RID_CNFOWNCHANNEL,
2149 priv->channel);
2150 if (err) {
2151 printk(KERN_ERR "%s: Error %d setting channel %d\n",
2152 dev->name, err, priv->channel);
2153 return err;
2154 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002155 }
2156
2157 if (priv->has_ibss) {
2158 u16 createibss;
2159
2160 if ((strlen(priv->desired_essid) == 0) && (priv->createibss)) {
2161 printk(KERN_WARNING "%s: This firmware requires an "
2162 "ESSID in IBSS-Ad-Hoc mode.\n", dev->name);
2163 /* With wvlan_cs, in this case, we would crash.
2164 * hopefully, this driver will behave better...
2165 * Jean II */
2166 createibss = 0;
2167 } else {
2168 createibss = priv->createibss;
2169 }
David Kilroy6fe9deb2009-02-04 23:05:43 +00002170
Linus Torvalds1da177e2005-04-16 15:20:36 -07002171 err = hermes_write_wordrec(hw, USER_BAP,
2172 HERMES_RID_CNFCREATEIBSS,
2173 createibss);
2174 if (err) {
2175 printk(KERN_ERR "%s: Error %d setting CREATEIBSS\n",
2176 dev->name, err);
2177 return err;
2178 }
2179 }
2180
Christoph Hellwig16739b02005-06-19 01:27:51 +02002181 /* Set the desired BSSID */
2182 err = __orinoco_hw_set_wap(priv);
2183 if (err) {
2184 printk(KERN_ERR "%s: Error %d setting AP address\n",
2185 dev->name, err);
2186 return err;
2187 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002188 /* Set the desired ESSID */
2189 idbuf.len = cpu_to_le16(strlen(priv->desired_essid));
2190 memcpy(&idbuf.val, priv->desired_essid, sizeof(idbuf.val));
2191 /* WinXP wants partner to configure OWNSSID even in IBSS mode. (jimc) */
2192 err = hermes_write_ltv(hw, USER_BAP, HERMES_RID_CNFOWNSSID,
David Kilroyb2f30a02009-02-04 23:05:46 +00002193 HERMES_BYTES_TO_RECLEN(strlen(priv->desired_essid)+2),
2194 &idbuf);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002195 if (err) {
2196 printk(KERN_ERR "%s: Error %d setting OWNSSID\n",
2197 dev->name, err);
2198 return err;
2199 }
2200 err = hermes_write_ltv(hw, USER_BAP, HERMES_RID_CNFDESIREDSSID,
David Kilroyb2f30a02009-02-04 23:05:46 +00002201 HERMES_BYTES_TO_RECLEN(strlen(priv->desired_essid)+2),
2202 &idbuf);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002203 if (err) {
2204 printk(KERN_ERR "%s: Error %d setting DESIREDSSID\n",
2205 dev->name, err);
2206 return err;
2207 }
2208
2209 /* Set the station name */
2210 idbuf.len = cpu_to_le16(strlen(priv->nick));
2211 memcpy(&idbuf.val, priv->nick, sizeof(idbuf.val));
2212 err = hermes_write_ltv(hw, USER_BAP, HERMES_RID_CNFOWNNAME,
2213 HERMES_BYTES_TO_RECLEN(strlen(priv->nick)+2),
2214 &idbuf);
2215 if (err) {
2216 printk(KERN_ERR "%s: Error %d setting nickname\n",
2217 dev->name, err);
2218 return err;
2219 }
2220
2221 /* Set AP density */
2222 if (priv->has_sensitivity) {
2223 err = hermes_write_wordrec(hw, USER_BAP,
2224 HERMES_RID_CNFSYSTEMSCALE,
2225 priv->ap_density);
2226 if (err) {
David Kilroyb2f30a02009-02-04 23:05:46 +00002227 printk(KERN_WARNING "%s: Error %d setting SYSTEMSCALE. "
Linus Torvalds1da177e2005-04-16 15:20:36 -07002228 "Disabling sensitivity control\n",
2229 dev->name, err);
2230
2231 priv->has_sensitivity = 0;
2232 }
2233 }
2234
2235 /* Set RTS threshold */
2236 err = hermes_write_wordrec(hw, USER_BAP, HERMES_RID_CNFRTSTHRESHOLD,
2237 priv->rts_thresh);
2238 if (err) {
2239 printk(KERN_ERR "%s: Error %d setting RTS threshold\n",
2240 dev->name, err);
2241 return err;
2242 }
2243
2244 /* Set fragmentation threshold or MWO robustness */
2245 if (priv->has_mwo)
2246 err = hermes_write_wordrec(hw, USER_BAP,
2247 HERMES_RID_CNFMWOROBUST_AGERE,
2248 priv->mwo_robust);
2249 else
2250 err = hermes_write_wordrec(hw, USER_BAP,
2251 HERMES_RID_CNFFRAGMENTATIONTHRESHOLD,
2252 priv->frag_thresh);
2253 if (err) {
2254 printk(KERN_ERR "%s: Error %d setting fragmentation\n",
2255 dev->name, err);
2256 return err;
2257 }
2258
2259 /* Set bitrate */
2260 err = __orinoco_hw_set_bitrate(priv);
2261 if (err) {
2262 printk(KERN_ERR "%s: Error %d setting bitrate\n",
2263 dev->name, err);
2264 return err;
2265 }
2266
2267 /* Set power management */
2268 if (priv->has_pm) {
2269 err = hermes_write_wordrec(hw, USER_BAP,
2270 HERMES_RID_CNFPMENABLED,
2271 priv->pm_on);
2272 if (err) {
2273 printk(KERN_ERR "%s: Error %d setting up PM\n",
2274 dev->name, err);
2275 return err;
2276 }
2277
2278 err = hermes_write_wordrec(hw, USER_BAP,
2279 HERMES_RID_CNFMULTICASTRECEIVE,
2280 priv->pm_mcast);
2281 if (err) {
2282 printk(KERN_ERR "%s: Error %d setting up PM\n",
2283 dev->name, err);
2284 return err;
2285 }
2286 err = hermes_write_wordrec(hw, USER_BAP,
2287 HERMES_RID_CNFMAXSLEEPDURATION,
2288 priv->pm_period);
2289 if (err) {
2290 printk(KERN_ERR "%s: Error %d setting up PM\n",
2291 dev->name, err);
2292 return err;
2293 }
2294 err = hermes_write_wordrec(hw, USER_BAP,
2295 HERMES_RID_CNFPMHOLDOVERDURATION,
2296 priv->pm_timeout);
2297 if (err) {
2298 printk(KERN_ERR "%s: Error %d setting up PM\n",
2299 dev->name, err);
2300 return err;
2301 }
2302 }
2303
2304 /* Set preamble - only for Symbol so far... */
2305 if (priv->has_preamble) {
2306 err = hermes_write_wordrec(hw, USER_BAP,
2307 HERMES_RID_CNFPREAMBLE_SYMBOL,
2308 priv->preamble);
2309 if (err) {
2310 printk(KERN_ERR "%s: Error %d setting preamble\n",
2311 dev->name, err);
2312 return err;
2313 }
2314 }
2315
2316 /* Set up encryption */
David Kilroyd03032a2008-08-21 23:28:02 +01002317 if (priv->has_wep || priv->has_wpa) {
2318 err = __orinoco_hw_setup_enc(priv);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002319 if (err) {
David Kilroyd03032a2008-08-21 23:28:02 +01002320 printk(KERN_ERR "%s: Error %d activating encryption\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -07002321 dev->name, err);
2322 return err;
2323 }
2324 }
2325
Christoph Hellwig98c4cae2005-06-19 01:28:06 +02002326 if (priv->iw_mode == IW_MODE_MONITOR) {
2327 /* Enable monitor mode */
2328 dev->type = ARPHRD_IEEE80211;
David Kilroy6fe9deb2009-02-04 23:05:43 +00002329 err = hermes_docmd_wait(hw, HERMES_CMD_TEST |
Christoph Hellwig98c4cae2005-06-19 01:28:06 +02002330 HERMES_TEST_MONITOR, 0, NULL);
2331 } else {
2332 /* Disable monitor mode */
2333 dev->type = ARPHRD_ETHER;
2334 err = hermes_docmd_wait(hw, HERMES_CMD_TEST |
2335 HERMES_TEST_STOP, 0, NULL);
2336 }
2337 if (err)
2338 return err;
2339
Linus Torvalds1da177e2005-04-16 15:20:36 -07002340 /* Set promiscuity / multicast*/
2341 priv->promiscuous = 0;
2342 priv->mc_count = 0;
Herbert Xu932ff272006-06-09 12:20:56 -07002343
2344 /* FIXME: what about netif_tx_lock */
2345 __orinoco_set_multicast_list(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002346
2347 return 0;
2348}
2349
David Kilroy5865d012009-02-04 23:05:54 +00002350static int __orinoco_hw_set_multicast_list(struct orinoco_private *priv,
2351 struct dev_addr_list *mc_list,
2352 int mc_count, int promisc)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002353{
Linus Torvalds1da177e2005-04-16 15:20:36 -07002354 hermes_t *hw = &priv->hw;
2355 int err = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002356
2357 if (promisc != priv->promiscuous) {
2358 err = hermes_write_wordrec(hw, USER_BAP,
2359 HERMES_RID_CNFPROMISCUOUSMODE,
2360 promisc);
2361 if (err) {
2362 printk(KERN_ERR "%s: Error %d setting PROMISCUOUSMODE to 1.\n",
David Kilroy5865d012009-02-04 23:05:54 +00002363 priv->ndev->name, err);
David Kilroy6fe9deb2009-02-04 23:05:43 +00002364 } else
Linus Torvalds1da177e2005-04-16 15:20:36 -07002365 priv->promiscuous = promisc;
2366 }
2367
David Kilroy667d4102008-08-23 19:03:34 +01002368 /* If we're not in promiscuous mode, then we need to set the
2369 * group address if either we want to multicast, or if we were
2370 * multicasting and want to stop */
David Kilroya94e8422009-02-04 23:05:44 +00002371 if (!promisc && (mc_count || priv->mc_count)) {
David Kilroy5865d012009-02-04 23:05:54 +00002372 struct dev_mc_list *p = mc_list;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002373 struct hermes_multicast mclist;
2374 int i;
2375
2376 for (i = 0; i < mc_count; i++) {
2377 /* paranoia: is list shorter than mc_count? */
David Kilroya94e8422009-02-04 23:05:44 +00002378 BUG_ON(!p);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002379 /* paranoia: bad address size in list? */
2380 BUG_ON(p->dmi_addrlen != ETH_ALEN);
David Kilroy6fe9deb2009-02-04 23:05:43 +00002381
Linus Torvalds1da177e2005-04-16 15:20:36 -07002382 memcpy(mclist.addr[i], p->dmi_addr, ETH_ALEN);
2383 p = p->next;
2384 }
David Kilroy6fe9deb2009-02-04 23:05:43 +00002385
Linus Torvalds1da177e2005-04-16 15:20:36 -07002386 if (p)
2387 printk(KERN_WARNING "%s: Multicast list is "
David Kilroy5865d012009-02-04 23:05:54 +00002388 "longer than mc_count\n", priv->ndev->name);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002389
David Kilroy667d4102008-08-23 19:03:34 +01002390 err = hermes_write_ltv(hw, USER_BAP,
2391 HERMES_RID_CNFGROUPADDRESSES,
2392 HERMES_BYTES_TO_RECLEN(mc_count * ETH_ALEN),
2393 &mclist);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002394 if (err)
2395 printk(KERN_ERR "%s: Error %d setting multicast list.\n",
David Kilroy5865d012009-02-04 23:05:54 +00002396 priv->ndev->name, err);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002397 else
2398 priv->mc_count = mc_count;
2399 }
David Kilroy5865d012009-02-04 23:05:54 +00002400 return err;
2401}
2402
2403/* FIXME: return int? */
2404static void
2405__orinoco_set_multicast_list(struct net_device *dev)
2406{
2407 struct orinoco_private *priv = netdev_priv(dev);
2408 int err = 0;
2409 int promisc, mc_count;
2410
2411 /* The Hermes doesn't seem to have an allmulti mode, so we go
2412 * into promiscuous mode and let the upper levels deal. */
2413 if ((dev->flags & IFF_PROMISC) || (dev->flags & IFF_ALLMULTI) ||
2414 (dev->mc_count > MAX_MULTICAST(priv))) {
2415 promisc = 1;
2416 mc_count = 0;
2417 } else {
2418 promisc = 0;
2419 mc_count = dev->mc_count;
2420 }
2421
2422 err = __orinoco_hw_set_multicast_list(priv, dev->mc_list, mc_count,
2423 promisc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002424}
2425
Linus Torvalds1da177e2005-04-16 15:20:36 -07002426/* This must be called from user context, without locks held - use
2427 * schedule_work() */
David Howellsc4028952006-11-22 14:57:56 +00002428static void orinoco_reset(struct work_struct *work)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002429{
David Howellsc4028952006-11-22 14:57:56 +00002430 struct orinoco_private *priv =
2431 container_of(work, struct orinoco_private, reset_work);
2432 struct net_device *dev = priv->ndev;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002433 struct hermes *hw = &priv->hw;
Christoph Hellwig8551cb92005-05-14 17:30:04 +02002434 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002435 unsigned long flags;
2436
2437 if (orinoco_lock(priv, &flags) != 0)
2438 /* When the hardware becomes available again, whatever
2439 * detects that is responsible for re-initializing
2440 * it. So no need for anything further */
2441 return;
2442
2443 netif_stop_queue(dev);
2444
2445 /* Shut off interrupts. Depending on what state the hardware
2446 * is in, this might not work, but we'll try anyway */
2447 hermes_set_irqmask(hw, 0);
2448 hermes_write_regn(hw, EVACK, 0xffff);
2449
2450 priv->hw_unavailable++;
2451 priv->last_linkstatus = 0xffff; /* firmware will have to reassociate */
2452 netif_carrier_off(dev);
2453
2454 orinoco_unlock(priv, &flags);
2455
David Kilroy6fe9deb2009-02-04 23:05:43 +00002456 /* Scanning support: Cleanup of driver struct */
Dan Williams1e3428e2007-10-10 23:56:25 -04002457 orinoco_clear_scan_results(priv, 0);
Christoph Hellwig95dd91f2005-06-19 01:27:56 +02002458 priv->scan_inprogress = 0;
2459
Christoph Hellwig8551cb92005-05-14 17:30:04 +02002460 if (priv->hard_reset) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002461 err = (*priv->hard_reset)(priv);
Christoph Hellwig8551cb92005-05-14 17:30:04 +02002462 if (err) {
2463 printk(KERN_ERR "%s: orinoco_reset: Error %d "
2464 "performing hard reset\n", dev->name, err);
2465 goto disable;
2466 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002467 }
2468
2469 err = orinoco_reinit_firmware(dev);
2470 if (err) {
2471 printk(KERN_ERR "%s: orinoco_reset: Error %d re-initializing firmware\n",
2472 dev->name, err);
Christoph Hellwig8551cb92005-05-14 17:30:04 +02002473 goto disable;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002474 }
2475
David Kilroyb2f30a02009-02-04 23:05:46 +00002476 /* This has to be called from user context */
2477 spin_lock_irq(&priv->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002478
2479 priv->hw_unavailable--;
2480
2481 /* priv->open or priv->hw_unavailable might have changed while
2482 * we dropped the lock */
David Kilroya94e8422009-02-04 23:05:44 +00002483 if (priv->open && (!priv->hw_unavailable)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002484 err = __orinoco_up(dev);
2485 if (err) {
2486 printk(KERN_ERR "%s: orinoco_reset: Error %d reenabling card\n",
2487 dev->name, err);
2488 } else
2489 dev->trans_start = jiffies;
2490 }
2491
2492 spin_unlock_irq(&priv->lock);
2493
2494 return;
Christoph Hellwig8551cb92005-05-14 17:30:04 +02002495 disable:
2496 hermes_set_irqmask(hw, 0);
2497 netif_device_detach(dev);
2498 printk(KERN_ERR "%s: Device has been disabled!\n", dev->name);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002499}
2500
2501/********************************************************************/
2502/* Interrupt handler */
2503/********************************************************************/
2504
2505static void __orinoco_ev_tick(struct net_device *dev, hermes_t *hw)
2506{
2507 printk(KERN_DEBUG "%s: TICK\n", dev->name);
2508}
2509
2510static void __orinoco_ev_wterr(struct net_device *dev, hermes_t *hw)
2511{
2512 /* This seems to happen a fair bit under load, but ignoring it
2513 seems to work fine...*/
2514 printk(KERN_DEBUG "%s: MAC controller error (WTERR). Ignoring.\n",
2515 dev->name);
2516}
2517
David Howells7d12e782006-10-05 14:55:46 +01002518irqreturn_t orinoco_interrupt(int irq, void *dev_id)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002519{
Jeff Garzikc31f28e2006-10-06 14:56:04 -04002520 struct net_device *dev = dev_id;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002521 struct orinoco_private *priv = netdev_priv(dev);
2522 hermes_t *hw = &priv->hw;
2523 int count = MAX_IRQLOOPS_PER_IRQ;
2524 u16 evstat, events;
David Kilroy21312662009-02-04 23:05:47 +00002525 /* These are used to detect a runaway interrupt situation.
2526 *
2527 * If we get more than MAX_IRQLOOPS_PER_JIFFY iterations in a jiffy,
2528 * we panic and shut down the hardware
2529 */
2530 /* jiffies value the last time we were called */
2531 static int last_irq_jiffy; /* = 0 */
2532 static int loops_this_jiffy; /* = 0 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002533 unsigned long flags;
2534
2535 if (orinoco_lock(priv, &flags) != 0) {
2536 /* If hw is unavailable - we don't know if the irq was
2537 * for us or not */
2538 return IRQ_HANDLED;
2539 }
2540
2541 evstat = hermes_read_regn(hw, EVSTAT);
2542 events = evstat & hw->inten;
David Kilroya94e8422009-02-04 23:05:44 +00002543 if (!events) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002544 orinoco_unlock(priv, &flags);
2545 return IRQ_NONE;
2546 }
David Kilroy6fe9deb2009-02-04 23:05:43 +00002547
Linus Torvalds1da177e2005-04-16 15:20:36 -07002548 if (jiffies != last_irq_jiffy)
2549 loops_this_jiffy = 0;
2550 last_irq_jiffy = jiffies;
2551
2552 while (events && count--) {
2553 if (++loops_this_jiffy > MAX_IRQLOOPS_PER_JIFFY) {
2554 printk(KERN_WARNING "%s: IRQ handler is looping too "
2555 "much! Resetting.\n", dev->name);
2556 /* Disable interrupts for now */
2557 hermes_set_irqmask(hw, 0);
2558 schedule_work(&priv->reset_work);
2559 break;
2560 }
2561
2562 /* Check the card hasn't been removed */
David Kilroya94e8422009-02-04 23:05:44 +00002563 if (!hermes_present(hw)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002564 DEBUG(0, "orinoco_interrupt(): card removed\n");
2565 break;
2566 }
2567
2568 if (events & HERMES_EV_TICK)
2569 __orinoco_ev_tick(dev, hw);
2570 if (events & HERMES_EV_WTERR)
2571 __orinoco_ev_wterr(dev, hw);
2572 if (events & HERMES_EV_INFDROP)
2573 __orinoco_ev_infdrop(dev, hw);
2574 if (events & HERMES_EV_INFO)
2575 __orinoco_ev_info(dev, hw);
2576 if (events & HERMES_EV_RX)
2577 __orinoco_ev_rx(dev, hw);
2578 if (events & HERMES_EV_TXEXC)
2579 __orinoco_ev_txexc(dev, hw);
2580 if (events & HERMES_EV_TX)
2581 __orinoco_ev_tx(dev, hw);
2582 if (events & HERMES_EV_ALLOC)
2583 __orinoco_ev_alloc(dev, hw);
David Kilroy6fe9deb2009-02-04 23:05:43 +00002584
Christoph Hellwig84d8a2f2005-05-14 17:30:22 +02002585 hermes_write_regn(hw, EVACK, evstat);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002586
2587 evstat = hermes_read_regn(hw, EVSTAT);
2588 events = evstat & hw->inten;
2589 };
2590
2591 orinoco_unlock(priv, &flags);
2592 return IRQ_HANDLED;
2593}
David Kilroy21312662009-02-04 23:05:47 +00002594EXPORT_SYMBOL(orinoco_interrupt);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002595
2596/********************************************************************/
David Kilroy39d1ffe2008-11-22 10:37:28 +00002597/* Power management */
2598/********************************************************************/
2599#if defined(CONFIG_PM_SLEEP) && !defined(CONFIG_HERMES_CACHE_FW_ON_INIT)
2600static int orinoco_pm_notifier(struct notifier_block *notifier,
2601 unsigned long pm_event,
2602 void *unused)
2603{
2604 struct orinoco_private *priv = container_of(notifier,
2605 struct orinoco_private,
2606 pm_notifier);
2607
2608 /* All we need to do is cache the firmware before suspend, and
2609 * release it when we come out.
2610 *
2611 * Only need to do this if we're downloading firmware. */
2612 if (!priv->do_fw_download)
2613 return NOTIFY_DONE;
2614
2615 switch (pm_event) {
2616 case PM_HIBERNATION_PREPARE:
2617 case PM_SUSPEND_PREPARE:
2618 orinoco_cache_fw(priv, 0);
2619 break;
2620
2621 case PM_POST_RESTORE:
2622 /* Restore from hibernation failed. We need to clean
2623 * up in exactly the same way, so fall through. */
2624 case PM_POST_HIBERNATION:
2625 case PM_POST_SUSPEND:
2626 orinoco_uncache_fw(priv);
2627 break;
2628
2629 case PM_RESTORE_PREPARE:
2630 default:
2631 break;
2632 }
2633
2634 return NOTIFY_DONE;
2635}
2636#else /* !PM_SLEEP || HERMES_CACHE_FW_ON_INIT */
2637#define orinoco_pm_notifier NULL
2638#endif
2639
2640/********************************************************************/
Linus Torvalds1da177e2005-04-16 15:20:36 -07002641/* Initialization */
2642/********************************************************************/
2643
2644struct comp_id {
2645 u16 id, variant, major, minor;
2646} __attribute__ ((packed));
2647
2648static inline fwtype_t determine_firmware_type(struct comp_id *nic_id)
2649{
2650 if (nic_id->id < 0x8000)
2651 return FIRMWARE_TYPE_AGERE;
2652 else if (nic_id->id == 0x8000 && nic_id->major == 0)
2653 return FIRMWARE_TYPE_SYMBOL;
2654 else
2655 return FIRMWARE_TYPE_INTERSIL;
2656}
2657
2658/* Set priv->firmware type, determine firmware properties */
2659static int determine_firmware(struct net_device *dev)
2660{
2661 struct orinoco_private *priv = netdev_priv(dev);
2662 hermes_t *hw = &priv->hw;
2663 int err;
2664 struct comp_id nic_id, sta_id;
2665 unsigned int firmver;
Hennerich, Michaeldde6d432007-02-05 16:41:35 -08002666 char tmp[SYMBOL_MAX_VER_LEN+1] __attribute__((aligned(2)));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002667
2668 /* Get the hardware version */
2669 err = HERMES_READ_RECORD(hw, USER_BAP, HERMES_RID_NICID, &nic_id);
2670 if (err) {
2671 printk(KERN_ERR "%s: Cannot read hardware identity: error %d\n",
2672 dev->name, err);
2673 return err;
2674 }
2675
2676 le16_to_cpus(&nic_id.id);
2677 le16_to_cpus(&nic_id.variant);
2678 le16_to_cpus(&nic_id.major);
2679 le16_to_cpus(&nic_id.minor);
2680 printk(KERN_DEBUG "%s: Hardware identity %04x:%04x:%04x:%04x\n",
2681 dev->name, nic_id.id, nic_id.variant,
2682 nic_id.major, nic_id.minor);
2683
2684 priv->firmware_type = determine_firmware_type(&nic_id);
2685
2686 /* Get the firmware version */
2687 err = HERMES_READ_RECORD(hw, USER_BAP, HERMES_RID_STAID, &sta_id);
2688 if (err) {
2689 printk(KERN_ERR "%s: Cannot read station identity: error %d\n",
2690 dev->name, err);
2691 return err;
2692 }
2693
2694 le16_to_cpus(&sta_id.id);
2695 le16_to_cpus(&sta_id.variant);
2696 le16_to_cpus(&sta_id.major);
2697 le16_to_cpus(&sta_id.minor);
2698 printk(KERN_DEBUG "%s: Station identity %04x:%04x:%04x:%04x\n",
2699 dev->name, sta_id.id, sta_id.variant,
2700 sta_id.major, sta_id.minor);
2701
2702 switch (sta_id.id) {
2703 case 0x15:
2704 printk(KERN_ERR "%s: Primary firmware is active\n",
2705 dev->name);
2706 return -ENODEV;
2707 case 0x14b:
2708 printk(KERN_ERR "%s: Tertiary firmware is active\n",
2709 dev->name);
2710 return -ENODEV;
2711 case 0x1f: /* Intersil, Agere, Symbol Spectrum24 */
2712 case 0x21: /* Symbol Spectrum24 Trilogy */
2713 break;
2714 default:
2715 printk(KERN_NOTICE "%s: Unknown station ID, please report\n",
2716 dev->name);
2717 break;
2718 }
2719
2720 /* Default capabilities */
2721 priv->has_sensitivity = 1;
2722 priv->has_mwo = 0;
2723 priv->has_preamble = 0;
2724 priv->has_port3 = 1;
2725 priv->has_ibss = 1;
2726 priv->has_wep = 0;
2727 priv->has_big_wep = 0;
David Kilroy6eecad72008-08-21 23:27:56 +01002728 priv->has_alt_txcntl = 0;
David Kilroy01632fa2008-08-21 23:27:58 +01002729 priv->has_ext_scan = 0;
David Kilroyd03032a2008-08-21 23:28:02 +01002730 priv->has_wpa = 0;
David Kilroy3994d502008-08-21 23:27:54 +01002731 priv->do_fw_download = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002732
2733 /* Determine capabilities from the firmware version */
2734 switch (priv->firmware_type) {
2735 case FIRMWARE_TYPE_AGERE:
2736 /* Lucent Wavelan IEEE, Lucent Orinoco, Cabletron RoamAbout,
2737 ELSA, Melco, HP, IBM, Dell 1150, Compaq 110/210 */
2738 snprintf(priv->fw_name, sizeof(priv->fw_name) - 1,
2739 "Lucent/Agere %d.%02d", sta_id.major, sta_id.minor);
2740
2741 firmver = ((unsigned long)sta_id.major << 16) | sta_id.minor;
2742
2743 priv->has_ibss = (firmver >= 0x60006);
2744 priv->has_wep = (firmver >= 0x40020);
2745 priv->has_big_wep = 1; /* FIXME: this is wrong - how do we tell
2746 Gold cards from the others? */
2747 priv->has_mwo = (firmver >= 0x60000);
2748 priv->has_pm = (firmver >= 0x40020); /* Don't work in 7.52 ? */
2749 priv->ibss_port = 1;
Christoph Hellwig95dd91f2005-06-19 01:27:56 +02002750 priv->has_hostscan = (firmver >= 0x8000a);
David Kilroy3994d502008-08-21 23:27:54 +01002751 priv->do_fw_download = 1;
Christoph Hellwig98c4cae2005-06-19 01:28:06 +02002752 priv->broken_monitor = (firmver >= 0x80000);
David Kilroy6eecad72008-08-21 23:27:56 +01002753 priv->has_alt_txcntl = (firmver >= 0x90000); /* All 9.x ? */
David Kilroy01632fa2008-08-21 23:27:58 +01002754 priv->has_ext_scan = (firmver >= 0x90000); /* All 9.x ? */
David Kilroyd03032a2008-08-21 23:28:02 +01002755 priv->has_wpa = (firmver >= 0x9002a);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002756 /* Tested with Agere firmware :
2757 * 1.16 ; 4.08 ; 4.52 ; 6.04 ; 6.16 ; 7.28 => Jean II
2758 * Tested CableTron firmware : 4.32 => Anton */
2759 break;
2760 case FIRMWARE_TYPE_SYMBOL:
2761 /* Symbol , 3Com AirConnect, Intel, Ericsson WLAN */
2762 /* Intel MAC : 00:02:B3:* */
2763 /* 3Com MAC : 00:50:DA:* */
2764 memset(tmp, 0, sizeof(tmp));
2765 /* Get the Symbol firmware version */
2766 err = hermes_read_ltv(hw, USER_BAP,
2767 HERMES_RID_SECONDARYVERSION_SYMBOL,
2768 SYMBOL_MAX_VER_LEN, NULL, &tmp);
2769 if (err) {
2770 printk(KERN_WARNING
David Kilroyb2f30a02009-02-04 23:05:46 +00002771 "%s: Error %d reading Symbol firmware info. "
2772 "Wildly guessing capabilities...\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -07002773 dev->name, err);
2774 firmver = 0;
2775 tmp[0] = '\0';
2776 } else {
2777 /* The firmware revision is a string, the format is
2778 * something like : "V2.20-01".
2779 * Quick and dirty parsing... - Jean II
2780 */
David Kilroyb2f30a02009-02-04 23:05:46 +00002781 firmver = ((tmp[1] - '0') << 16)
2782 | ((tmp[3] - '0') << 12)
2783 | ((tmp[4] - '0') << 8)
2784 | ((tmp[6] - '0') << 4)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002785 | (tmp[7] - '0');
2786
2787 tmp[SYMBOL_MAX_VER_LEN] = '\0';
2788 }
2789
2790 snprintf(priv->fw_name, sizeof(priv->fw_name) - 1,
2791 "Symbol %s", tmp);
2792
2793 priv->has_ibss = (firmver >= 0x20000);
2794 priv->has_wep = (firmver >= 0x15012);
2795 priv->has_big_wep = (firmver >= 0x20000);
David Kilroy6fe9deb2009-02-04 23:05:43 +00002796 priv->has_pm = (firmver >= 0x20000 && firmver < 0x22000) ||
Linus Torvalds1da177e2005-04-16 15:20:36 -07002797 (firmver >= 0x29000 && firmver < 0x30000) ||
2798 firmver >= 0x31000;
2799 priv->has_preamble = (firmver >= 0x20000);
2800 priv->ibss_port = 4;
David Kilroy3994d502008-08-21 23:27:54 +01002801
2802 /* Symbol firmware is found on various cards, but
2803 * there has been no attempt to check firmware
2804 * download on non-spectrum_cs based cards.
2805 *
2806 * Given that the Agere firmware download works
2807 * differently, we should avoid doing a firmware
2808 * download with the Symbol algorithm on non-spectrum
2809 * cards.
2810 *
2811 * For now we can identify a spectrum_cs based card
2812 * because it has a firmware reset function.
2813 */
2814 priv->do_fw_download = (priv->stop_fw != NULL);
2815
David Kilroy6fe9deb2009-02-04 23:05:43 +00002816 priv->broken_disableport = (firmver == 0x25013) ||
David Kilroyb2f30a02009-02-04 23:05:46 +00002817 (firmver >= 0x30000 && firmver <= 0x31000);
Christoph Hellwig95dd91f2005-06-19 01:27:56 +02002818 priv->has_hostscan = (firmver >= 0x31001) ||
2819 (firmver >= 0x29057 && firmver < 0x30000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002820 /* Tested with Intel firmware : 0x20015 => Jean II */
2821 /* Tested with 3Com firmware : 0x15012 & 0x22001 => Jean II */
2822 break;
2823 case FIRMWARE_TYPE_INTERSIL:
2824 /* D-Link, Linksys, Adtron, ZoomAir, and many others...
2825 * Samsung, Compaq 100/200 and Proxim are slightly
2826 * different and less well tested */
2827 /* D-Link MAC : 00:40:05:* */
2828 /* Addtron MAC : 00:90:D1:* */
2829 snprintf(priv->fw_name, sizeof(priv->fw_name) - 1,
2830 "Intersil %d.%d.%d", sta_id.major, sta_id.minor,
2831 sta_id.variant);
2832
2833 firmver = ((unsigned long)sta_id.major << 16) |
2834 ((unsigned long)sta_id.minor << 8) | sta_id.variant;
2835
2836 priv->has_ibss = (firmver >= 0x000700); /* FIXME */
2837 priv->has_big_wep = priv->has_wep = (firmver >= 0x000800);
2838 priv->has_pm = (firmver >= 0x000700);
Christoph Hellwig95dd91f2005-06-19 01:27:56 +02002839 priv->has_hostscan = (firmver >= 0x010301);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002840
2841 if (firmver >= 0x000800)
2842 priv->ibss_port = 0;
2843 else {
2844 printk(KERN_NOTICE "%s: Intersil firmware earlier "
2845 "than v0.8.x - several features not supported\n",
2846 dev->name);
2847 priv->ibss_port = 1;
2848 }
2849 break;
2850 }
2851 printk(KERN_DEBUG "%s: Firmware determined as %s\n", dev->name,
2852 priv->fw_name);
2853
2854 return 0;
2855}
2856
2857static int orinoco_init(struct net_device *dev)
2858{
2859 struct orinoco_private *priv = netdev_priv(dev);
2860 hermes_t *hw = &priv->hw;
2861 int err = 0;
2862 struct hermes_idstring nickbuf;
2863 u16 reclen;
2864 int len;
2865
Linus Torvalds1da177e2005-04-16 15:20:36 -07002866 /* No need to lock, the hw_unavailable flag is already set in
2867 * alloc_orinocodev() */
Johannes Berg2c7060022008-10-30 22:09:54 +01002868 priv->nicbuf_size = IEEE80211_MAX_FRAME_LEN + ETH_HLEN;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002869
2870 /* Initialize the firmware */
Pavel Roskin37a6c612006-04-07 04:10:49 -04002871 err = hermes_init(hw);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002872 if (err != 0) {
2873 printk(KERN_ERR "%s: failed to initialize firmware (err = %d)\n",
2874 dev->name, err);
2875 goto out;
2876 }
2877
2878 err = determine_firmware(dev);
2879 if (err != 0) {
2880 printk(KERN_ERR "%s: Incompatible firmware, aborting\n",
2881 dev->name);
2882 goto out;
2883 }
2884
David Kilroy3994d502008-08-21 23:27:54 +01002885 if (priv->do_fw_download) {
David Kilroy39d1ffe2008-11-22 10:37:28 +00002886#ifdef CONFIG_HERMES_CACHE_FW_ON_INIT
David Kilroy74734312008-11-22 10:37:25 +00002887 orinoco_cache_fw(priv, 0);
David Kilroy39d1ffe2008-11-22 10:37:28 +00002888#endif
David Kilroy74734312008-11-22 10:37:25 +00002889
David Kilroy3994d502008-08-21 23:27:54 +01002890 err = orinoco_download(priv);
2891 if (err)
2892 priv->do_fw_download = 0;
2893
2894 /* Check firmware version again */
2895 err = determine_firmware(dev);
2896 if (err != 0) {
2897 printk(KERN_ERR "%s: Incompatible firmware, aborting\n",
2898 dev->name);
2899 goto out;
2900 }
2901 }
2902
Linus Torvalds1da177e2005-04-16 15:20:36 -07002903 if (priv->has_port3)
David Kilroyb2f30a02009-02-04 23:05:46 +00002904 printk(KERN_DEBUG "%s: Ad-hoc demo mode supported\n",
2905 dev->name);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002906 if (priv->has_ibss)
2907 printk(KERN_DEBUG "%s: IEEE standard IBSS ad-hoc mode supported\n",
2908 dev->name);
2909 if (priv->has_wep) {
David Kilroy21312662009-02-04 23:05:47 +00002910 printk(KERN_DEBUG "%s: WEP supported, %s-bit key\n", dev->name,
2911 priv->has_big_wep ? "104" : "40");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002912 }
David Kilroy23edcc42008-08-21 23:28:05 +01002913 if (priv->has_wpa) {
David Kilroyd03032a2008-08-21 23:28:02 +01002914 printk(KERN_DEBUG "%s: WPA-PSK supported\n", dev->name);
David Kilroy23edcc42008-08-21 23:28:05 +01002915 if (orinoco_mic_init(priv)) {
2916 printk(KERN_ERR "%s: Failed to setup MIC crypto "
2917 "algorithm. Disabling WPA support\n", dev->name);
2918 priv->has_wpa = 0;
2919 }
2920 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002921
David Kilroy01632fa2008-08-21 23:27:58 +01002922 /* Now we have the firmware capabilities, allocate appropiate
2923 * sized scan buffers */
2924 if (orinoco_bss_data_allocate(priv))
2925 goto out;
2926 orinoco_bss_data_init(priv);
2927
Linus Torvalds1da177e2005-04-16 15:20:36 -07002928 /* Get the MAC address */
2929 err = hermes_read_ltv(hw, USER_BAP, HERMES_RID_CNFOWNMACADDR,
2930 ETH_ALEN, NULL, dev->dev_addr);
2931 if (err) {
2932 printk(KERN_WARNING "%s: failed to read MAC address!\n",
2933 dev->name);
2934 goto out;
2935 }
2936
Johannes Berge1749612008-10-27 15:59:26 -07002937 printk(KERN_DEBUG "%s: MAC address %pM\n",
2938 dev->name, dev->dev_addr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002939
2940 /* Get the station name */
2941 err = hermes_read_ltv(hw, USER_BAP, HERMES_RID_CNFOWNNAME,
2942 sizeof(nickbuf), &reclen, &nickbuf);
2943 if (err) {
2944 printk(KERN_ERR "%s: failed to read station name\n",
2945 dev->name);
2946 goto out;
2947 }
2948 if (nickbuf.len)
2949 len = min(IW_ESSID_MAX_SIZE, (int)le16_to_cpu(nickbuf.len));
2950 else
2951 len = min(IW_ESSID_MAX_SIZE, 2 * reclen);
2952 memcpy(priv->nick, &nickbuf.val, len);
2953 priv->nick[len] = '\0';
2954
2955 printk(KERN_DEBUG "%s: Station name \"%s\"\n", dev->name, priv->nick);
2956
Pavel Roskin37a6c612006-04-07 04:10:49 -04002957 err = orinoco_allocate_fid(dev);
2958 if (err) {
2959 printk(KERN_ERR "%s: failed to allocate NIC buffer!\n",
2960 dev->name);
2961 goto out;
2962 }
2963
Linus Torvalds1da177e2005-04-16 15:20:36 -07002964 /* Get allowed channels */
2965 err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_CHANNELLIST,
2966 &priv->channel_mask);
2967 if (err) {
2968 printk(KERN_ERR "%s: failed to read channel list!\n",
2969 dev->name);
2970 goto out;
2971 }
2972
2973 /* Get initial AP density */
2974 err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_CNFSYSTEMSCALE,
2975 &priv->ap_density);
David Kilroy566f2d92009-02-04 23:05:45 +00002976 if (err || priv->ap_density < 1 || priv->ap_density > 3)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002977 priv->has_sensitivity = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002978
2979 /* Get initial RTS threshold */
2980 err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_CNFRTSTHRESHOLD,
2981 &priv->rts_thresh);
2982 if (err) {
2983 printk(KERN_ERR "%s: failed to read RTS threshold!\n",
2984 dev->name);
2985 goto out;
2986 }
2987
2988 /* Get initial fragmentation settings */
2989 if (priv->has_mwo)
2990 err = hermes_read_wordrec(hw, USER_BAP,
2991 HERMES_RID_CNFMWOROBUST_AGERE,
2992 &priv->mwo_robust);
2993 else
David Kilroyb2f30a02009-02-04 23:05:46 +00002994 err = hermes_read_wordrec(hw, USER_BAP,
2995 HERMES_RID_CNFFRAGMENTATIONTHRESHOLD,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002996 &priv->frag_thresh);
2997 if (err) {
2998 printk(KERN_ERR "%s: failed to read fragmentation settings!\n",
2999 dev->name);
3000 goto out;
3001 }
3002
3003 /* Power management setup */
3004 if (priv->has_pm) {
3005 priv->pm_on = 0;
3006 priv->pm_mcast = 1;
3007 err = hermes_read_wordrec(hw, USER_BAP,
3008 HERMES_RID_CNFMAXSLEEPDURATION,
3009 &priv->pm_period);
3010 if (err) {
3011 printk(KERN_ERR "%s: failed to read power management period!\n",
3012 dev->name);
3013 goto out;
3014 }
3015 err = hermes_read_wordrec(hw, USER_BAP,
3016 HERMES_RID_CNFPMHOLDOVERDURATION,
3017 &priv->pm_timeout);
3018 if (err) {
3019 printk(KERN_ERR "%s: failed to read power management timeout!\n",
3020 dev->name);
3021 goto out;
3022 }
3023 }
3024
3025 /* Preamble setup */
3026 if (priv->has_preamble) {
3027 err = hermes_read_wordrec(hw, USER_BAP,
3028 HERMES_RID_CNFPREAMBLE_SYMBOL,
3029 &priv->preamble);
3030 if (err)
3031 goto out;
3032 }
David Kilroy6fe9deb2009-02-04 23:05:43 +00003033
Linus Torvalds1da177e2005-04-16 15:20:36 -07003034 /* Set up the default configuration */
3035 priv->iw_mode = IW_MODE_INFRA;
3036 /* By default use IEEE/IBSS ad-hoc mode if we have it */
David Kilroya94e8422009-02-04 23:05:44 +00003037 priv->prefer_port3 = priv->has_port3 && (!priv->has_ibss);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003038 set_port_type(priv);
David Gibsond51d8b12005-05-12 20:03:36 -04003039 priv->channel = 0; /* use firmware default */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003040
3041 priv->promiscuous = 0;
David Kilroy4ae6ee22008-08-21 23:27:59 +01003042 priv->encode_alg = IW_ENCODE_ALG_NONE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003043 priv->tx_key = 0;
David Kilroyd03032a2008-08-21 23:28:02 +01003044 priv->wpa_enabled = 0;
3045 priv->tkip_cm_active = 0;
3046 priv->key_mgmt = 0;
3047 priv->wpa_ie_len = 0;
3048 priv->wpa_ie = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003049
Linus Torvalds1da177e2005-04-16 15:20:36 -07003050 /* Make the hardware available, as long as it hasn't been
3051 * removed elsewhere (e.g. by PCMCIA hot unplug) */
3052 spin_lock_irq(&priv->lock);
3053 priv->hw_unavailable--;
3054 spin_unlock_irq(&priv->lock);
3055
3056 printk(KERN_DEBUG "%s: ready\n", dev->name);
3057
3058 out:
Linus Torvalds1da177e2005-04-16 15:20:36 -07003059 return err;
3060}
3061
Andrey Borzenkov89ea4092009-01-21 20:46:46 +03003062static const struct net_device_ops orinoco_netdev_ops = {
3063 .ndo_init = orinoco_init,
3064 .ndo_open = orinoco_open,
3065 .ndo_stop = orinoco_stop,
3066 .ndo_start_xmit = orinoco_xmit,
3067 .ndo_set_multicast_list = orinoco_set_multicast_list,
3068 .ndo_change_mtu = orinoco_change_mtu,
3069 .ndo_tx_timeout = orinoco_tx_timeout,
3070 .ndo_get_stats = orinoco_get_stats,
3071};
3072
David Kilroy3994d502008-08-21 23:27:54 +01003073struct net_device
3074*alloc_orinocodev(int sizeof_card,
3075 struct device *device,
3076 int (*hard_reset)(struct orinoco_private *),
3077 int (*stop_fw)(struct orinoco_private *, int))
Linus Torvalds1da177e2005-04-16 15:20:36 -07003078{
3079 struct net_device *dev;
3080 struct orinoco_private *priv;
3081
3082 dev = alloc_etherdev(sizeof(struct orinoco_private) + sizeof_card);
Andrey Borzenkove129a942009-01-21 21:55:29 +03003083 if (!dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003084 return NULL;
3085 priv = netdev_priv(dev);
3086 priv->ndev = dev;
3087 if (sizeof_card)
Christoph Hellwig84d8a2f2005-05-14 17:30:22 +02003088 priv->card = (void *)((unsigned long)priv
Linus Torvalds1da177e2005-04-16 15:20:36 -07003089 + sizeof(struct orinoco_private));
3090 else
3091 priv->card = NULL;
David Kilroy3994d502008-08-21 23:27:54 +01003092 priv->dev = device;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003093
3094 /* Setup / override net_device fields */
Andrey Borzenkov89ea4092009-01-21 20:46:46 +03003095 dev->netdev_ops = &orinoco_netdev_ops;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003096 dev->watchdog_timeo = HZ; /* 1 second timeout */
Christoph Hellwig1fab2e82005-06-19 01:27:40 +02003097 dev->ethtool_ops = &orinoco_ethtool_ops;
Andrey Borzenkove129a942009-01-21 21:55:29 +03003098 dev->wireless_handlers = &orinoco_handler_def;
Pavel Roskin343c6862005-09-09 18:43:02 -04003099#ifdef WIRELESS_SPY
3100 priv->wireless_data.spy_data = &priv->spy_data;
3101 dev->wireless_data = &priv->wireless_data;
3102#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -07003103 /* we use the default eth_mac_addr for setting the MAC addr */
3104
David Kilroy23edcc42008-08-21 23:28:05 +01003105 /* Reserve space in skb for the SNAP header */
3106 dev->hard_header_len += ENCAPS_OVERHEAD;
3107
Linus Torvalds1da177e2005-04-16 15:20:36 -07003108 /* Set up default callbacks */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003109 priv->hard_reset = hard_reset;
David Kilroy3994d502008-08-21 23:27:54 +01003110 priv->stop_fw = stop_fw;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003111
3112 spin_lock_init(&priv->lock);
3113 priv->open = 0;
3114 priv->hw_unavailable = 1; /* orinoco_init() must clear this
3115 * before anything else touches the
3116 * hardware */
David Howellsc4028952006-11-22 14:57:56 +00003117 INIT_WORK(&priv->reset_work, orinoco_reset);
3118 INIT_WORK(&priv->join_work, orinoco_join_ap);
3119 INIT_WORK(&priv->wevent_work, orinoco_send_wevents);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003120
David Kilroy31afcef2008-08-21 23:28:04 +01003121 INIT_LIST_HEAD(&priv->rx_list);
3122 tasklet_init(&priv->rx_tasklet, orinoco_rx_isr_tasklet,
3123 (unsigned long) dev);
3124
Linus Torvalds1da177e2005-04-16 15:20:36 -07003125 netif_carrier_off(dev);
3126 priv->last_linkstatus = 0xffff;
3127
David Kilroy2cea7b22008-11-22 10:37:26 +00003128 priv->cached_pri_fw = NULL;
Andrey Borzenkov4fb30782008-10-19 12:06:11 +04003129 priv->cached_fw = NULL;
3130
David Kilroy39d1ffe2008-11-22 10:37:28 +00003131 /* Register PM notifiers */
3132 priv->pm_notifier.notifier_call = orinoco_pm_notifier;
3133 register_pm_notifier(&priv->pm_notifier);
3134
Linus Torvalds1da177e2005-04-16 15:20:36 -07003135 return dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003136}
David Kilroy21312662009-02-04 23:05:47 +00003137EXPORT_SYMBOL(alloc_orinocodev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003138
3139void free_orinocodev(struct net_device *dev)
3140{
Christoph Hellwig95dd91f2005-06-19 01:27:56 +02003141 struct orinoco_private *priv = netdev_priv(dev);
David Kilroy20953ad2009-01-07 00:23:55 +00003142 struct orinoco_rx_data *rx_data, *temp;
Christoph Hellwig95dd91f2005-06-19 01:27:56 +02003143
David Kilroy20953ad2009-01-07 00:23:55 +00003144 /* If the tasklet is scheduled when we call tasklet_kill it
3145 * will run one final time. However the tasklet will only
3146 * drain priv->rx_list if the hw is still available. */
David Kilroy31afcef2008-08-21 23:28:04 +01003147 tasklet_kill(&priv->rx_tasklet);
David Kilroy74734312008-11-22 10:37:25 +00003148
David Kilroy20953ad2009-01-07 00:23:55 +00003149 /* Explicitly drain priv->rx_list */
3150 list_for_each_entry_safe(rx_data, temp, &priv->rx_list, list) {
3151 list_del(&rx_data->list);
3152
3153 dev_kfree_skb(rx_data->skb);
3154 kfree(rx_data->desc);
3155 kfree(rx_data);
3156 }
3157
David Kilroy39d1ffe2008-11-22 10:37:28 +00003158 unregister_pm_notifier(&priv->pm_notifier);
David Kilroy74734312008-11-22 10:37:25 +00003159 orinoco_uncache_fw(priv);
3160
David Kilroyd03032a2008-08-21 23:28:02 +01003161 priv->wpa_ie_len = 0;
3162 kfree(priv->wpa_ie);
David Kilroy23edcc42008-08-21 23:28:05 +01003163 orinoco_mic_free(priv);
Dan Williams1e3428e2007-10-10 23:56:25 -04003164 orinoco_bss_data_free(priv);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003165 free_netdev(dev);
3166}
David Kilroy21312662009-02-04 23:05:47 +00003167EXPORT_SYMBOL(free_orinocodev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003168
3169/********************************************************************/
3170/* Wireless extensions */
3171/********************************************************************/
3172
Jean Tourrilhes7e4e8d92006-10-10 14:45:44 -07003173/* Return : < 0 -> error code ; >= 0 -> length */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003174static int orinoco_hw_get_essid(struct orinoco_private *priv, int *active,
3175 char buf[IW_ESSID_MAX_SIZE+1])
3176{
3177 hermes_t *hw = &priv->hw;
3178 int err = 0;
3179 struct hermes_idstring essidbuf;
3180 char *p = (char *)(&essidbuf.val);
3181 int len;
3182 unsigned long flags;
3183
3184 if (orinoco_lock(priv, &flags) != 0)
3185 return -EBUSY;
3186
3187 if (strlen(priv->desired_essid) > 0) {
3188 /* We read the desired SSID from the hardware rather
3189 than from priv->desired_essid, just in case the
3190 firmware is allowed to change it on us. I'm not
3191 sure about this */
3192 /* My guess is that the OWNSSID should always be whatever
3193 * we set to the card, whereas CURRENT_SSID is the one that
3194 * may change... - Jean II */
3195 u16 rid;
3196
3197 *active = 1;
3198
3199 rid = (priv->port_type == 3) ? HERMES_RID_CNFOWNSSID :
3200 HERMES_RID_CNFDESIREDSSID;
David Kilroy6fe9deb2009-02-04 23:05:43 +00003201
Linus Torvalds1da177e2005-04-16 15:20:36 -07003202 err = hermes_read_ltv(hw, USER_BAP, rid, sizeof(essidbuf),
3203 NULL, &essidbuf);
3204 if (err)
3205 goto fail_unlock;
3206 } else {
3207 *active = 0;
3208
3209 err = hermes_read_ltv(hw, USER_BAP, HERMES_RID_CURRENTSSID,
3210 sizeof(essidbuf), NULL, &essidbuf);
3211 if (err)
3212 goto fail_unlock;
3213 }
3214
3215 len = le16_to_cpu(essidbuf.len);
Christoph Hellwig84d8a2f2005-05-14 17:30:22 +02003216 BUG_ON(len > IW_ESSID_MAX_SIZE);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003217
Jean Tourrilhes7e4e8d92006-10-10 14:45:44 -07003218 memset(buf, 0, IW_ESSID_MAX_SIZE);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003219 memcpy(buf, p, len);
Jean Tourrilhes7e4e8d92006-10-10 14:45:44 -07003220 err = len;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003221
3222 fail_unlock:
3223 orinoco_unlock(priv, &flags);
3224
David Kilroy6fe9deb2009-02-04 23:05:43 +00003225 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003226}
3227
David Kilroy9ee677c2008-12-23 14:03:38 +00003228static int orinoco_hw_get_freq(struct orinoco_private *priv)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003229{
David Kilroy6fe9deb2009-02-04 23:05:43 +00003230
Linus Torvalds1da177e2005-04-16 15:20:36 -07003231 hermes_t *hw = &priv->hw;
3232 int err = 0;
3233 u16 channel;
David Kilroy9ee677c2008-12-23 14:03:38 +00003234 int freq = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003235 unsigned long flags;
3236
3237 if (orinoco_lock(priv, &flags) != 0)
3238 return -EBUSY;
David Kilroy6fe9deb2009-02-04 23:05:43 +00003239
David Kilroyb2f30a02009-02-04 23:05:46 +00003240 err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_CURRENTCHANNEL,
3241 &channel);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003242 if (err)
3243 goto out;
3244
3245 /* Intersil firmware 1.3.5 returns 0 when the interface is down */
3246 if (channel == 0) {
3247 err = -EBUSY;
3248 goto out;
3249 }
3250
David Kilroya94e8422009-02-04 23:05:44 +00003251 if ((channel < 1) || (channel > NUM_CHANNELS)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07003252 printk(KERN_WARNING "%s: Channel out of range (%d)!\n",
3253 priv->ndev->name, channel);
3254 err = -EBUSY;
3255 goto out;
3256
3257 }
David Kilroy9ee677c2008-12-23 14:03:38 +00003258 freq = ieee80211_dsss_chan_to_freq(channel);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003259
3260 out:
3261 orinoco_unlock(priv, &flags);
3262
3263 if (err > 0)
3264 err = -EBUSY;
3265 return err ? err : freq;
3266}
3267
3268static int orinoco_hw_get_bitratelist(struct orinoco_private *priv,
3269 int *numrates, s32 *rates, int max)
3270{
3271 hermes_t *hw = &priv->hw;
3272 struct hermes_idstring list;
3273 unsigned char *p = (unsigned char *)&list.val;
3274 int err = 0;
3275 int num;
3276 int i;
3277 unsigned long flags;
3278
3279 if (orinoco_lock(priv, &flags) != 0)
3280 return -EBUSY;
3281
3282 err = hermes_read_ltv(hw, USER_BAP, HERMES_RID_SUPPORTEDDATARATES,
3283 sizeof(list), NULL, &list);
3284 orinoco_unlock(priv, &flags);
3285
3286 if (err)
3287 return err;
David Kilroy6fe9deb2009-02-04 23:05:43 +00003288
Linus Torvalds1da177e2005-04-16 15:20:36 -07003289 num = le16_to_cpu(list.len);
3290 *numrates = num;
3291 num = min(num, max);
3292
David Kilroy566f2d92009-02-04 23:05:45 +00003293 for (i = 0; i < num; i++)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003294 rates[i] = (p[i] & 0x7f) * 500000; /* convert to bps */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003295
3296 return 0;
3297}
3298
Christoph Hellwig620554e2005-06-19 01:27:33 +02003299static int orinoco_ioctl_getname(struct net_device *dev,
3300 struct iw_request_info *info,
3301 char *name,
3302 char *extra)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003303{
3304 struct orinoco_private *priv = netdev_priv(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003305 int numrates;
Christoph Hellwig620554e2005-06-19 01:27:33 +02003306 int err;
3307
3308 err = orinoco_hw_get_bitratelist(priv, &numrates, NULL, 0);
3309
3310 if (!err && (numrates > 2))
3311 strcpy(name, "IEEE 802.11b");
3312 else
3313 strcpy(name, "IEEE 802.11-DS");
3314
3315 return 0;
3316}
3317
Christoph Hellwig16739b02005-06-19 01:27:51 +02003318static int orinoco_ioctl_setwap(struct net_device *dev,
3319 struct iw_request_info *info,
3320 struct sockaddr *ap_addr,
3321 char *extra)
3322{
3323 struct orinoco_private *priv = netdev_priv(dev);
3324 int err = -EINPROGRESS; /* Call commit handler */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003325 unsigned long flags;
Christoph Hellwig16739b02005-06-19 01:27:51 +02003326 static const u8 off_addr[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
3327 static const u8 any_addr[] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
Linus Torvalds1da177e2005-04-16 15:20:36 -07003328
3329 if (orinoco_lock(priv, &flags) != 0)
3330 return -EBUSY;
3331
Christoph Hellwig16739b02005-06-19 01:27:51 +02003332 /* Enable automatic roaming - no sanity checks are needed */
3333 if (memcmp(&ap_addr->sa_data, off_addr, ETH_ALEN) == 0 ||
3334 memcmp(&ap_addr->sa_data, any_addr, ETH_ALEN) == 0) {
3335 priv->bssid_fixed = 0;
3336 memset(priv->desired_bssid, 0, ETH_ALEN);
3337
3338 /* "off" means keep existing connection */
3339 if (ap_addr->sa_data[0] == 0) {
3340 __orinoco_hw_set_wap(priv);
3341 err = 0;
3342 }
3343 goto out;
3344 }
3345
3346 if (priv->firmware_type == FIRMWARE_TYPE_AGERE) {
3347 printk(KERN_WARNING "%s: Lucent/Agere firmware doesn't "
3348 "support manual roaming\n",
3349 dev->name);
3350 err = -EOPNOTSUPP;
3351 goto out;
3352 }
3353
3354 if (priv->iw_mode != IW_MODE_INFRA) {
3355 printk(KERN_WARNING "%s: Manual roaming supported only in "
3356 "managed mode\n", dev->name);
3357 err = -EOPNOTSUPP;
3358 goto out;
3359 }
3360
3361 /* Intersil firmware hangs without Desired ESSID */
3362 if (priv->firmware_type == FIRMWARE_TYPE_INTERSIL &&
3363 strlen(priv->desired_essid) == 0) {
3364 printk(KERN_WARNING "%s: Desired ESSID must be set for "
3365 "manual roaming\n", dev->name);
3366 err = -EOPNOTSUPP;
3367 goto out;
3368 }
3369
3370 /* Finally, enable manual roaming */
3371 priv->bssid_fixed = 1;
3372 memcpy(priv->desired_bssid, &ap_addr->sa_data, ETH_ALEN);
3373
3374 out:
3375 orinoco_unlock(priv, &flags);
3376 return err;
3377}
3378
Christoph Hellwig620554e2005-06-19 01:27:33 +02003379static int orinoco_ioctl_getwap(struct net_device *dev,
3380 struct iw_request_info *info,
3381 struct sockaddr *ap_addr,
3382 char *extra)
3383{
3384 struct orinoco_private *priv = netdev_priv(dev);
3385
3386 hermes_t *hw = &priv->hw;
3387 int err = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003388 unsigned long flags;
3389
Linus Torvalds1da177e2005-04-16 15:20:36 -07003390 if (orinoco_lock(priv, &flags) != 0)
3391 return -EBUSY;
3392
Christoph Hellwig620554e2005-06-19 01:27:33 +02003393 ap_addr->sa_family = ARPHRD_ETHER;
3394 err = hermes_read_ltv(hw, USER_BAP, HERMES_RID_CURRENTBSSID,
3395 ETH_ALEN, NULL, ap_addr->sa_data);
3396
Linus Torvalds1da177e2005-04-16 15:20:36 -07003397 orinoco_unlock(priv, &flags);
3398
Christoph Hellwig620554e2005-06-19 01:27:33 +02003399 return err;
3400}
Linus Torvalds1da177e2005-04-16 15:20:36 -07003401
Christoph Hellwig620554e2005-06-19 01:27:33 +02003402static int orinoco_ioctl_setmode(struct net_device *dev,
3403 struct iw_request_info *info,
3404 u32 *mode,
3405 char *extra)
3406{
3407 struct orinoco_private *priv = netdev_priv(dev);
3408 int err = -EINPROGRESS; /* Call commit handler */
3409 unsigned long flags;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003410
Christoph Hellwig620554e2005-06-19 01:27:33 +02003411 if (priv->iw_mode == *mode)
3412 return 0;
3413
3414 if (orinoco_lock(priv, &flags) != 0)
3415 return -EBUSY;
3416
3417 switch (*mode) {
3418 case IW_MODE_ADHOC:
3419 if (!priv->has_ibss && !priv->has_port3)
3420 err = -EOPNOTSUPP;
3421 break;
3422
3423 case IW_MODE_INFRA:
3424 break;
3425
Christoph Hellwig98c4cae2005-06-19 01:28:06 +02003426 case IW_MODE_MONITOR:
3427 if (priv->broken_monitor && !force_monitor) {
3428 printk(KERN_WARNING "%s: Monitor mode support is "
3429 "buggy in this firmware, not enabling\n",
3430 dev->name);
3431 err = -EOPNOTSUPP;
3432 }
3433 break;
3434
Christoph Hellwig620554e2005-06-19 01:27:33 +02003435 default:
3436 err = -EOPNOTSUPP;
3437 break;
3438 }
3439
3440 if (err == -EINPROGRESS) {
3441 priv->iw_mode = *mode;
3442 set_port_type(priv);
3443 }
3444
3445 orinoco_unlock(priv, &flags);
3446
3447 return err;
3448}
3449
3450static int orinoco_ioctl_getmode(struct net_device *dev,
3451 struct iw_request_info *info,
3452 u32 *mode,
3453 char *extra)
3454{
3455 struct orinoco_private *priv = netdev_priv(dev);
3456
3457 *mode = priv->iw_mode;
3458 return 0;
3459}
3460
3461static int orinoco_ioctl_getiwrange(struct net_device *dev,
3462 struct iw_request_info *info,
3463 struct iw_point *rrq,
3464 char *extra)
3465{
3466 struct orinoco_private *priv = netdev_priv(dev);
3467 int err = 0;
3468 struct iw_range *range = (struct iw_range *) extra;
3469 int numrates;
3470 int i, k;
3471
Christoph Hellwig620554e2005-06-19 01:27:33 +02003472 rrq->length = sizeof(struct iw_range);
3473 memset(range, 0, sizeof(struct iw_range));
3474
3475 range->we_version_compiled = WIRELESS_EXT;
David Kilroyd03032a2008-08-21 23:28:02 +01003476 range->we_version_source = 22;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003477
3478 /* Set available channels/frequencies */
Christoph Hellwig620554e2005-06-19 01:27:33 +02003479 range->num_channels = NUM_CHANNELS;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003480 k = 0;
3481 for (i = 0; i < NUM_CHANNELS; i++) {
3482 if (priv->channel_mask & (1 << i)) {
Christoph Hellwig620554e2005-06-19 01:27:33 +02003483 range->freq[k].i = i + 1;
David Kilroy9ee677c2008-12-23 14:03:38 +00003484 range->freq[k].m = (ieee80211_dsss_chan_to_freq(i + 1) *
3485 100000);
Christoph Hellwig620554e2005-06-19 01:27:33 +02003486 range->freq[k].e = 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003487 k++;
3488 }
David Kilroy6fe9deb2009-02-04 23:05:43 +00003489
Linus Torvalds1da177e2005-04-16 15:20:36 -07003490 if (k >= IW_MAX_FREQUENCIES)
3491 break;
3492 }
Christoph Hellwig620554e2005-06-19 01:27:33 +02003493 range->num_frequency = k;
3494 range->sensitivity = 3;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003495
Christoph Hellwig620554e2005-06-19 01:27:33 +02003496 if (priv->has_wep) {
3497 range->max_encoding_tokens = ORINOCO_MAX_KEYS;
3498 range->encoding_size[0] = SMALL_KEY_SIZE;
3499 range->num_encoding_sizes = 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003500
Christoph Hellwig620554e2005-06-19 01:27:33 +02003501 if (priv->has_big_wep) {
3502 range->encoding_size[1] = LARGE_KEY_SIZE;
3503 range->num_encoding_sizes = 2;
3504 }
3505 }
3506
David Kilroyd03032a2008-08-21 23:28:02 +01003507 if (priv->has_wpa)
3508 range->enc_capa = IW_ENC_CAPA_WPA | IW_ENC_CAPA_CIPHER_TKIP;
3509
David Kilroya94e8422009-02-04 23:05:44 +00003510 if ((priv->iw_mode == IW_MODE_ADHOC) && (!SPY_NUMBER(priv))) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07003511 /* Quality stats meaningless in ad-hoc mode */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003512 } else {
Christoph Hellwig620554e2005-06-19 01:27:33 +02003513 range->max_qual.qual = 0x8b - 0x2f;
3514 range->max_qual.level = 0x2f - 0x95 - 1;
3515 range->max_qual.noise = 0x2f - 0x95 - 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003516 /* Need to get better values */
Christoph Hellwig620554e2005-06-19 01:27:33 +02003517 range->avg_qual.qual = 0x24;
3518 range->avg_qual.level = 0xC2;
3519 range->avg_qual.noise = 0x9E;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003520 }
3521
3522 err = orinoco_hw_get_bitratelist(priv, &numrates,
Christoph Hellwig620554e2005-06-19 01:27:33 +02003523 range->bitrate, IW_MAX_BITRATES);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003524 if (err)
3525 return err;
Christoph Hellwig620554e2005-06-19 01:27:33 +02003526 range->num_bitrates = numrates;
3527
Linus Torvalds1da177e2005-04-16 15:20:36 -07003528 /* Set an indication of the max TCP throughput in bit/s that we can
3529 * expect using this interface. May be use for QoS stuff...
3530 * Jean II */
Christoph Hellwig620554e2005-06-19 01:27:33 +02003531 if (numrates > 2)
3532 range->throughput = 5 * 1000 * 1000; /* ~5 Mb/s */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003533 else
Christoph Hellwig620554e2005-06-19 01:27:33 +02003534 range->throughput = 1.5 * 1000 * 1000; /* ~1.5 Mb/s */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003535
Christoph Hellwig620554e2005-06-19 01:27:33 +02003536 range->min_rts = 0;
3537 range->max_rts = 2347;
3538 range->min_frag = 256;
3539 range->max_frag = 2346;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003540
Christoph Hellwig620554e2005-06-19 01:27:33 +02003541 range->min_pmp = 0;
3542 range->max_pmp = 65535000;
3543 range->min_pmt = 0;
3544 range->max_pmt = 65535 * 1000; /* ??? */
3545 range->pmp_flags = IW_POWER_PERIOD;
3546 range->pmt_flags = IW_POWER_TIMEOUT;
David Kilroyb2f30a02009-02-04 23:05:46 +00003547 range->pm_capa = (IW_POWER_PERIOD | IW_POWER_TIMEOUT |
3548 IW_POWER_UNICAST_R);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003549
Christoph Hellwig620554e2005-06-19 01:27:33 +02003550 range->retry_capa = IW_RETRY_LIMIT | IW_RETRY_LIFETIME;
3551 range->retry_flags = IW_RETRY_LIMIT;
3552 range->r_time_flags = IW_RETRY_LIFETIME;
3553 range->min_retry = 0;
3554 range->max_retry = 65535; /* ??? */
3555 range->min_r_time = 0;
3556 range->max_r_time = 65535 * 1000; /* ??? */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003557
David Kilroy0753bba2008-08-21 23:27:46 +01003558 if (priv->firmware_type == FIRMWARE_TYPE_AGERE)
3559 range->scan_capa = IW_SCAN_CAPA_ESSID;
3560 else
3561 range->scan_capa = IW_SCAN_CAPA_NONE;
3562
Pavel Roskin343c6862005-09-09 18:43:02 -04003563 /* Event capability (kernel) */
3564 IW_EVENT_CAPA_SET_KERNEL(range->event_capa);
3565 /* Event capability (driver) */
3566 IW_EVENT_CAPA_SET(range->event_capa, SIOCGIWTHRSPY);
3567 IW_EVENT_CAPA_SET(range->event_capa, SIOCGIWAP);
3568 IW_EVENT_CAPA_SET(range->event_capa, SIOCGIWSCAN);
3569 IW_EVENT_CAPA_SET(range->event_capa, IWEVTXDROP);
3570
Linus Torvalds1da177e2005-04-16 15:20:36 -07003571 return 0;
3572}
3573
Christoph Hellwig620554e2005-06-19 01:27:33 +02003574static int orinoco_ioctl_setiwencode(struct net_device *dev,
3575 struct iw_request_info *info,
3576 struct iw_point *erq,
3577 char *keybuf)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003578{
3579 struct orinoco_private *priv = netdev_priv(dev);
3580 int index = (erq->flags & IW_ENCODE_INDEX) - 1;
3581 int setindex = priv->tx_key;
David Kilroy4ae6ee22008-08-21 23:27:59 +01003582 int encode_alg = priv->encode_alg;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003583 int restricted = priv->wep_restrict;
3584 u16 xlen = 0;
Christoph Hellwig620554e2005-06-19 01:27:33 +02003585 int err = -EINPROGRESS; /* Call commit handler */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003586 unsigned long flags;
3587
David Kilroya94e8422009-02-04 23:05:44 +00003588 if (!priv->has_wep)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003589 return -EOPNOTSUPP;
3590
3591 if (erq->pointer) {
3592 /* We actually have a key to set - check its length */
3593 if (erq->length > LARGE_KEY_SIZE)
3594 return -E2BIG;
3595
David Kilroya94e8422009-02-04 23:05:44 +00003596 if ((erq->length > SMALL_KEY_SIZE) && !priv->has_big_wep)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003597 return -E2BIG;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003598 }
3599
3600 if (orinoco_lock(priv, &flags) != 0)
3601 return -EBUSY;
3602
David Kilroyd03032a2008-08-21 23:28:02 +01003603 /* Clear any TKIP key we have */
3604 if ((priv->has_wpa) && (priv->encode_alg == IW_ENCODE_ALG_TKIP))
3605 (void) orinoco_clear_tkip_key(priv, setindex);
3606
Dan Williamsfe397d42006-07-14 11:41:47 -04003607 if (erq->length > 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07003608 if ((index < 0) || (index >= ORINOCO_MAX_KEYS))
3609 index = priv->tx_key;
3610
3611 /* Adjust key length to a supported value */
David Kilroy566f2d92009-02-04 23:05:45 +00003612 if (erq->length > SMALL_KEY_SIZE)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003613 xlen = LARGE_KEY_SIZE;
David Kilroy566f2d92009-02-04 23:05:45 +00003614 else if (erq->length > 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003615 xlen = SMALL_KEY_SIZE;
David Kilroy566f2d92009-02-04 23:05:45 +00003616 else
Linus Torvalds1da177e2005-04-16 15:20:36 -07003617 xlen = 0;
3618
3619 /* Switch on WEP if off */
David Kilroy4ae6ee22008-08-21 23:27:59 +01003620 if ((encode_alg != IW_ENCODE_ALG_WEP) && (xlen > 0)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07003621 setindex = index;
David Kilroy4ae6ee22008-08-21 23:27:59 +01003622 encode_alg = IW_ENCODE_ALG_WEP;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003623 }
3624 } else {
3625 /* Important note : if the user do "iwconfig eth0 enc off",
3626 * we will arrive there with an index of -1. This is valid
3627 * but need to be taken care off... Jean II */
3628 if ((index < 0) || (index >= ORINOCO_MAX_KEYS)) {
David Kilroya94e8422009-02-04 23:05:44 +00003629 if ((index != -1) || (erq->flags == 0)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07003630 err = -EINVAL;
3631 goto out;
3632 }
3633 } else {
3634 /* Set the index : Check that the key is valid */
David Kilroya94e8422009-02-04 23:05:44 +00003635 if (priv->keys[index].len == 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07003636 err = -EINVAL;
3637 goto out;
3638 }
3639 setindex = index;
3640 }
3641 }
3642
3643 if (erq->flags & IW_ENCODE_DISABLED)
David Kilroy4ae6ee22008-08-21 23:27:59 +01003644 encode_alg = IW_ENCODE_ALG_NONE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003645 if (erq->flags & IW_ENCODE_OPEN)
3646 restricted = 0;
3647 if (erq->flags & IW_ENCODE_RESTRICTED)
3648 restricted = 1;
3649
Dan Williamsfe397d42006-07-14 11:41:47 -04003650 if (erq->pointer && erq->length > 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07003651 priv->keys[index].len = cpu_to_le16(xlen);
3652 memset(priv->keys[index].data, 0,
3653 sizeof(priv->keys[index].data));
3654 memcpy(priv->keys[index].data, keybuf, erq->length);
3655 }
3656 priv->tx_key = setindex;
3657
3658 /* Try fast key change if connected and only keys are changed */
David Kilroy4ae6ee22008-08-21 23:27:59 +01003659 if ((priv->encode_alg == encode_alg) &&
3660 (priv->wep_restrict == restricted) &&
Linus Torvalds1da177e2005-04-16 15:20:36 -07003661 netif_carrier_ok(dev)) {
3662 err = __orinoco_hw_setup_wepkeys(priv);
3663 /* No need to commit if successful */
3664 goto out;
3665 }
3666
David Kilroy4ae6ee22008-08-21 23:27:59 +01003667 priv->encode_alg = encode_alg;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003668 priv->wep_restrict = restricted;
3669
3670 out:
3671 orinoco_unlock(priv, &flags);
3672
3673 return err;
3674}
3675
Christoph Hellwig620554e2005-06-19 01:27:33 +02003676static int orinoco_ioctl_getiwencode(struct net_device *dev,
3677 struct iw_request_info *info,
3678 struct iw_point *erq,
3679 char *keybuf)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003680{
3681 struct orinoco_private *priv = netdev_priv(dev);
3682 int index = (erq->flags & IW_ENCODE_INDEX) - 1;
3683 u16 xlen = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003684 unsigned long flags;
3685
David Kilroya94e8422009-02-04 23:05:44 +00003686 if (!priv->has_wep)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003687 return -EOPNOTSUPP;
3688
3689 if (orinoco_lock(priv, &flags) != 0)
3690 return -EBUSY;
3691
3692 if ((index < 0) || (index >= ORINOCO_MAX_KEYS))
3693 index = priv->tx_key;
3694
3695 erq->flags = 0;
David Kilroy4ae6ee22008-08-21 23:27:59 +01003696 if (!priv->encode_alg)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003697 erq->flags |= IW_ENCODE_DISABLED;
3698 erq->flags |= index + 1;
3699
3700 if (priv->wep_restrict)
3701 erq->flags |= IW_ENCODE_RESTRICTED;
3702 else
3703 erq->flags |= IW_ENCODE_OPEN;
3704
3705 xlen = le16_to_cpu(priv->keys[index].len);
3706
3707 erq->length = xlen;
3708
3709 memcpy(keybuf, priv->keys[index].data, ORINOCO_MAX_KEY_SIZE);
3710
3711 orinoco_unlock(priv, &flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003712 return 0;
3713}
3714
Christoph Hellwig620554e2005-06-19 01:27:33 +02003715static int orinoco_ioctl_setessid(struct net_device *dev,
3716 struct iw_request_info *info,
3717 struct iw_point *erq,
3718 char *essidbuf)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003719{
3720 struct orinoco_private *priv = netdev_priv(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003721 unsigned long flags;
3722
3723 /* Note : ESSID is ignored in Ad-Hoc demo mode, but we can set it
3724 * anyway... - Jean II */
3725
Christoph Hellwig620554e2005-06-19 01:27:33 +02003726 /* Hum... Should not use Wireless Extension constant (may change),
3727 * should use our own... - Jean II */
3728 if (erq->length > IW_ESSID_MAX_SIZE)
3729 return -E2BIG;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003730
3731 if (orinoco_lock(priv, &flags) != 0)
3732 return -EBUSY;
3733
Christoph Hellwig620554e2005-06-19 01:27:33 +02003734 /* NULL the string (for NULL termination & ESSID = ANY) - Jean II */
3735 memset(priv->desired_essid, 0, sizeof(priv->desired_essid));
3736
3737 /* If not ANY, get the new ESSID */
David Kilroy566f2d92009-02-04 23:05:45 +00003738 if (erq->flags)
Christoph Hellwig620554e2005-06-19 01:27:33 +02003739 memcpy(priv->desired_essid, essidbuf, erq->length);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003740
3741 orinoco_unlock(priv, &flags);
3742
Christoph Hellwig620554e2005-06-19 01:27:33 +02003743 return -EINPROGRESS; /* Call commit handler */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003744}
3745
Christoph Hellwig620554e2005-06-19 01:27:33 +02003746static int orinoco_ioctl_getessid(struct net_device *dev,
3747 struct iw_request_info *info,
3748 struct iw_point *erq,
3749 char *essidbuf)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003750{
3751 struct orinoco_private *priv = netdev_priv(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003752 int active;
3753 int err = 0;
3754 unsigned long flags;
3755
Linus Torvalds1da177e2005-04-16 15:20:36 -07003756 if (netif_running(dev)) {
3757 err = orinoco_hw_get_essid(priv, &active, essidbuf);
Jean Tourrilhes7e4e8d92006-10-10 14:45:44 -07003758 if (err < 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003759 return err;
Jean Tourrilhes7e4e8d92006-10-10 14:45:44 -07003760 erq->length = err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003761 } else {
3762 if (orinoco_lock(priv, &flags) != 0)
3763 return -EBUSY;
Jean Tourrilhes7e4e8d92006-10-10 14:45:44 -07003764 memcpy(essidbuf, priv->desired_essid, IW_ESSID_MAX_SIZE);
3765 erq->length = strlen(priv->desired_essid);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003766 orinoco_unlock(priv, &flags);
3767 }
3768
3769 erq->flags = 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003770
Linus Torvalds1da177e2005-04-16 15:20:36 -07003771 return 0;
3772}
3773
Christoph Hellwig620554e2005-06-19 01:27:33 +02003774static int orinoco_ioctl_setnick(struct net_device *dev,
3775 struct iw_request_info *info,
3776 struct iw_point *nrq,
3777 char *nickbuf)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003778{
3779 struct orinoco_private *priv = netdev_priv(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003780 unsigned long flags;
3781
3782 if (nrq->length > IW_ESSID_MAX_SIZE)
3783 return -E2BIG;
3784
Linus Torvalds1da177e2005-04-16 15:20:36 -07003785 if (orinoco_lock(priv, &flags) != 0)
3786 return -EBUSY;
3787
Christoph Hellwig620554e2005-06-19 01:27:33 +02003788 memset(priv->nick, 0, sizeof(priv->nick));
3789 memcpy(priv->nick, nickbuf, nrq->length);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003790
3791 orinoco_unlock(priv, &flags);
3792
Christoph Hellwig620554e2005-06-19 01:27:33 +02003793 return -EINPROGRESS; /* Call commit handler */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003794}
3795
Christoph Hellwig620554e2005-06-19 01:27:33 +02003796static int orinoco_ioctl_getnick(struct net_device *dev,
3797 struct iw_request_info *info,
3798 struct iw_point *nrq,
3799 char *nickbuf)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003800{
3801 struct orinoco_private *priv = netdev_priv(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003802 unsigned long flags;
3803
3804 if (orinoco_lock(priv, &flags) != 0)
3805 return -EBUSY;
3806
Jean Tourrilhes7e4e8d92006-10-10 14:45:44 -07003807 memcpy(nickbuf, priv->nick, IW_ESSID_MAX_SIZE);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003808 orinoco_unlock(priv, &flags);
3809
Jean Tourrilhes7e4e8d92006-10-10 14:45:44 -07003810 nrq->length = strlen(priv->nick);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003811
Linus Torvalds1da177e2005-04-16 15:20:36 -07003812 return 0;
3813}
3814
Christoph Hellwig620554e2005-06-19 01:27:33 +02003815static int orinoco_ioctl_setfreq(struct net_device *dev,
3816 struct iw_request_info *info,
3817 struct iw_freq *frq,
3818 char *extra)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003819{
3820 struct orinoco_private *priv = netdev_priv(dev);
3821 int chan = -1;
3822 unsigned long flags;
Christoph Hellwig620554e2005-06-19 01:27:33 +02003823 int err = -EINPROGRESS; /* Call commit handler */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003824
Christoph Hellwig98c4cae2005-06-19 01:28:06 +02003825 /* In infrastructure mode the AP sets the channel */
3826 if (priv->iw_mode == IW_MODE_INFRA)
3827 return -EBUSY;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003828
David Kilroya94e8422009-02-04 23:05:44 +00003829 if ((frq->e == 0) && (frq->m <= 1000)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07003830 /* Setting by channel number */
3831 chan = frq->m;
3832 } else {
David Kilroy9ee677c2008-12-23 14:03:38 +00003833 /* Setting by frequency */
3834 int denom = 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003835 int i;
3836
David Kilroy9ee677c2008-12-23 14:03:38 +00003837 /* Calculate denominator to rescale to MHz */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003838 for (i = 0; i < (6 - frq->e); i++)
David Kilroy9ee677c2008-12-23 14:03:38 +00003839 denom *= 10;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003840
David Kilroy9ee677c2008-12-23 14:03:38 +00003841 chan = ieee80211_freq_to_dsss_chan(frq->m / denom);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003842 }
3843
David Kilroya94e8422009-02-04 23:05:44 +00003844 if ((chan < 1) || (chan > NUM_CHANNELS) ||
3845 !(priv->channel_mask & (1 << (chan-1))))
Linus Torvalds1da177e2005-04-16 15:20:36 -07003846 return -EINVAL;
3847
3848 if (orinoco_lock(priv, &flags) != 0)
3849 return -EBUSY;
Christoph Hellwig98c4cae2005-06-19 01:28:06 +02003850
Linus Torvalds1da177e2005-04-16 15:20:36 -07003851 priv->channel = chan;
Christoph Hellwig98c4cae2005-06-19 01:28:06 +02003852 if (priv->iw_mode == IW_MODE_MONITOR) {
3853 /* Fast channel change - no commit if successful */
3854 hermes_t *hw = &priv->hw;
3855 err = hermes_docmd_wait(hw, HERMES_CMD_TEST |
3856 HERMES_TEST_SET_CHANNEL,
3857 chan, NULL);
3858 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07003859 orinoco_unlock(priv, &flags);
3860
Christoph Hellwig620554e2005-06-19 01:27:33 +02003861 return err;
3862}
3863
3864static int orinoco_ioctl_getfreq(struct net_device *dev,
3865 struct iw_request_info *info,
3866 struct iw_freq *frq,
3867 char *extra)
3868{
3869 struct orinoco_private *priv = netdev_priv(dev);
3870 int tmp;
3871
3872 /* Locking done in there */
3873 tmp = orinoco_hw_get_freq(priv);
David Kilroy566f2d92009-02-04 23:05:45 +00003874 if (tmp < 0)
Christoph Hellwig620554e2005-06-19 01:27:33 +02003875 return tmp;
Christoph Hellwig620554e2005-06-19 01:27:33 +02003876
David Kilroy9ee677c2008-12-23 14:03:38 +00003877 frq->m = tmp * 100000;
Christoph Hellwig620554e2005-06-19 01:27:33 +02003878 frq->e = 1;
3879
Linus Torvalds1da177e2005-04-16 15:20:36 -07003880 return 0;
3881}
3882
Christoph Hellwig620554e2005-06-19 01:27:33 +02003883static int orinoco_ioctl_getsens(struct net_device *dev,
3884 struct iw_request_info *info,
3885 struct iw_param *srq,
3886 char *extra)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003887{
3888 struct orinoco_private *priv = netdev_priv(dev);
3889 hermes_t *hw = &priv->hw;
3890 u16 val;
3891 int err;
3892 unsigned long flags;
3893
3894 if (!priv->has_sensitivity)
3895 return -EOPNOTSUPP;
3896
3897 if (orinoco_lock(priv, &flags) != 0)
3898 return -EBUSY;
3899 err = hermes_read_wordrec(hw, USER_BAP,
3900 HERMES_RID_CNFSYSTEMSCALE, &val);
3901 orinoco_unlock(priv, &flags);
3902
3903 if (err)
3904 return err;
3905
3906 srq->value = val;
3907 srq->fixed = 0; /* auto */
3908
3909 return 0;
3910}
3911
Christoph Hellwig620554e2005-06-19 01:27:33 +02003912static int orinoco_ioctl_setsens(struct net_device *dev,
3913 struct iw_request_info *info,
3914 struct iw_param *srq,
3915 char *extra)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003916{
3917 struct orinoco_private *priv = netdev_priv(dev);
3918 int val = srq->value;
3919 unsigned long flags;
3920
3921 if (!priv->has_sensitivity)
3922 return -EOPNOTSUPP;
3923
3924 if ((val < 1) || (val > 3))
3925 return -EINVAL;
David Kilroy6fe9deb2009-02-04 23:05:43 +00003926
Linus Torvalds1da177e2005-04-16 15:20:36 -07003927 if (orinoco_lock(priv, &flags) != 0)
3928 return -EBUSY;
3929 priv->ap_density = val;
3930 orinoco_unlock(priv, &flags);
3931
Christoph Hellwig620554e2005-06-19 01:27:33 +02003932 return -EINPROGRESS; /* Call commit handler */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003933}
3934
Christoph Hellwig620554e2005-06-19 01:27:33 +02003935static int orinoco_ioctl_setrts(struct net_device *dev,
3936 struct iw_request_info *info,
3937 struct iw_param *rrq,
3938 char *extra)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003939{
3940 struct orinoco_private *priv = netdev_priv(dev);
3941 int val = rrq->value;
3942 unsigned long flags;
3943
3944 if (rrq->disabled)
3945 val = 2347;
3946
David Kilroya94e8422009-02-04 23:05:44 +00003947 if ((val < 0) || (val > 2347))
Linus Torvalds1da177e2005-04-16 15:20:36 -07003948 return -EINVAL;
3949
3950 if (orinoco_lock(priv, &flags) != 0)
3951 return -EBUSY;
3952
3953 priv->rts_thresh = val;
3954 orinoco_unlock(priv, &flags);
3955
Christoph Hellwig620554e2005-06-19 01:27:33 +02003956 return -EINPROGRESS; /* Call commit handler */
3957}
3958
3959static int orinoco_ioctl_getrts(struct net_device *dev,
3960 struct iw_request_info *info,
3961 struct iw_param *rrq,
3962 char *extra)
3963{
3964 struct orinoco_private *priv = netdev_priv(dev);
3965
3966 rrq->value = priv->rts_thresh;
3967 rrq->disabled = (rrq->value == 2347);
3968 rrq->fixed = 1;
3969
Linus Torvalds1da177e2005-04-16 15:20:36 -07003970 return 0;
3971}
3972
Christoph Hellwig620554e2005-06-19 01:27:33 +02003973static int orinoco_ioctl_setfrag(struct net_device *dev,
3974 struct iw_request_info *info,
3975 struct iw_param *frq,
3976 char *extra)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003977{
3978 struct orinoco_private *priv = netdev_priv(dev);
Christoph Hellwig620554e2005-06-19 01:27:33 +02003979 int err = -EINPROGRESS; /* Call commit handler */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003980 unsigned long flags;
3981
3982 if (orinoco_lock(priv, &flags) != 0)
3983 return -EBUSY;
3984
3985 if (priv->has_mwo) {
3986 if (frq->disabled)
3987 priv->mwo_robust = 0;
3988 else {
3989 if (frq->fixed)
David Kilroyb2f30a02009-02-04 23:05:46 +00003990 printk(KERN_WARNING "%s: Fixed fragmentation "
3991 "is not supported on this firmware. "
3992 "Using MWO robust instead.\n",
3993 dev->name);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003994 priv->mwo_robust = 1;
3995 }
3996 } else {
3997 if (frq->disabled)
3998 priv->frag_thresh = 2346;
3999 else {
David Kilroya94e8422009-02-04 23:05:44 +00004000 if ((frq->value < 256) || (frq->value > 2346))
Linus Torvalds1da177e2005-04-16 15:20:36 -07004001 err = -EINVAL;
4002 else
David Kilroyb2f30a02009-02-04 23:05:46 +00004003 /* must be even */
4004 priv->frag_thresh = frq->value & ~0x1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004005 }
4006 }
4007
4008 orinoco_unlock(priv, &flags);
4009
4010 return err;
4011}
4012
Christoph Hellwig620554e2005-06-19 01:27:33 +02004013static int orinoco_ioctl_getfrag(struct net_device *dev,
4014 struct iw_request_info *info,
4015 struct iw_param *frq,
4016 char *extra)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004017{
4018 struct orinoco_private *priv = netdev_priv(dev);
4019 hermes_t *hw = &priv->hw;
Christoph Hellwig620554e2005-06-19 01:27:33 +02004020 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004021 u16 val;
4022 unsigned long flags;
4023
4024 if (orinoco_lock(priv, &flags) != 0)
4025 return -EBUSY;
David Kilroy6fe9deb2009-02-04 23:05:43 +00004026
Linus Torvalds1da177e2005-04-16 15:20:36 -07004027 if (priv->has_mwo) {
4028 err = hermes_read_wordrec(hw, USER_BAP,
4029 HERMES_RID_CNFMWOROBUST_AGERE,
4030 &val);
4031 if (err)
4032 val = 0;
4033
4034 frq->value = val ? 2347 : 0;
David Kilroya94e8422009-02-04 23:05:44 +00004035 frq->disabled = !val;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004036 frq->fixed = 0;
4037 } else {
David Kilroyb2f30a02009-02-04 23:05:46 +00004038 err = hermes_read_wordrec(hw, USER_BAP,
4039 HERMES_RID_CNFFRAGMENTATIONTHRESHOLD,
Linus Torvalds1da177e2005-04-16 15:20:36 -07004040 &val);
4041 if (err)
4042 val = 0;
4043
4044 frq->value = val;
4045 frq->disabled = (val >= 2346);
4046 frq->fixed = 1;
4047 }
4048
4049 orinoco_unlock(priv, &flags);
David Kilroy6fe9deb2009-02-04 23:05:43 +00004050
Linus Torvalds1da177e2005-04-16 15:20:36 -07004051 return err;
4052}
4053
Christoph Hellwig620554e2005-06-19 01:27:33 +02004054static int orinoco_ioctl_setrate(struct net_device *dev,
4055 struct iw_request_info *info,
4056 struct iw_param *rrq,
4057 char *extra)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004058{
4059 struct orinoco_private *priv = netdev_priv(dev);
David Kilroycfeb1db2009-02-04 23:05:53 +00004060 int ratemode;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004061 int bitrate; /* 100s of kilobits */
Linus Torvalds1da177e2005-04-16 15:20:36 -07004062 unsigned long flags;
David Kilroy6fe9deb2009-02-04 23:05:43 +00004063
Linus Torvalds1da177e2005-04-16 15:20:36 -07004064 /* As the user space doesn't know our highest rate, it uses -1
4065 * to ask us to set the highest rate. Test it using "iwconfig
4066 * ethX rate auto" - Jean II */
4067 if (rrq->value == -1)
4068 bitrate = 110;
4069 else {
4070 if (rrq->value % 100000)
4071 return -EINVAL;
4072 bitrate = rrq->value / 100000;
4073 }
4074
David Kilroycfeb1db2009-02-04 23:05:53 +00004075 ratemode = orinoco_get_bitratemode(bitrate, !rrq->fixed);
David Kilroy6fe9deb2009-02-04 23:05:43 +00004076
Linus Torvalds1da177e2005-04-16 15:20:36 -07004077 if (ratemode == -1)
4078 return -EINVAL;
4079
4080 if (orinoco_lock(priv, &flags) != 0)
4081 return -EBUSY;
4082 priv->bitratemode = ratemode;
4083 orinoco_unlock(priv, &flags);
4084
Christoph Hellwig620554e2005-06-19 01:27:33 +02004085 return -EINPROGRESS;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004086}
4087
Christoph Hellwig620554e2005-06-19 01:27:33 +02004088static int orinoco_ioctl_getrate(struct net_device *dev,
4089 struct iw_request_info *info,
4090 struct iw_param *rrq,
4091 char *extra)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004092{
4093 struct orinoco_private *priv = netdev_priv(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004094 int err = 0;
David Kilroycfeb1db2009-02-04 23:05:53 +00004095 int bitrate, automatic;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004096 unsigned long flags;
4097
4098 if (orinoco_lock(priv, &flags) != 0)
4099 return -EBUSY;
4100
David Kilroycfeb1db2009-02-04 23:05:53 +00004101 orinoco_get_ratemode_cfg(priv->bitratemode, &bitrate, &automatic);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004102
4103 /* If the interface is running we try to find more about the
4104 current mode */
David Kilroycfeb1db2009-02-04 23:05:53 +00004105 if (netif_running(dev))
4106 err = orinoco_hw_get_act_bitrate(priv, &bitrate);
David Kilroy6fe9deb2009-02-04 23:05:43 +00004107
Linus Torvalds1da177e2005-04-16 15:20:36 -07004108 orinoco_unlock(priv, &flags);
4109
David Kilroycfeb1db2009-02-04 23:05:53 +00004110 rrq->value = bitrate;
4111 rrq->fixed = !automatic;
4112 rrq->disabled = 0;
4113
Linus Torvalds1da177e2005-04-16 15:20:36 -07004114 return err;
4115}
4116
Christoph Hellwig620554e2005-06-19 01:27:33 +02004117static int orinoco_ioctl_setpower(struct net_device *dev,
4118 struct iw_request_info *info,
4119 struct iw_param *prq,
4120 char *extra)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004121{
4122 struct orinoco_private *priv = netdev_priv(dev);
Christoph Hellwig620554e2005-06-19 01:27:33 +02004123 int err = -EINPROGRESS; /* Call commit handler */
Linus Torvalds1da177e2005-04-16 15:20:36 -07004124 unsigned long flags;
4125
4126 if (orinoco_lock(priv, &flags) != 0)
4127 return -EBUSY;
4128
4129 if (prq->disabled) {
4130 priv->pm_on = 0;
4131 } else {
4132 switch (prq->flags & IW_POWER_MODE) {
4133 case IW_POWER_UNICAST_R:
4134 priv->pm_mcast = 0;
4135 priv->pm_on = 1;
4136 break;
4137 case IW_POWER_ALL_R:
4138 priv->pm_mcast = 1;
4139 priv->pm_on = 1;
4140 break;
4141 case IW_POWER_ON:
4142 /* No flags : but we may have a value - Jean II */
4143 break;
4144 default:
4145 err = -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004146 goto out;
Pavel Roskinc08ad1e2005-11-29 02:59:27 -05004147 }
David Kilroy6fe9deb2009-02-04 23:05:43 +00004148
Linus Torvalds1da177e2005-04-16 15:20:36 -07004149 if (prq->flags & IW_POWER_TIMEOUT) {
4150 priv->pm_on = 1;
4151 priv->pm_timeout = prq->value / 1000;
4152 }
4153 if (prq->flags & IW_POWER_PERIOD) {
4154 priv->pm_on = 1;
4155 priv->pm_period = prq->value / 1000;
4156 }
4157 /* It's valid to not have a value if we are just toggling
4158 * the flags... Jean II */
David Kilroya94e8422009-02-04 23:05:44 +00004159 if (!priv->pm_on) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07004160 err = -EINVAL;
4161 goto out;
David Kilroy6fe9deb2009-02-04 23:05:43 +00004162 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07004163 }
4164
4165 out:
4166 orinoco_unlock(priv, &flags);
4167
4168 return err;
4169}
4170
Christoph Hellwig620554e2005-06-19 01:27:33 +02004171static int orinoco_ioctl_getpower(struct net_device *dev,
4172 struct iw_request_info *info,
4173 struct iw_param *prq,
4174 char *extra)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004175{
4176 struct orinoco_private *priv = netdev_priv(dev);
4177 hermes_t *hw = &priv->hw;
4178 int err = 0;
4179 u16 enable, period, timeout, mcast;
4180 unsigned long flags;
4181
4182 if (orinoco_lock(priv, &flags) != 0)
4183 return -EBUSY;
David Kilroy6fe9deb2009-02-04 23:05:43 +00004184
David Kilroyb2f30a02009-02-04 23:05:46 +00004185 err = hermes_read_wordrec(hw, USER_BAP,
4186 HERMES_RID_CNFPMENABLED, &enable);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004187 if (err)
4188 goto out;
4189
4190 err = hermes_read_wordrec(hw, USER_BAP,
4191 HERMES_RID_CNFMAXSLEEPDURATION, &period);
4192 if (err)
4193 goto out;
4194
David Kilroyb2f30a02009-02-04 23:05:46 +00004195 err = hermes_read_wordrec(hw, USER_BAP,
4196 HERMES_RID_CNFPMHOLDOVERDURATION, &timeout);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004197 if (err)
4198 goto out;
4199
David Kilroyb2f30a02009-02-04 23:05:46 +00004200 err = hermes_read_wordrec(hw, USER_BAP,
4201 HERMES_RID_CNFMULTICASTRECEIVE, &mcast);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004202 if (err)
4203 goto out;
4204
4205 prq->disabled = !enable;
4206 /* Note : by default, display the period */
4207 if ((prq->flags & IW_POWER_TYPE) == IW_POWER_TIMEOUT) {
4208 prq->flags = IW_POWER_TIMEOUT;
4209 prq->value = timeout * 1000;
4210 } else {
4211 prq->flags = IW_POWER_PERIOD;
4212 prq->value = period * 1000;
4213 }
4214 if (mcast)
4215 prq->flags |= IW_POWER_ALL_R;
4216 else
4217 prq->flags |= IW_POWER_UNICAST_R;
4218
4219 out:
4220 orinoco_unlock(priv, &flags);
4221
4222 return err;
4223}
4224
David Kilroyd03032a2008-08-21 23:28:02 +01004225static int orinoco_ioctl_set_encodeext(struct net_device *dev,
4226 struct iw_request_info *info,
4227 union iwreq_data *wrqu,
4228 char *extra)
4229{
4230 struct orinoco_private *priv = netdev_priv(dev);
4231 struct iw_point *encoding = &wrqu->encoding;
4232 struct iw_encode_ext *ext = (struct iw_encode_ext *)extra;
4233 int idx, alg = ext->alg, set_key = 1;
4234 unsigned long flags;
4235 int err = -EINVAL;
4236 u16 key_len;
4237
4238 if (orinoco_lock(priv, &flags) != 0)
4239 return -EBUSY;
4240
4241 /* Determine and validate the key index */
4242 idx = encoding->flags & IW_ENCODE_INDEX;
4243 if (idx) {
Johannes Berg2c7060022008-10-30 22:09:54 +01004244 if ((idx < 1) || (idx > 4))
David Kilroyd03032a2008-08-21 23:28:02 +01004245 goto out;
4246 idx--;
4247 } else
4248 idx = priv->tx_key;
4249
4250 if (encoding->flags & IW_ENCODE_DISABLED)
David Kilroy6fe9deb2009-02-04 23:05:43 +00004251 alg = IW_ENCODE_ALG_NONE;
David Kilroyd03032a2008-08-21 23:28:02 +01004252
4253 if (priv->has_wpa && (alg != IW_ENCODE_ALG_TKIP)) {
4254 /* Clear any TKIP TX key we had */
4255 (void) orinoco_clear_tkip_key(priv, priv->tx_key);
4256 }
4257
4258 if (ext->ext_flags & IW_ENCODE_EXT_SET_TX_KEY) {
4259 priv->tx_key = idx;
4260 set_key = ((alg == IW_ENCODE_ALG_TKIP) ||
4261 (ext->key_len > 0)) ? 1 : 0;
4262 }
4263
4264 if (set_key) {
4265 /* Set the requested key first */
4266 switch (alg) {
4267 case IW_ENCODE_ALG_NONE:
4268 priv->encode_alg = alg;
4269 priv->keys[idx].len = 0;
4270 break;
4271
4272 case IW_ENCODE_ALG_WEP:
4273 if (ext->key_len > SMALL_KEY_SIZE)
4274 key_len = LARGE_KEY_SIZE;
4275 else if (ext->key_len > 0)
4276 key_len = SMALL_KEY_SIZE;
4277 else
4278 goto out;
4279
4280 priv->encode_alg = alg;
4281 priv->keys[idx].len = cpu_to_le16(key_len);
4282
4283 key_len = min(ext->key_len, key_len);
4284
4285 memset(priv->keys[idx].data, 0, ORINOCO_MAX_KEY_SIZE);
4286 memcpy(priv->keys[idx].data, ext->key, key_len);
4287 break;
4288
4289 case IW_ENCODE_ALG_TKIP:
4290 {
4291 hermes_t *hw = &priv->hw;
4292 u8 *tkip_iv = NULL;
4293
4294 if (!priv->has_wpa ||
4295 (ext->key_len > sizeof(priv->tkip_key[0])))
4296 goto out;
4297
4298 priv->encode_alg = alg;
4299 memset(&priv->tkip_key[idx], 0,
4300 sizeof(priv->tkip_key[idx]));
4301 memcpy(&priv->tkip_key[idx], ext->key, ext->key_len);
4302
4303 if (ext->ext_flags & IW_ENCODE_EXT_RX_SEQ_VALID)
4304 tkip_iv = &ext->rx_seq[0];
4305
4306 err = __orinoco_hw_set_tkip_key(hw, idx,
4307 ext->ext_flags & IW_ENCODE_EXT_SET_TX_KEY,
4308 (u8 *) &priv->tkip_key[idx],
4309 tkip_iv, NULL);
4310 if (err)
4311 printk(KERN_ERR "%s: Error %d setting TKIP key"
4312 "\n", dev->name, err);
4313
4314 goto out;
4315 }
4316 default:
4317 goto out;
4318 }
4319 }
4320 err = -EINPROGRESS;
4321 out:
4322 orinoco_unlock(priv, &flags);
4323
4324 return err;
4325}
4326
4327static int orinoco_ioctl_get_encodeext(struct net_device *dev,
4328 struct iw_request_info *info,
4329 union iwreq_data *wrqu,
4330 char *extra)
4331{
4332 struct orinoco_private *priv = netdev_priv(dev);
4333 struct iw_point *encoding = &wrqu->encoding;
4334 struct iw_encode_ext *ext = (struct iw_encode_ext *)extra;
4335 int idx, max_key_len;
4336 unsigned long flags;
4337 int err;
4338
4339 if (orinoco_lock(priv, &flags) != 0)
4340 return -EBUSY;
4341
4342 err = -EINVAL;
4343 max_key_len = encoding->length - sizeof(*ext);
4344 if (max_key_len < 0)
4345 goto out;
4346
4347 idx = encoding->flags & IW_ENCODE_INDEX;
4348 if (idx) {
Johannes Berg2c7060022008-10-30 22:09:54 +01004349 if ((idx < 1) || (idx > 4))
David Kilroyd03032a2008-08-21 23:28:02 +01004350 goto out;
4351 idx--;
4352 } else
4353 idx = priv->tx_key;
4354
4355 encoding->flags = idx + 1;
4356 memset(ext, 0, sizeof(*ext));
4357
4358 ext->alg = priv->encode_alg;
4359 switch (priv->encode_alg) {
4360 case IW_ENCODE_ALG_NONE:
4361 ext->key_len = 0;
4362 encoding->flags |= IW_ENCODE_DISABLED;
4363 break;
4364 case IW_ENCODE_ALG_WEP:
David Kilroy75d31cf2008-09-12 22:28:18 +01004365 ext->key_len = min_t(u16, le16_to_cpu(priv->keys[idx].len),
4366 max_key_len);
David Kilroyd03032a2008-08-21 23:28:02 +01004367 memcpy(ext->key, priv->keys[idx].data, ext->key_len);
4368 encoding->flags |= IW_ENCODE_ENABLED;
4369 break;
4370 case IW_ENCODE_ALG_TKIP:
David Kilroy75d31cf2008-09-12 22:28:18 +01004371 ext->key_len = min_t(u16, sizeof(struct orinoco_tkip_key),
4372 max_key_len);
David Kilroyd03032a2008-08-21 23:28:02 +01004373 memcpy(ext->key, &priv->tkip_key[idx], ext->key_len);
4374 encoding->flags |= IW_ENCODE_ENABLED;
4375 break;
4376 }
4377
4378 err = 0;
4379 out:
4380 orinoco_unlock(priv, &flags);
4381
4382 return err;
4383}
4384
4385static int orinoco_ioctl_set_auth(struct net_device *dev,
4386 struct iw_request_info *info,
4387 union iwreq_data *wrqu, char *extra)
4388{
4389 struct orinoco_private *priv = netdev_priv(dev);
4390 hermes_t *hw = &priv->hw;
4391 struct iw_param *param = &wrqu->param;
4392 unsigned long flags;
4393 int ret = -EINPROGRESS;
4394
4395 if (orinoco_lock(priv, &flags) != 0)
4396 return -EBUSY;
4397
4398 switch (param->flags & IW_AUTH_INDEX) {
4399 case IW_AUTH_WPA_VERSION:
4400 case IW_AUTH_CIPHER_PAIRWISE:
4401 case IW_AUTH_CIPHER_GROUP:
4402 case IW_AUTH_RX_UNENCRYPTED_EAPOL:
4403 case IW_AUTH_PRIVACY_INVOKED:
4404 case IW_AUTH_DROP_UNENCRYPTED:
4405 /*
4406 * orinoco does not use these parameters
4407 */
4408 break;
4409
4410 case IW_AUTH_KEY_MGMT:
4411 /* wl_lkm implies value 2 == PSK for Hermes I
4412 * which ties in with WEXT
4413 * no other hints tho :(
4414 */
4415 priv->key_mgmt = param->value;
4416 break;
4417
4418 case IW_AUTH_TKIP_COUNTERMEASURES:
4419 /* When countermeasures are enabled, shut down the
4420 * card; when disabled, re-enable the card. This must
4421 * take effect immediately.
4422 *
4423 * TODO: Make sure that the EAPOL message is getting
4424 * out before card disabled
4425 */
4426 if (param->value) {
4427 priv->tkip_cm_active = 1;
4428 ret = hermes_enable_port(hw, 0);
4429 } else {
4430 priv->tkip_cm_active = 0;
4431 ret = hermes_disable_port(hw, 0);
4432 }
4433 break;
4434
4435 case IW_AUTH_80211_AUTH_ALG:
4436 if (param->value & IW_AUTH_ALG_SHARED_KEY)
4437 priv->wep_restrict = 1;
4438 else if (param->value & IW_AUTH_ALG_OPEN_SYSTEM)
4439 priv->wep_restrict = 0;
4440 else
4441 ret = -EINVAL;
4442 break;
4443
4444 case IW_AUTH_WPA_ENABLED:
4445 if (priv->has_wpa) {
4446 priv->wpa_enabled = param->value ? 1 : 0;
4447 } else {
4448 if (param->value)
4449 ret = -EOPNOTSUPP;
4450 /* else silently accept disable of WPA */
4451 priv->wpa_enabled = 0;
4452 }
4453 break;
4454
4455 default:
4456 ret = -EOPNOTSUPP;
4457 }
4458
4459 orinoco_unlock(priv, &flags);
4460 return ret;
4461}
4462
4463static int orinoco_ioctl_get_auth(struct net_device *dev,
4464 struct iw_request_info *info,
4465 union iwreq_data *wrqu, char *extra)
4466{
4467 struct orinoco_private *priv = netdev_priv(dev);
4468 struct iw_param *param = &wrqu->param;
4469 unsigned long flags;
4470 int ret = 0;
4471
4472 if (orinoco_lock(priv, &flags) != 0)
4473 return -EBUSY;
4474
4475 switch (param->flags & IW_AUTH_INDEX) {
4476 case IW_AUTH_KEY_MGMT:
4477 param->value = priv->key_mgmt;
4478 break;
4479
4480 case IW_AUTH_TKIP_COUNTERMEASURES:
4481 param->value = priv->tkip_cm_active;
4482 break;
4483
4484 case IW_AUTH_80211_AUTH_ALG:
4485 if (priv->wep_restrict)
4486 param->value = IW_AUTH_ALG_SHARED_KEY;
4487 else
4488 param->value = IW_AUTH_ALG_OPEN_SYSTEM;
4489 break;
4490
4491 case IW_AUTH_WPA_ENABLED:
4492 param->value = priv->wpa_enabled;
4493 break;
4494
4495 default:
4496 ret = -EOPNOTSUPP;
4497 }
4498
4499 orinoco_unlock(priv, &flags);
4500 return ret;
4501}
4502
4503static int orinoco_ioctl_set_genie(struct net_device *dev,
4504 struct iw_request_info *info,
4505 union iwreq_data *wrqu, char *extra)
4506{
4507 struct orinoco_private *priv = netdev_priv(dev);
4508 u8 *buf;
4509 unsigned long flags;
David Kilroyd03032a2008-08-21 23:28:02 +01004510
Johannes Berg2c7060022008-10-30 22:09:54 +01004511 /* cut off at IEEE80211_MAX_DATA_LEN */
4512 if ((wrqu->data.length > IEEE80211_MAX_DATA_LEN) ||
David Kilroyd03032a2008-08-21 23:28:02 +01004513 (wrqu->data.length && (extra == NULL)))
4514 return -EINVAL;
4515
David Kilroyd03032a2008-08-21 23:28:02 +01004516 if (wrqu->data.length) {
4517 buf = kmalloc(wrqu->data.length, GFP_KERNEL);
Andrey Borzenkov7fe99c42009-01-20 20:26:46 +03004518 if (buf == NULL)
4519 return -ENOMEM;
David Kilroyd03032a2008-08-21 23:28:02 +01004520
4521 memcpy(buf, extra, wrqu->data.length);
Andrey Borzenkov7fe99c42009-01-20 20:26:46 +03004522 } else
4523 buf = NULL;
4524
4525 if (orinoco_lock(priv, &flags) != 0) {
4526 kfree(buf);
4527 return -EBUSY;
David Kilroyd03032a2008-08-21 23:28:02 +01004528 }
4529
Andrey Borzenkov7fe99c42009-01-20 20:26:46 +03004530 kfree(priv->wpa_ie);
4531 priv->wpa_ie = buf;
4532 priv->wpa_ie_len = wrqu->data.length;
4533
David Kilroyd03032a2008-08-21 23:28:02 +01004534 if (priv->wpa_ie) {
4535 /* Looks like wl_lkm wants to check the auth alg, and
4536 * somehow pass it to the firmware.
4537 * Instead it just calls the key mgmt rid
4538 * - we do this in set auth.
4539 */
4540 }
4541
David Kilroyd03032a2008-08-21 23:28:02 +01004542 orinoco_unlock(priv, &flags);
Andrey Borzenkov7fe99c42009-01-20 20:26:46 +03004543 return 0;
David Kilroyd03032a2008-08-21 23:28:02 +01004544}
4545
4546static int orinoco_ioctl_get_genie(struct net_device *dev,
4547 struct iw_request_info *info,
4548 union iwreq_data *wrqu, char *extra)
4549{
4550 struct orinoco_private *priv = netdev_priv(dev);
4551 unsigned long flags;
4552 int err = 0;
4553
4554 if (orinoco_lock(priv, &flags) != 0)
4555 return -EBUSY;
4556
4557 if ((priv->wpa_ie_len == 0) || (priv->wpa_ie == NULL)) {
4558 wrqu->data.length = 0;
4559 goto out;
4560 }
4561
4562 if (wrqu->data.length < priv->wpa_ie_len) {
4563 err = -E2BIG;
4564 goto out;
4565 }
4566
4567 wrqu->data.length = priv->wpa_ie_len;
4568 memcpy(extra, priv->wpa_ie, priv->wpa_ie_len);
4569
4570out:
4571 orinoco_unlock(priv, &flags);
4572 return err;
4573}
4574
4575static int orinoco_ioctl_set_mlme(struct net_device *dev,
4576 struct iw_request_info *info,
4577 union iwreq_data *wrqu, char *extra)
4578{
4579 struct orinoco_private *priv = netdev_priv(dev);
4580 hermes_t *hw = &priv->hw;
4581 struct iw_mlme *mlme = (struct iw_mlme *)extra;
4582 unsigned long flags;
4583 int ret = 0;
4584
4585 if (orinoco_lock(priv, &flags) != 0)
4586 return -EBUSY;
4587
4588 switch (mlme->cmd) {
4589 case IW_MLME_DEAUTH:
4590 /* silently ignore */
4591 break;
4592
4593 case IW_MLME_DISASSOC:
4594 {
4595 struct {
4596 u8 addr[ETH_ALEN];
4597 __le16 reason_code;
4598 } __attribute__ ((packed)) buf;
4599
4600 memcpy(buf.addr, mlme->addr.sa_data, ETH_ALEN);
4601 buf.reason_code = cpu_to_le16(mlme->reason_code);
4602 ret = HERMES_WRITE_RECORD(hw, USER_BAP,
4603 HERMES_RID_CNFDISASSOCIATE,
4604 &buf);
4605 break;
4606 }
4607 default:
4608 ret = -EOPNOTSUPP;
4609 }
4610
4611 orinoco_unlock(priv, &flags);
4612 return ret;
4613}
4614
Christoph Hellwig620554e2005-06-19 01:27:33 +02004615static int orinoco_ioctl_getretry(struct net_device *dev,
4616 struct iw_request_info *info,
4617 struct iw_param *rrq,
4618 char *extra)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004619{
4620 struct orinoco_private *priv = netdev_priv(dev);
4621 hermes_t *hw = &priv->hw;
4622 int err = 0;
4623 u16 short_limit, long_limit, lifetime;
4624 unsigned long flags;
4625
4626 if (orinoco_lock(priv, &flags) != 0)
4627 return -EBUSY;
David Kilroy6fe9deb2009-02-04 23:05:43 +00004628
Linus Torvalds1da177e2005-04-16 15:20:36 -07004629 err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_SHORTRETRYLIMIT,
4630 &short_limit);
4631 if (err)
4632 goto out;
4633
4634 err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_LONGRETRYLIMIT,
4635 &long_limit);
4636 if (err)
4637 goto out;
4638
4639 err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_MAXTRANSMITLIFETIME,
4640 &lifetime);
4641 if (err)
4642 goto out;
4643
4644 rrq->disabled = 0; /* Can't be disabled */
4645
4646 /* Note : by default, display the retry number */
4647 if ((rrq->flags & IW_RETRY_TYPE) == IW_RETRY_LIFETIME) {
4648 rrq->flags = IW_RETRY_LIFETIME;
4649 rrq->value = lifetime * 1000; /* ??? */
4650 } else {
4651 /* By default, display the min number */
Jean Tourrilheseeec9f12006-08-29 18:02:31 -07004652 if ((rrq->flags & IW_RETRY_LONG)) {
4653 rrq->flags = IW_RETRY_LIMIT | IW_RETRY_LONG;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004654 rrq->value = long_limit;
4655 } else {
4656 rrq->flags = IW_RETRY_LIMIT;
4657 rrq->value = short_limit;
David Kilroya94e8422009-02-04 23:05:44 +00004658 if (short_limit != long_limit)
Jean Tourrilheseeec9f12006-08-29 18:02:31 -07004659 rrq->flags |= IW_RETRY_SHORT;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004660 }
4661 }
4662
4663 out:
4664 orinoco_unlock(priv, &flags);
4665
4666 return err;
4667}
4668
Christoph Hellwig620554e2005-06-19 01:27:33 +02004669static int orinoco_ioctl_reset(struct net_device *dev,
4670 struct iw_request_info *info,
4671 void *wrqu,
4672 char *extra)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004673{
4674 struct orinoco_private *priv = netdev_priv(dev);
Christoph Hellwig620554e2005-06-19 01:27:33 +02004675
David Kilroya94e8422009-02-04 23:05:44 +00004676 if (!capable(CAP_NET_ADMIN))
Christoph Hellwig620554e2005-06-19 01:27:33 +02004677 return -EPERM;
4678
4679 if (info->cmd == (SIOCIWFIRSTPRIV + 0x1)) {
4680 printk(KERN_DEBUG "%s: Forcing reset!\n", dev->name);
4681
4682 /* Firmware reset */
David Howellsc4028952006-11-22 14:57:56 +00004683 orinoco_reset(&priv->reset_work);
Christoph Hellwig620554e2005-06-19 01:27:33 +02004684 } else {
4685 printk(KERN_DEBUG "%s: Force scheduling reset!\n", dev->name);
4686
4687 schedule_work(&priv->reset_work);
4688 }
4689
4690 return 0;
4691}
4692
4693static int orinoco_ioctl_setibssport(struct net_device *dev,
4694 struct iw_request_info *info,
4695 void *wrqu,
4696 char *extra)
4697
4698{
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 unsigned long flags;
4702
4703 if (orinoco_lock(priv, &flags) != 0)
4704 return -EBUSY;
4705
4706 priv->ibss_port = val ;
4707
4708 /* Actually update the mode we are using */
4709 set_port_type(priv);
4710
4711 orinoco_unlock(priv, &flags);
Christoph Hellwig620554e2005-06-19 01:27:33 +02004712 return -EINPROGRESS; /* Call commit handler */
Linus Torvalds1da177e2005-04-16 15:20:36 -07004713}
4714
Christoph Hellwig620554e2005-06-19 01:27:33 +02004715static int orinoco_ioctl_getibssport(struct net_device *dev,
4716 struct iw_request_info *info,
4717 void *wrqu,
4718 char *extra)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004719{
4720 struct orinoco_private *priv = netdev_priv(dev);
Christoph Hellwig620554e2005-06-19 01:27:33 +02004721 int *val = (int *) extra;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004722
4723 *val = priv->ibss_port;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004724 return 0;
4725}
4726
Christoph Hellwig620554e2005-06-19 01:27:33 +02004727static int orinoco_ioctl_setport3(struct net_device *dev,
4728 struct iw_request_info *info,
4729 void *wrqu,
4730 char *extra)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004731{
4732 struct orinoco_private *priv = netdev_priv(dev);
David Kilroya94e8422009-02-04 23:05:44 +00004733 int val = *((int *) extra);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004734 int err = 0;
4735 unsigned long flags;
4736
4737 if (orinoco_lock(priv, &flags) != 0)
4738 return -EBUSY;
4739
4740 switch (val) {
4741 case 0: /* Try to do IEEE ad-hoc mode */
David Kilroya94e8422009-02-04 23:05:44 +00004742 if (!priv->has_ibss) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07004743 err = -EINVAL;
4744 break;
4745 }
4746 priv->prefer_port3 = 0;
David Kilroy6fe9deb2009-02-04 23:05:43 +00004747
Linus Torvalds1da177e2005-04-16 15:20:36 -07004748 break;
4749
4750 case 1: /* Try to do Lucent proprietary ad-hoc mode */
David Kilroya94e8422009-02-04 23:05:44 +00004751 if (!priv->has_port3) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07004752 err = -EINVAL;
4753 break;
4754 }
4755 priv->prefer_port3 = 1;
4756 break;
4757
4758 default:
4759 err = -EINVAL;
4760 }
4761
David Kilroya94e8422009-02-04 23:05:44 +00004762 if (!err) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07004763 /* Actually update the mode we are using */
4764 set_port_type(priv);
Christoph Hellwig620554e2005-06-19 01:27:33 +02004765 err = -EINPROGRESS;
4766 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07004767
4768 orinoco_unlock(priv, &flags);
4769
4770 return err;
4771}
4772
Christoph Hellwig620554e2005-06-19 01:27:33 +02004773static int orinoco_ioctl_getport3(struct net_device *dev,
4774 struct iw_request_info *info,
4775 void *wrqu,
4776 char *extra)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004777{
4778 struct orinoco_private *priv = netdev_priv(dev);
Christoph Hellwig620554e2005-06-19 01:27:33 +02004779 int *val = (int *) extra;
4780
4781 *val = priv->prefer_port3;
4782 return 0;
4783}
4784
4785static int orinoco_ioctl_setpreamble(struct net_device *dev,
4786 struct iw_request_info *info,
4787 void *wrqu,
4788 char *extra)
4789{
4790 struct orinoco_private *priv = netdev_priv(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004791 unsigned long flags;
Christoph Hellwig620554e2005-06-19 01:27:33 +02004792 int val;
4793
David Kilroya94e8422009-02-04 23:05:44 +00004794 if (!priv->has_preamble)
Christoph Hellwig620554e2005-06-19 01:27:33 +02004795 return -EOPNOTSUPP;
4796
4797 /* 802.11b has recently defined some short preamble.
4798 * Basically, the Phy header has been reduced in size.
4799 * This increase performance, especially at high rates
4800 * (the preamble is transmitted at 1Mb/s), unfortunately
4801 * this give compatibility troubles... - Jean II */
David Kilroya94e8422009-02-04 23:05:44 +00004802 val = *((int *) extra);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004803
4804 if (orinoco_lock(priv, &flags) != 0)
4805 return -EBUSY;
4806
Christoph Hellwig620554e2005-06-19 01:27:33 +02004807 if (val)
4808 priv->preamble = 1;
4809 else
4810 priv->preamble = 0;
4811
Linus Torvalds1da177e2005-04-16 15:20:36 -07004812 orinoco_unlock(priv, &flags);
Christoph Hellwig620554e2005-06-19 01:27:33 +02004813
4814 return -EINPROGRESS; /* Call commit handler */
4815}
4816
4817static int orinoco_ioctl_getpreamble(struct net_device *dev,
4818 struct iw_request_info *info,
4819 void *wrqu,
4820 char *extra)
4821{
4822 struct orinoco_private *priv = netdev_priv(dev);
4823 int *val = (int *) extra;
4824
David Kilroya94e8422009-02-04 23:05:44 +00004825 if (!priv->has_preamble)
Christoph Hellwig620554e2005-06-19 01:27:33 +02004826 return -EOPNOTSUPP;
4827
4828 *val = priv->preamble;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004829 return 0;
4830}
4831
Christoph Hellwig620554e2005-06-19 01:27:33 +02004832/* ioctl interface to hermes_read_ltv()
4833 * To use with iwpriv, pass the RID as the token argument, e.g.
4834 * iwpriv get_rid [0xfc00]
4835 * At least Wireless Tools 25 is required to use iwpriv.
4836 * For Wireless Tools 25 and 26 append "dummy" are the end. */
4837static int orinoco_ioctl_getrid(struct net_device *dev,
4838 struct iw_request_info *info,
4839 struct iw_point *data,
4840 char *extra)
4841{
4842 struct orinoco_private *priv = netdev_priv(dev);
4843 hermes_t *hw = &priv->hw;
4844 int rid = data->flags;
4845 u16 length;
4846 int err;
4847 unsigned long flags;
4848
4849 /* It's a "get" function, but we don't want users to access the
4850 * WEP key and other raw firmware data */
David Kilroya94e8422009-02-04 23:05:44 +00004851 if (!capable(CAP_NET_ADMIN))
Christoph Hellwig620554e2005-06-19 01:27:33 +02004852 return -EPERM;
4853
4854 if (rid < 0xfc00 || rid > 0xffff)
4855 return -EINVAL;
4856
4857 if (orinoco_lock(priv, &flags) != 0)
4858 return -EBUSY;
4859
4860 err = hermes_read_ltv(hw, USER_BAP, rid, MAX_RID_LEN, &length,
4861 extra);
4862 if (err)
4863 goto out;
4864
4865 data->length = min_t(u16, HERMES_RECLEN_TO_BYTES(length),
4866 MAX_RID_LEN);
4867
4868 out:
4869 orinoco_unlock(priv, &flags);
4870 return err;
4871}
4872
David Kilroy17a1a882008-08-21 23:27:47 +01004873/* Trigger a scan (look for other cells in the vicinity) */
Christoph Hellwig95dd91f2005-06-19 01:27:56 +02004874static int orinoco_ioctl_setscan(struct net_device *dev,
4875 struct iw_request_info *info,
David Kilroy9930cce2008-09-13 12:22:05 +01004876 struct iw_point *srq,
Christoph Hellwig95dd91f2005-06-19 01:27:56 +02004877 char *extra)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004878{
4879 struct orinoco_private *priv = netdev_priv(dev);
Christoph Hellwig95dd91f2005-06-19 01:27:56 +02004880 hermes_t *hw = &priv->hw;
David Kilroy0753bba2008-08-21 23:27:46 +01004881 struct iw_scan_req *si = (struct iw_scan_req *) extra;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004882 int err = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004883 unsigned long flags;
4884
Christoph Hellwig95dd91f2005-06-19 01:27:56 +02004885 /* Note : you may have realised that, as this is a SET operation,
Alexey Dobriyan7f927fc2006-03-28 01:56:53 -08004886 * this is privileged and therefore a normal user can't
Christoph Hellwig95dd91f2005-06-19 01:27:56 +02004887 * perform scanning.
4888 * This is not an error, while the device perform scanning,
4889 * traffic doesn't flow, so it's a perfect DoS...
4890 * Jean II */
Linus Torvalds1da177e2005-04-16 15:20:36 -07004891
Christoph Hellwig95dd91f2005-06-19 01:27:56 +02004892 if (orinoco_lock(priv, &flags) != 0)
4893 return -EBUSY;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004894
Christoph Hellwig95dd91f2005-06-19 01:27:56 +02004895 /* Scanning with port 0 disabled would fail */
4896 if (!netif_running(dev)) {
4897 err = -ENETDOWN;
4898 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004899 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07004900
Christoph Hellwig95dd91f2005-06-19 01:27:56 +02004901 /* In monitor mode, the scan results are always empty.
4902 * Probe responses are passed to the driver as received
4903 * frames and could be processed in software. */
4904 if (priv->iw_mode == IW_MODE_MONITOR) {
4905 err = -EOPNOTSUPP;
4906 goto out;
4907 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07004908
Christoph Hellwig95dd91f2005-06-19 01:27:56 +02004909 /* Note : because we don't lock out the irq handler, the way
4910 * we access scan variables in priv is critical.
4911 * o scan_inprogress : not touched by irq handler
4912 * o scan_mode : not touched by irq handler
Christoph Hellwig95dd91f2005-06-19 01:27:56 +02004913 * Before modifying anything on those variables, please think hard !
4914 * Jean II */
4915
Christoph Hellwig95dd91f2005-06-19 01:27:56 +02004916 /* Save flags */
4917 priv->scan_mode = srq->flags;
4918
4919 /* Always trigger scanning, even if it's in progress.
4920 * This way, if the info frame get lost, we will recover somewhat
4921 * gracefully - Jean II */
4922
4923 if (priv->has_hostscan) {
4924 switch (priv->firmware_type) {
4925 case FIRMWARE_TYPE_SYMBOL:
4926 err = hermes_write_wordrec(hw, USER_BAP,
David Kilroyb2f30a02009-02-04 23:05:46 +00004927 HERMES_RID_CNFHOSTSCAN_SYMBOL,
4928 HERMES_HOSTSCAN_SYMBOL_ONCE |
4929 HERMES_HOSTSCAN_SYMBOL_BCAST);
Christoph Hellwig95dd91f2005-06-19 01:27:56 +02004930 break;
4931 case FIRMWARE_TYPE_INTERSIL: {
Pavel Roskind133ae42005-09-23 04:18:06 -04004932 __le16 req[3];
Christoph Hellwig95dd91f2005-06-19 01:27:56 +02004933
4934 req[0] = cpu_to_le16(0x3fff); /* All channels */
4935 req[1] = cpu_to_le16(0x0001); /* rate 1 Mbps */
4936 req[2] = 0; /* Any ESSID */
4937 err = HERMES_WRITE_RECORD(hw, USER_BAP,
4938 HERMES_RID_CNFHOSTSCAN, &req);
4939 }
4940 break;
4941 case FIRMWARE_TYPE_AGERE:
David Kilroy0753bba2008-08-21 23:27:46 +01004942 if (priv->scan_mode & IW_SCAN_THIS_ESSID) {
4943 struct hermes_idstring idbuf;
4944 size_t len = min(sizeof(idbuf.val),
4945 (size_t) si->essid_len);
4946 idbuf.len = cpu_to_le16(len);
4947 memcpy(idbuf.val, si->essid, len);
4948
4949 err = hermes_write_ltv(hw, USER_BAP,
4950 HERMES_RID_CNFSCANSSID_AGERE,
4951 HERMES_BYTES_TO_RECLEN(len + 2),
4952 &idbuf);
4953 } else
4954 err = hermes_write_wordrec(hw, USER_BAP,
Christoph Hellwig95dd91f2005-06-19 01:27:56 +02004955 HERMES_RID_CNFSCANSSID_AGERE,
4956 0); /* Any ESSID */
4957 if (err)
4958 break;
4959
David Kilroy01632fa2008-08-21 23:27:58 +01004960 if (priv->has_ext_scan) {
4961 /* Clear scan results at the start of
4962 * an extended scan */
4963 orinoco_clear_scan_results(priv,
4964 msecs_to_jiffies(15000));
4965
4966 /* TODO: Is this available on older firmware?
4967 * Can we use it to scan specific channels
4968 * for IW_SCAN_THIS_FREQ? */
4969 err = hermes_write_wordrec(hw, USER_BAP,
4970 HERMES_RID_CNFSCANCHANNELS2GHZ,
4971 0x7FFF);
4972 if (err)
4973 goto out;
4974
4975 err = hermes_inquire(hw,
4976 HERMES_INQ_CHANNELINFO);
4977 } else
4978 err = hermes_inquire(hw, HERMES_INQ_SCAN);
Christoph Hellwig95dd91f2005-06-19 01:27:56 +02004979 break;
4980 }
4981 } else
4982 err = hermes_inquire(hw, HERMES_INQ_SCAN);
4983
4984 /* One more client */
David Kilroya94e8422009-02-04 23:05:44 +00004985 if (!err)
Christoph Hellwig95dd91f2005-06-19 01:27:56 +02004986 priv->scan_inprogress = 1;
4987
4988 out:
4989 orinoco_unlock(priv, &flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004990 return err;
4991}
4992
Dan Williams1e3428e2007-10-10 23:56:25 -04004993#define MAX_CUSTOM_LEN 64
4994
Christoph Hellwig95dd91f2005-06-19 01:27:56 +02004995/* Translate scan data returned from the card to a card independant
David Kilroy17a1a882008-08-21 23:27:47 +01004996 * format that the Wireless Tools will understand - Jean II */
Dan Williams1e3428e2007-10-10 23:56:25 -04004997static inline char *orinoco_translate_scan(struct net_device *dev,
David S. Millerccc58052008-06-16 18:50:49 -07004998 struct iw_request_info *info,
Dan Williams1e3428e2007-10-10 23:56:25 -04004999 char *current_ev,
5000 char *end_buf,
5001 union hermes_scan_info *bss,
Pavel Roskindfe1baf2008-11-10 09:25:53 -05005002 unsigned long last_scanned)
Christoph Hellwig95dd91f2005-06-19 01:27:56 +02005003{
5004 struct orinoco_private *priv = netdev_priv(dev);
Christoph Hellwig95dd91f2005-06-19 01:27:56 +02005005 u16 capabilities;
5006 u16 channel;
5007 struct iw_event iwe; /* Temporary buffer */
Dan Williams1e3428e2007-10-10 23:56:25 -04005008 char custom[MAX_CUSTOM_LEN];
Christoph Hellwig95dd91f2005-06-19 01:27:56 +02005009
David Kilroy17a1a882008-08-21 23:27:47 +01005010 memset(&iwe, 0, sizeof(iwe));
5011
Dan Williams1e3428e2007-10-10 23:56:25 -04005012 /* First entry *MUST* be the AP MAC address */
5013 iwe.cmd = SIOCGIWAP;
5014 iwe.u.ap_addr.sa_family = ARPHRD_ETHER;
5015 memcpy(iwe.u.ap_addr.sa_data, bss->a.bssid, ETH_ALEN);
David S. Millerccc58052008-06-16 18:50:49 -07005016 current_ev = iwe_stream_add_event(info, current_ev, end_buf,
5017 &iwe, IW_EV_ADDR_LEN);
Dan Williams1e3428e2007-10-10 23:56:25 -04005018
5019 /* Other entries will be displayed in the order we give them */
5020
5021 /* Add the ESSID */
5022 iwe.u.data.length = le16_to_cpu(bss->a.essid_len);
5023 if (iwe.u.data.length > 32)
5024 iwe.u.data.length = 32;
5025 iwe.cmd = SIOCGIWESSID;
5026 iwe.u.data.flags = 1;
David S. Millerccc58052008-06-16 18:50:49 -07005027 current_ev = iwe_stream_add_point(info, current_ev, end_buf,
5028 &iwe, bss->a.essid);
Dan Williams1e3428e2007-10-10 23:56:25 -04005029
5030 /* Add mode */
5031 iwe.cmd = SIOCGIWMODE;
5032 capabilities = le16_to_cpu(bss->a.capabilities);
David Kilroy17a1a882008-08-21 23:27:47 +01005033 if (capabilities & (WLAN_CAPABILITY_ESS | WLAN_CAPABILITY_IBSS)) {
5034 if (capabilities & WLAN_CAPABILITY_ESS)
Dan Williams1e3428e2007-10-10 23:56:25 -04005035 iwe.u.mode = IW_MODE_MASTER;
Christoph Hellwig95dd91f2005-06-19 01:27:56 +02005036 else
Dan Williams1e3428e2007-10-10 23:56:25 -04005037 iwe.u.mode = IW_MODE_ADHOC;
David S. Millerccc58052008-06-16 18:50:49 -07005038 current_ev = iwe_stream_add_event(info, current_ev, end_buf,
5039 &iwe, IW_EV_UINT_LEN);
Christoph Hellwig95dd91f2005-06-19 01:27:56 +02005040 }
5041
Dan Williams1e3428e2007-10-10 23:56:25 -04005042 channel = bss->s.channel;
5043 if ((channel >= 1) && (channel <= NUM_CHANNELS)) {
David Kilroy17a1a882008-08-21 23:27:47 +01005044 /* Add channel and frequency */
Dan Williams1e3428e2007-10-10 23:56:25 -04005045 iwe.cmd = SIOCGIWFREQ;
David Kilroy17a1a882008-08-21 23:27:47 +01005046 iwe.u.freq.m = channel;
5047 iwe.u.freq.e = 0;
5048 current_ev = iwe_stream_add_event(info, current_ev, end_buf,
5049 &iwe, IW_EV_FREQ_LEN);
5050
David Kilroy9ee677c2008-12-23 14:03:38 +00005051 iwe.u.freq.m = ieee80211_dsss_chan_to_freq(channel) * 100000;
Dan Williams1e3428e2007-10-10 23:56:25 -04005052 iwe.u.freq.e = 1;
David S. Millerccc58052008-06-16 18:50:49 -07005053 current_ev = iwe_stream_add_event(info, current_ev, end_buf,
Dan Williams1e3428e2007-10-10 23:56:25 -04005054 &iwe, IW_EV_FREQ_LEN);
Christoph Hellwig95dd91f2005-06-19 01:27:56 +02005055 }
5056
David Kilroy17a1a882008-08-21 23:27:47 +01005057 /* Add quality statistics. level and noise in dB. No link quality */
Dan Williams1e3428e2007-10-10 23:56:25 -04005058 iwe.cmd = IWEVQUAL;
David Kilroy17a1a882008-08-21 23:27:47 +01005059 iwe.u.qual.updated = IW_QUAL_DBM | IW_QUAL_QUAL_INVALID;
Dan Williams1e3428e2007-10-10 23:56:25 -04005060 iwe.u.qual.level = (__u8) le16_to_cpu(bss->a.level) - 0x95;
5061 iwe.u.qual.noise = (__u8) le16_to_cpu(bss->a.noise) - 0x95;
5062 /* Wireless tools prior to 27.pre22 will show link quality
5063 * anyway, so we provide a reasonable value. */
5064 if (iwe.u.qual.level > iwe.u.qual.noise)
5065 iwe.u.qual.qual = iwe.u.qual.level - iwe.u.qual.noise;
5066 else
5067 iwe.u.qual.qual = 0;
David S. Millerccc58052008-06-16 18:50:49 -07005068 current_ev = iwe_stream_add_event(info, current_ev, end_buf,
5069 &iwe, IW_EV_QUAL_LEN);
Christoph Hellwig95dd91f2005-06-19 01:27:56 +02005070
Dan Williams1e3428e2007-10-10 23:56:25 -04005071 /* Add encryption capability */
5072 iwe.cmd = SIOCGIWENCODE;
David Kilroy17a1a882008-08-21 23:27:47 +01005073 if (capabilities & WLAN_CAPABILITY_PRIVACY)
Dan Williams1e3428e2007-10-10 23:56:25 -04005074 iwe.u.data.flags = IW_ENCODE_ENABLED | IW_ENCODE_NOKEY;
5075 else
5076 iwe.u.data.flags = IW_ENCODE_DISABLED;
5077 iwe.u.data.length = 0;
David S. Millerccc58052008-06-16 18:50:49 -07005078 current_ev = iwe_stream_add_point(info, current_ev, end_buf,
David Kilroy17a1a882008-08-21 23:27:47 +01005079 &iwe, NULL);
Christoph Hellwig95dd91f2005-06-19 01:27:56 +02005080
Dan Williams1e3428e2007-10-10 23:56:25 -04005081 /* Bit rate is not available in Lucent/Agere firmwares */
5082 if (priv->firmware_type != FIRMWARE_TYPE_AGERE) {
David S. Millerccc58052008-06-16 18:50:49 -07005083 char *current_val = current_ev + iwe_stream_lcp_len(info);
Dan Williams1e3428e2007-10-10 23:56:25 -04005084 int i;
5085 int step;
Christoph Hellwig95dd91f2005-06-19 01:27:56 +02005086
Dan Williams1e3428e2007-10-10 23:56:25 -04005087 if (priv->firmware_type == FIRMWARE_TYPE_SYMBOL)
5088 step = 2;
Christoph Hellwig95dd91f2005-06-19 01:27:56 +02005089 else
Dan Williams1e3428e2007-10-10 23:56:25 -04005090 step = 1;
Christoph Hellwig95dd91f2005-06-19 01:27:56 +02005091
Dan Williams1e3428e2007-10-10 23:56:25 -04005092 iwe.cmd = SIOCGIWRATE;
5093 /* Those two flags are ignored... */
5094 iwe.u.bitrate.fixed = iwe.u.bitrate.disabled = 0;
5095 /* Max 10 values */
5096 for (i = 0; i < 10; i += step) {
5097 /* NULL terminated */
5098 if (bss->p.rates[i] == 0x0)
5099 break;
5100 /* Bit rate given in 500 kb/s units (+ 0x80) */
David Kilroy17a1a882008-08-21 23:27:47 +01005101 iwe.u.bitrate.value =
5102 ((bss->p.rates[i] & 0x7f) * 500000);
David S. Millerccc58052008-06-16 18:50:49 -07005103 current_val = iwe_stream_add_value(info, current_ev,
5104 current_val,
Dan Williams1e3428e2007-10-10 23:56:25 -04005105 end_buf, &iwe,
5106 IW_EV_PARAM_LEN);
Christoph Hellwig95dd91f2005-06-19 01:27:56 +02005107 }
Dan Williams1e3428e2007-10-10 23:56:25 -04005108 /* Check if we added any event */
David S. Millerccc58052008-06-16 18:50:49 -07005109 if ((current_val - current_ev) > iwe_stream_lcp_len(info))
Dan Williams1e3428e2007-10-10 23:56:25 -04005110 current_ev = current_val;
Christoph Hellwig95dd91f2005-06-19 01:27:56 +02005111 }
Dan Williams1e3428e2007-10-10 23:56:25 -04005112
David Kilroy17a1a882008-08-21 23:27:47 +01005113 /* Beacon interval */
5114 iwe.cmd = IWEVCUSTOM;
5115 iwe.u.data.length = snprintf(custom, MAX_CUSTOM_LEN,
5116 "bcn_int=%d",
5117 le16_to_cpu(bss->a.beacon_interv));
5118 if (iwe.u.data.length)
5119 current_ev = iwe_stream_add_point(info, current_ev, end_buf,
5120 &iwe, custom);
5121
5122 /* Capabilites */
5123 iwe.cmd = IWEVCUSTOM;
5124 iwe.u.data.length = snprintf(custom, MAX_CUSTOM_LEN,
5125 "capab=0x%04x",
5126 capabilities);
5127 if (iwe.u.data.length)
5128 current_ev = iwe_stream_add_point(info, current_ev, end_buf,
5129 &iwe, custom);
5130
5131 /* Add EXTRA: Age to display seconds since last beacon/probe response
5132 * for given network. */
5133 iwe.cmd = IWEVCUSTOM;
5134 iwe.u.data.length = snprintf(custom, MAX_CUSTOM_LEN,
5135 " Last beacon: %dms ago",
5136 jiffies_to_msecs(jiffies - last_scanned));
5137 if (iwe.u.data.length)
5138 current_ev = iwe_stream_add_point(info, current_ev, end_buf,
5139 &iwe, custom);
5140
Dan Williams1e3428e2007-10-10 23:56:25 -04005141 return current_ev;
Christoph Hellwig95dd91f2005-06-19 01:27:56 +02005142}
5143
David Kilroy01632fa2008-08-21 23:27:58 +01005144static inline char *orinoco_translate_ext_scan(struct net_device *dev,
5145 struct iw_request_info *info,
5146 char *current_ev,
5147 char *end_buf,
5148 struct agere_ext_scan_info *bss,
Pavel Roskindfe1baf2008-11-10 09:25:53 -05005149 unsigned long last_scanned)
David Kilroy01632fa2008-08-21 23:27:58 +01005150{
5151 u16 capabilities;
5152 u16 channel;
5153 struct iw_event iwe; /* Temporary buffer */
5154 char custom[MAX_CUSTOM_LEN];
5155 u8 *ie;
5156
5157 memset(&iwe, 0, sizeof(iwe));
5158
5159 /* First entry *MUST* be the AP MAC address */
5160 iwe.cmd = SIOCGIWAP;
5161 iwe.u.ap_addr.sa_family = ARPHRD_ETHER;
5162 memcpy(iwe.u.ap_addr.sa_data, bss->bssid, ETH_ALEN);
5163 current_ev = iwe_stream_add_event(info, current_ev, end_buf,
5164 &iwe, IW_EV_ADDR_LEN);
5165
5166 /* Other entries will be displayed in the order we give them */
5167
5168 /* Add the ESSID */
5169 ie = bss->data;
5170 iwe.u.data.length = ie[1];
5171 if (iwe.u.data.length) {
5172 if (iwe.u.data.length > 32)
5173 iwe.u.data.length = 32;
5174 iwe.cmd = SIOCGIWESSID;
5175 iwe.u.data.flags = 1;
5176 current_ev = iwe_stream_add_point(info, current_ev, end_buf,
5177 &iwe, &ie[2]);
5178 }
5179
5180 /* Add mode */
5181 capabilities = le16_to_cpu(bss->capabilities);
5182 if (capabilities & (WLAN_CAPABILITY_ESS | WLAN_CAPABILITY_IBSS)) {
5183 iwe.cmd = SIOCGIWMODE;
5184 if (capabilities & WLAN_CAPABILITY_ESS)
5185 iwe.u.mode = IW_MODE_MASTER;
5186 else
5187 iwe.u.mode = IW_MODE_ADHOC;
5188 current_ev = iwe_stream_add_event(info, current_ev, end_buf,
5189 &iwe, IW_EV_UINT_LEN);
5190 }
5191
Johannes Berg2c7060022008-10-30 22:09:54 +01005192 ie = orinoco_get_ie(bss->data, sizeof(bss->data), WLAN_EID_DS_PARAMS);
David Kilroy01632fa2008-08-21 23:27:58 +01005193 channel = ie ? ie[2] : 0;
5194 if ((channel >= 1) && (channel <= NUM_CHANNELS)) {
5195 /* Add channel and frequency */
5196 iwe.cmd = SIOCGIWFREQ;
5197 iwe.u.freq.m = channel;
5198 iwe.u.freq.e = 0;
5199 current_ev = iwe_stream_add_event(info, current_ev, end_buf,
5200 &iwe, IW_EV_FREQ_LEN);
5201
David Kilroy9ee677c2008-12-23 14:03:38 +00005202 iwe.u.freq.m = ieee80211_dsss_chan_to_freq(channel) * 100000;
David Kilroy01632fa2008-08-21 23:27:58 +01005203 iwe.u.freq.e = 1;
5204 current_ev = iwe_stream_add_event(info, current_ev, end_buf,
5205 &iwe, IW_EV_FREQ_LEN);
5206 }
5207
5208 /* Add quality statistics. level and noise in dB. No link quality */
5209 iwe.cmd = IWEVQUAL;
5210 iwe.u.qual.updated = IW_QUAL_DBM | IW_QUAL_QUAL_INVALID;
5211 iwe.u.qual.level = bss->level - 0x95;
5212 iwe.u.qual.noise = bss->noise - 0x95;
5213 /* Wireless tools prior to 27.pre22 will show link quality
5214 * anyway, so we provide a reasonable value. */
5215 if (iwe.u.qual.level > iwe.u.qual.noise)
5216 iwe.u.qual.qual = iwe.u.qual.level - iwe.u.qual.noise;
5217 else
5218 iwe.u.qual.qual = 0;
5219 current_ev = iwe_stream_add_event(info, current_ev, end_buf,
5220 &iwe, IW_EV_QUAL_LEN);
5221
5222 /* Add encryption capability */
5223 iwe.cmd = SIOCGIWENCODE;
5224 if (capabilities & WLAN_CAPABILITY_PRIVACY)
5225 iwe.u.data.flags = IW_ENCODE_ENABLED | IW_ENCODE_NOKEY;
5226 else
5227 iwe.u.data.flags = IW_ENCODE_DISABLED;
5228 iwe.u.data.length = 0;
5229 current_ev = iwe_stream_add_point(info, current_ev, end_buf,
5230 &iwe, NULL);
5231
5232 /* WPA IE */
5233 ie = orinoco_get_wpa_ie(bss->data, sizeof(bss->data));
5234 if (ie) {
5235 iwe.cmd = IWEVGENIE;
5236 iwe.u.data.length = ie[1] + 2;
5237 current_ev = iwe_stream_add_point(info, current_ev, end_buf,
5238 &iwe, ie);
5239 }
5240
5241 /* RSN IE */
Johannes Berg2c7060022008-10-30 22:09:54 +01005242 ie = orinoco_get_ie(bss->data, sizeof(bss->data), WLAN_EID_RSN);
David Kilroy01632fa2008-08-21 23:27:58 +01005243 if (ie) {
5244 iwe.cmd = IWEVGENIE;
5245 iwe.u.data.length = ie[1] + 2;
5246 current_ev = iwe_stream_add_point(info, current_ev, end_buf,
5247 &iwe, ie);
5248 }
5249
Johannes Berg2c7060022008-10-30 22:09:54 +01005250 ie = orinoco_get_ie(bss->data, sizeof(bss->data), WLAN_EID_SUPP_RATES);
David Kilroy01632fa2008-08-21 23:27:58 +01005251 if (ie) {
5252 char *p = current_ev + iwe_stream_lcp_len(info);
5253 int i;
5254
5255 iwe.cmd = SIOCGIWRATE;
5256 /* Those two flags are ignored... */
5257 iwe.u.bitrate.fixed = iwe.u.bitrate.disabled = 0;
5258
5259 for (i = 2; i < (ie[1] + 2); i++) {
5260 iwe.u.bitrate.value = ((ie[i] & 0x7F) * 500000);
5261 p = iwe_stream_add_value(info, current_ev, p, end_buf,
5262 &iwe, IW_EV_PARAM_LEN);
5263 }
5264 /* Check if we added any event */
5265 if (p > (current_ev + iwe_stream_lcp_len(info)))
5266 current_ev = p;
5267 }
5268
5269 /* Timestamp */
5270 iwe.cmd = IWEVCUSTOM;
David Kilroy75d31cf2008-09-12 22:28:18 +01005271 iwe.u.data.length =
5272 snprintf(custom, MAX_CUSTOM_LEN, "tsf=%016llx",
5273 (unsigned long long) le64_to_cpu(bss->timestamp));
David Kilroy01632fa2008-08-21 23:27:58 +01005274 if (iwe.u.data.length)
5275 current_ev = iwe_stream_add_point(info, current_ev, end_buf,
5276 &iwe, custom);
5277
5278 /* Beacon interval */
5279 iwe.cmd = IWEVCUSTOM;
5280 iwe.u.data.length = snprintf(custom, MAX_CUSTOM_LEN,
5281 "bcn_int=%d",
5282 le16_to_cpu(bss->beacon_interval));
5283 if (iwe.u.data.length)
5284 current_ev = iwe_stream_add_point(info, current_ev, end_buf,
5285 &iwe, custom);
5286
5287 /* Capabilites */
5288 iwe.cmd = IWEVCUSTOM;
5289 iwe.u.data.length = snprintf(custom, MAX_CUSTOM_LEN,
5290 "capab=0x%04x",
5291 capabilities);
5292 if (iwe.u.data.length)
5293 current_ev = iwe_stream_add_point(info, current_ev, end_buf,
5294 &iwe, custom);
5295
5296 /* Add EXTRA: Age to display seconds since last beacon/probe response
5297 * for given network. */
5298 iwe.cmd = IWEVCUSTOM;
5299 iwe.u.data.length = snprintf(custom, MAX_CUSTOM_LEN,
5300 " Last beacon: %dms ago",
5301 jiffies_to_msecs(jiffies - last_scanned));
5302 if (iwe.u.data.length)
5303 current_ev = iwe_stream_add_point(info, current_ev, end_buf,
5304 &iwe, custom);
5305
5306 return current_ev;
5307}
5308
Christoph Hellwig95dd91f2005-06-19 01:27:56 +02005309/* Return results of a scan */
5310static int orinoco_ioctl_getscan(struct net_device *dev,
5311 struct iw_request_info *info,
5312 struct iw_point *srq,
5313 char *extra)
5314{
5315 struct orinoco_private *priv = netdev_priv(dev);
5316 int err = 0;
5317 unsigned long flags;
Dan Williams1e3428e2007-10-10 23:56:25 -04005318 char *current_ev = extra;
Christoph Hellwig95dd91f2005-06-19 01:27:56 +02005319
5320 if (orinoco_lock(priv, &flags) != 0)
5321 return -EBUSY;
5322
Dan Williams1e3428e2007-10-10 23:56:25 -04005323 if (priv->scan_inprogress) {
5324 /* Important note : we don't want to block the caller
5325 * until results are ready for various reasons.
5326 * First, managing wait queues is complex and racy.
5327 * Second, we grab some rtnetlink lock before comming
5328 * here (in dev_ioctl()).
5329 * Third, we generate an Wireless Event, so the
5330 * caller can wait itself on that - Jean II */
5331 err = -EAGAIN;
5332 goto out;
Christoph Hellwig95dd91f2005-06-19 01:27:56 +02005333 }
Dan Williams1e3428e2007-10-10 23:56:25 -04005334
David Kilroy01632fa2008-08-21 23:27:58 +01005335 if (priv->has_ext_scan) {
5336 struct xbss_element *bss;
Dan Williams1e3428e2007-10-10 23:56:25 -04005337
David Kilroy01632fa2008-08-21 23:27:58 +01005338 list_for_each_entry(bss, &priv->bss_list, list) {
5339 /* Translate this entry to WE format */
5340 current_ev =
5341 orinoco_translate_ext_scan(dev, info,
5342 current_ev,
5343 extra + srq->length,
5344 &bss->bss,
5345 bss->last_scanned);
5346
5347 /* Check if there is space for one more entry */
5348 if ((extra + srq->length - current_ev)
5349 <= IW_EV_ADDR_LEN) {
5350 /* Ask user space to try again with a
5351 * bigger buffer */
5352 err = -E2BIG;
5353 goto out;
5354 }
5355 }
5356
5357 } else {
5358 struct bss_element *bss;
5359
5360 list_for_each_entry(bss, &priv->bss_list, list) {
5361 /* Translate this entry to WE format */
5362 current_ev = orinoco_translate_scan(dev, info,
5363 current_ev,
5364 extra + srq->length,
5365 &bss->bss,
5366 bss->last_scanned);
5367
5368 /* Check if there is space for one more entry */
5369 if ((extra + srq->length - current_ev)
5370 <= IW_EV_ADDR_LEN) {
5371 /* Ask user space to try again with a
5372 * bigger buffer */
5373 err = -E2BIG;
5374 goto out;
5375 }
Dan Williams1e3428e2007-10-10 23:56:25 -04005376 }
5377 }
5378
5379 srq->length = (current_ev - extra);
5380 srq->flags = (__u16) priv->scan_mode;
5381
5382out:
Christoph Hellwig95dd91f2005-06-19 01:27:56 +02005383 orinoco_unlock(priv, &flags);
5384 return err;
5385}
5386
Christoph Hellwig620554e2005-06-19 01:27:33 +02005387/* Commit handler, called after set operations */
5388static int orinoco_ioctl_commit(struct net_device *dev,
5389 struct iw_request_info *info,
5390 void *wrqu,
5391 char *extra)
Linus Torvalds1da177e2005-04-16 15:20:36 -07005392{
5393 struct orinoco_private *priv = netdev_priv(dev);
Christoph Hellwig620554e2005-06-19 01:27:33 +02005394 struct hermes *hw = &priv->hw;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005395 unsigned long flags;
Christoph Hellwig620554e2005-06-19 01:27:33 +02005396 int err = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005397
Christoph Hellwig620554e2005-06-19 01:27:33 +02005398 if (!priv->open)
5399 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005400
Christoph Hellwig620554e2005-06-19 01:27:33 +02005401 if (priv->broken_disableport) {
David Howellsc4028952006-11-22 14:57:56 +00005402 orinoco_reset(&priv->reset_work);
Christoph Hellwig620554e2005-06-19 01:27:33 +02005403 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005404 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07005405
Christoph Hellwig620554e2005-06-19 01:27:33 +02005406 if (orinoco_lock(priv, &flags) != 0)
5407 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005408
Christoph Hellwig620554e2005-06-19 01:27:33 +02005409 err = hermes_disable_port(hw, 0);
5410 if (err) {
5411 printk(KERN_WARNING "%s: Unable to disable port "
5412 "while reconfiguring card\n", dev->name);
5413 priv->broken_disableport = 1;
5414 goto out;
5415 }
5416
5417 err = __orinoco_program_rids(dev);
5418 if (err) {
5419 printk(KERN_WARNING "%s: Unable to reconfigure card\n",
5420 dev->name);
5421 goto out;
5422 }
5423
5424 err = hermes_enable_port(hw, 0);
5425 if (err) {
5426 printk(KERN_WARNING "%s: Unable to enable port while reconfiguring card\n",
5427 dev->name);
5428 goto out;
5429 }
5430
5431 out:
5432 if (err) {
5433 printk(KERN_WARNING "%s: Resetting instead...\n", dev->name);
5434 schedule_work(&priv->reset_work);
5435 err = 0;
5436 }
5437
5438 orinoco_unlock(priv, &flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005439 return err;
5440}
5441
Christoph Hellwig620554e2005-06-19 01:27:33 +02005442static const struct iw_priv_args orinoco_privtab[] = {
5443 { SIOCIWFIRSTPRIV + 0x0, 0, 0, "force_reset" },
5444 { SIOCIWFIRSTPRIV + 0x1, 0, 0, "card_reset" },
5445 { SIOCIWFIRSTPRIV + 0x2, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
5446 0, "set_port3" },
5447 { SIOCIWFIRSTPRIV + 0x3, 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
5448 "get_port3" },
5449 { SIOCIWFIRSTPRIV + 0x4, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
5450 0, "set_preamble" },
5451 { SIOCIWFIRSTPRIV + 0x5, 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
5452 "get_preamble" },
5453 { SIOCIWFIRSTPRIV + 0x6, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
5454 0, "set_ibssport" },
5455 { SIOCIWFIRSTPRIV + 0x7, 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
5456 "get_ibssport" },
5457 { SIOCIWFIRSTPRIV + 0x9, 0, IW_PRIV_TYPE_BYTE | MAX_RID_LEN,
5458 "get_rid" },
5459};
5460
5461
5462/*
5463 * Structures to export the Wireless Handlers
5464 */
5465
David Kilroy409644a2008-08-21 23:28:01 +01005466#define STD_IW_HANDLER(id, func) \
5467 [IW_IOCTL_IDX(id)] = (iw_handler) func
Christoph Hellwig620554e2005-06-19 01:27:33 +02005468static const iw_handler orinoco_handler[] = {
David Kilroy409644a2008-08-21 23:28:01 +01005469 STD_IW_HANDLER(SIOCSIWCOMMIT, orinoco_ioctl_commit),
5470 STD_IW_HANDLER(SIOCGIWNAME, orinoco_ioctl_getname),
5471 STD_IW_HANDLER(SIOCSIWFREQ, orinoco_ioctl_setfreq),
5472 STD_IW_HANDLER(SIOCGIWFREQ, orinoco_ioctl_getfreq),
5473 STD_IW_HANDLER(SIOCSIWMODE, orinoco_ioctl_setmode),
5474 STD_IW_HANDLER(SIOCGIWMODE, orinoco_ioctl_getmode),
5475 STD_IW_HANDLER(SIOCSIWSENS, orinoco_ioctl_setsens),
5476 STD_IW_HANDLER(SIOCGIWSENS, orinoco_ioctl_getsens),
5477 STD_IW_HANDLER(SIOCGIWRANGE, orinoco_ioctl_getiwrange),
5478 STD_IW_HANDLER(SIOCSIWSPY, iw_handler_set_spy),
5479 STD_IW_HANDLER(SIOCGIWSPY, iw_handler_get_spy),
5480 STD_IW_HANDLER(SIOCSIWTHRSPY, iw_handler_set_thrspy),
5481 STD_IW_HANDLER(SIOCGIWTHRSPY, iw_handler_get_thrspy),
5482 STD_IW_HANDLER(SIOCSIWAP, orinoco_ioctl_setwap),
5483 STD_IW_HANDLER(SIOCGIWAP, orinoco_ioctl_getwap),
5484 STD_IW_HANDLER(SIOCSIWSCAN, orinoco_ioctl_setscan),
5485 STD_IW_HANDLER(SIOCGIWSCAN, orinoco_ioctl_getscan),
5486 STD_IW_HANDLER(SIOCSIWESSID, orinoco_ioctl_setessid),
5487 STD_IW_HANDLER(SIOCGIWESSID, orinoco_ioctl_getessid),
5488 STD_IW_HANDLER(SIOCSIWNICKN, orinoco_ioctl_setnick),
5489 STD_IW_HANDLER(SIOCGIWNICKN, orinoco_ioctl_getnick),
5490 STD_IW_HANDLER(SIOCSIWRATE, orinoco_ioctl_setrate),
5491 STD_IW_HANDLER(SIOCGIWRATE, orinoco_ioctl_getrate),
5492 STD_IW_HANDLER(SIOCSIWRTS, orinoco_ioctl_setrts),
5493 STD_IW_HANDLER(SIOCGIWRTS, orinoco_ioctl_getrts),
5494 STD_IW_HANDLER(SIOCSIWFRAG, orinoco_ioctl_setfrag),
5495 STD_IW_HANDLER(SIOCGIWFRAG, orinoco_ioctl_getfrag),
5496 STD_IW_HANDLER(SIOCGIWRETRY, orinoco_ioctl_getretry),
5497 STD_IW_HANDLER(SIOCSIWENCODE, orinoco_ioctl_setiwencode),
5498 STD_IW_HANDLER(SIOCGIWENCODE, orinoco_ioctl_getiwencode),
5499 STD_IW_HANDLER(SIOCSIWPOWER, orinoco_ioctl_setpower),
5500 STD_IW_HANDLER(SIOCGIWPOWER, orinoco_ioctl_getpower),
David Kilroyd03032a2008-08-21 23:28:02 +01005501 STD_IW_HANDLER(SIOCSIWGENIE, orinoco_ioctl_set_genie),
5502 STD_IW_HANDLER(SIOCGIWGENIE, orinoco_ioctl_get_genie),
5503 STD_IW_HANDLER(SIOCSIWMLME, orinoco_ioctl_set_mlme),
5504 STD_IW_HANDLER(SIOCSIWAUTH, orinoco_ioctl_set_auth),
5505 STD_IW_HANDLER(SIOCGIWAUTH, orinoco_ioctl_get_auth),
5506 STD_IW_HANDLER(SIOCSIWENCODEEXT, orinoco_ioctl_set_encodeext),
5507 STD_IW_HANDLER(SIOCGIWENCODEEXT, orinoco_ioctl_get_encodeext),
Christoph Hellwig620554e2005-06-19 01:27:33 +02005508};
5509
5510
5511/*
5512 Added typecasting since we no longer use iwreq_data -- Moustafa
5513 */
5514static const iw_handler orinoco_private_handler[] = {
Peter Hagervall6b9b97c2005-07-27 01:14:46 -07005515 [0] = (iw_handler) orinoco_ioctl_reset,
5516 [1] = (iw_handler) orinoco_ioctl_reset,
5517 [2] = (iw_handler) orinoco_ioctl_setport3,
5518 [3] = (iw_handler) orinoco_ioctl_getport3,
5519 [4] = (iw_handler) orinoco_ioctl_setpreamble,
5520 [5] = (iw_handler) orinoco_ioctl_getpreamble,
5521 [6] = (iw_handler) orinoco_ioctl_setibssport,
5522 [7] = (iw_handler) orinoco_ioctl_getibssport,
5523 [9] = (iw_handler) orinoco_ioctl_getrid,
Christoph Hellwig620554e2005-06-19 01:27:33 +02005524};
5525
5526static const struct iw_handler_def orinoco_handler_def = {
5527 .num_standard = ARRAY_SIZE(orinoco_handler),
5528 .num_private = ARRAY_SIZE(orinoco_private_handler),
5529 .num_private_args = ARRAY_SIZE(orinoco_privtab),
5530 .standard = orinoco_handler,
5531 .private = orinoco_private_handler,
5532 .private_args = orinoco_privtab,
Pavel Roskin343c6862005-09-09 18:43:02 -04005533 .get_wireless_stats = orinoco_get_wireless_stats,
Christoph Hellwig620554e2005-06-19 01:27:33 +02005534};
Linus Torvalds1da177e2005-04-16 15:20:36 -07005535
Christoph Hellwig1fab2e82005-06-19 01:27:40 +02005536static void orinoco_get_drvinfo(struct net_device *dev,
5537 struct ethtool_drvinfo *info)
5538{
5539 struct orinoco_private *priv = netdev_priv(dev);
5540
5541 strncpy(info->driver, DRIVER_NAME, sizeof(info->driver) - 1);
5542 strncpy(info->version, DRIVER_VERSION, sizeof(info->version) - 1);
5543 strncpy(info->fw_version, priv->fw_name, sizeof(info->fw_version) - 1);
Greg Kroah-Hartman43cb76d2002-04-09 12:14:34 -07005544 if (dev->dev.parent)
Kay Sieversfb28ad32008-11-10 13:55:14 -08005545 strncpy(info->bus_info, dev_name(dev->dev.parent),
Christoph Hellwig1fab2e82005-06-19 01:27:40 +02005546 sizeof(info->bus_info) - 1);
5547 else
5548 snprintf(info->bus_info, sizeof(info->bus_info) - 1,
5549 "PCMCIA %p", priv->hw.iobase);
5550}
5551
Jeff Garzik7282d492006-09-13 14:30:00 -04005552static const struct ethtool_ops orinoco_ethtool_ops = {
Christoph Hellwig1fab2e82005-06-19 01:27:40 +02005553 .get_drvinfo = orinoco_get_drvinfo,
5554 .get_link = ethtool_op_get_link,
5555};
Linus Torvalds1da177e2005-04-16 15:20:36 -07005556
5557/********************************************************************/
Linus Torvalds1da177e2005-04-16 15:20:36 -07005558/* Module initialization */
5559/********************************************************************/
5560
Linus Torvalds1da177e2005-04-16 15:20:36 -07005561/* Can't be declared "const" or the whole __initdata section will
5562 * become const */
5563static char version[] __initdata = DRIVER_NAME " " DRIVER_VERSION
5564 " (David Gibson <hermes@gibson.dropbear.id.au>, "
5565 "Pavel Roskin <proski@gnu.org>, et al)";
5566
5567static int __init init_orinoco(void)
5568{
5569 printk(KERN_DEBUG "%s\n", version);
5570 return 0;
5571}
5572
5573static void __exit exit_orinoco(void)
5574{
5575}
5576
5577module_init(init_orinoco);
5578module_exit(exit_orinoco);