162306a36Sopenharmony_ci/* main.c - (formerly known as dldwd_cs.c, orinoco_cs.c and orinoco.c)
262306a36Sopenharmony_ci *
362306a36Sopenharmony_ci * A driver for Hermes or Prism 2 chipset based PCMCIA wireless
462306a36Sopenharmony_ci * adaptors, with Lucent/Agere, Intersil or Symbol firmware.
562306a36Sopenharmony_ci *
662306a36Sopenharmony_ci * Current maintainers (as of 29 September 2003) are:
762306a36Sopenharmony_ci *	Pavel Roskin <proski AT gnu.org>
862306a36Sopenharmony_ci * and	David Gibson <hermes AT gibson.dropbear.id.au>
962306a36Sopenharmony_ci *
1062306a36Sopenharmony_ci * (C) Copyright David Gibson, IBM Corporation 2001-2003.
1162306a36Sopenharmony_ci * Copyright (C) 2000 David Gibson, Linuxcare Australia.
1262306a36Sopenharmony_ci *	With some help from :
1362306a36Sopenharmony_ci * Copyright (C) 2001 Jean Tourrilhes, HP Labs
1462306a36Sopenharmony_ci * Copyright (C) 2001 Benjamin Herrenschmidt
1562306a36Sopenharmony_ci *
1662306a36Sopenharmony_ci * Based on dummy_cs.c 1.27 2000/06/12 21:27:25
1762306a36Sopenharmony_ci *
1862306a36Sopenharmony_ci * Portions based on wvlan_cs.c 1.0.6, Copyright Andreas Neuhaus <andy
1962306a36Sopenharmony_ci * AT fasta.fh-dortmund.de>
2062306a36Sopenharmony_ci *      http://www.stud.fh-dortmund.de/~andy/wvlan/
2162306a36Sopenharmony_ci *
2262306a36Sopenharmony_ci * The contents of this file are subject to the Mozilla Public License
2362306a36Sopenharmony_ci * Version 1.1 (the "License"); you may not use this file except in
2462306a36Sopenharmony_ci * compliance with the License. You may obtain a copy of the License
2562306a36Sopenharmony_ci * at http://www.mozilla.org/MPL/
2662306a36Sopenharmony_ci *
2762306a36Sopenharmony_ci * Software distributed under the License is distributed on an "AS IS"
2862306a36Sopenharmony_ci * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
2962306a36Sopenharmony_ci * the License for the specific language governing rights and
3062306a36Sopenharmony_ci * limitations under the License.
3162306a36Sopenharmony_ci *
3262306a36Sopenharmony_ci * The initial developer of the original code is David A. Hinds
3362306a36Sopenharmony_ci * <dahinds AT users.sourceforge.net>.  Portions created by David
3462306a36Sopenharmony_ci * A. Hinds are Copyright (C) 1999 David A. Hinds.  All Rights
3562306a36Sopenharmony_ci * Reserved.
3662306a36Sopenharmony_ci *
3762306a36Sopenharmony_ci * Alternatively, the contents of this file may be used under the
3862306a36Sopenharmony_ci * terms of the GNU General Public License version 2 (the "GPL"), in
3962306a36Sopenharmony_ci * which case the provisions of the GPL are applicable instead of the
4062306a36Sopenharmony_ci * above.  If you wish to allow the use of your version of this file
4162306a36Sopenharmony_ci * only under the terms of the GPL and not to allow others to use your
4262306a36Sopenharmony_ci * version of this file under the MPL, indicate your decision by
4362306a36Sopenharmony_ci * deleting the provisions above and replace them with the notice and
4462306a36Sopenharmony_ci * other provisions required by the GPL.  If you do not delete the
4562306a36Sopenharmony_ci * provisions above, a recipient may use your version of this file
4662306a36Sopenharmony_ci * under either the MPL or the GPL.  */
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_ci/*
4962306a36Sopenharmony_ci * TODO
5062306a36Sopenharmony_ci *	o Handle de-encapsulation within network layer, provide 802.11
5162306a36Sopenharmony_ci *	  headers (patch from Thomas 'Dent' Mirlacher)
5262306a36Sopenharmony_ci *	o Fix possible races in SPY handling.
5362306a36Sopenharmony_ci *	o Disconnect wireless extensions from fundamental configuration.
5462306a36Sopenharmony_ci *	o (maybe) Software WEP support (patch from Stano Meduna).
5562306a36Sopenharmony_ci *	o (maybe) Use multiple Tx buffers - driver handling queue
5662306a36Sopenharmony_ci *	  rather than firmware.
5762306a36Sopenharmony_ci */
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_ci/* Locking and synchronization:
6062306a36Sopenharmony_ci *
6162306a36Sopenharmony_ci * The basic principle is that everything is serialized through a
6262306a36Sopenharmony_ci * single spinlock, priv->lock.  The lock is used in user, bh and irq
6362306a36Sopenharmony_ci * context, so when taken outside hardirq context it should always be
6462306a36Sopenharmony_ci * taken with interrupts disabled.  The lock protects both the
6562306a36Sopenharmony_ci * hardware and the struct orinoco_private.
6662306a36Sopenharmony_ci *
6762306a36Sopenharmony_ci * Another flag, priv->hw_unavailable indicates that the hardware is
6862306a36Sopenharmony_ci * unavailable for an extended period of time (e.g. suspended, or in
6962306a36Sopenharmony_ci * the middle of a hard reset).  This flag is protected by the
7062306a36Sopenharmony_ci * spinlock.  All code which touches the hardware should check the
7162306a36Sopenharmony_ci * flag after taking the lock, and if it is set, give up on whatever
7262306a36Sopenharmony_ci * they are doing and drop the lock again.  The orinoco_lock()
7362306a36Sopenharmony_ci * function handles this (it unlocks and returns -EBUSY if
7462306a36Sopenharmony_ci * hw_unavailable is non-zero).
7562306a36Sopenharmony_ci */
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_ci#define DRIVER_NAME "orinoco"
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_ci#include <linux/module.h>
8062306a36Sopenharmony_ci#include <linux/kernel.h>
8162306a36Sopenharmony_ci#include <linux/slab.h>
8262306a36Sopenharmony_ci#include <linux/init.h>
8362306a36Sopenharmony_ci#include <linux/delay.h>
8462306a36Sopenharmony_ci#include <linux/device.h>
8562306a36Sopenharmony_ci#include <linux/netdevice.h>
8662306a36Sopenharmony_ci#include <linux/etherdevice.h>
8762306a36Sopenharmony_ci#include <linux/suspend.h>
8862306a36Sopenharmony_ci#include <linux/if_arp.h>
8962306a36Sopenharmony_ci#include <linux/wireless.h>
9062306a36Sopenharmony_ci#include <linux/ieee80211.h>
9162306a36Sopenharmony_ci#include <net/iw_handler.h>
9262306a36Sopenharmony_ci#include <net/cfg80211.h>
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_ci#include "hermes_rid.h"
9562306a36Sopenharmony_ci#include "hermes_dld.h"
9662306a36Sopenharmony_ci#include "hw.h"
9762306a36Sopenharmony_ci#include "scan.h"
9862306a36Sopenharmony_ci#include "mic.h"
9962306a36Sopenharmony_ci#include "fw.h"
10062306a36Sopenharmony_ci#include "wext.h"
10162306a36Sopenharmony_ci#include "cfg.h"
10262306a36Sopenharmony_ci#include "main.h"
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_ci#include "orinoco.h"
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ci/********************************************************************/
10762306a36Sopenharmony_ci/* Module information                                               */
10862306a36Sopenharmony_ci/********************************************************************/
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_ciMODULE_AUTHOR("Pavel Roskin <proski@gnu.org> & "
11162306a36Sopenharmony_ci	      "David Gibson <hermes@gibson.dropbear.id.au>");
11262306a36Sopenharmony_ciMODULE_DESCRIPTION("Driver for Lucent Orinoco, Prism II based "
11362306a36Sopenharmony_ci		   "and similar wireless cards");
11462306a36Sopenharmony_ciMODULE_LICENSE("Dual MPL/GPL");
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_ci/* Level of debugging. Used in the macros in orinoco.h */
11762306a36Sopenharmony_ci#ifdef ORINOCO_DEBUG
11862306a36Sopenharmony_ciint orinoco_debug = ORINOCO_DEBUG;
11962306a36Sopenharmony_ciEXPORT_SYMBOL(orinoco_debug);
12062306a36Sopenharmony_cimodule_param(orinoco_debug, int, 0644);
12162306a36Sopenharmony_ciMODULE_PARM_DESC(orinoco_debug, "Debug level");
12262306a36Sopenharmony_ci#endif
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_cistatic bool suppress_linkstatus; /* = 0 */
12562306a36Sopenharmony_cimodule_param(suppress_linkstatus, bool, 0644);
12662306a36Sopenharmony_ciMODULE_PARM_DESC(suppress_linkstatus, "Don't log link status changes");
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_cistatic int ignore_disconnect; /* = 0 */
12962306a36Sopenharmony_cimodule_param(ignore_disconnect, int, 0644);
13062306a36Sopenharmony_ciMODULE_PARM_DESC(ignore_disconnect,
13162306a36Sopenharmony_ci		 "Don't report lost link to the network layer");
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_ciint force_monitor; /* = 0 */
13462306a36Sopenharmony_cimodule_param(force_monitor, int, 0644);
13562306a36Sopenharmony_ciMODULE_PARM_DESC(force_monitor, "Allow monitor mode for all firmware versions");
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_ci/********************************************************************/
13862306a36Sopenharmony_ci/* Internal constants                                               */
13962306a36Sopenharmony_ci/********************************************************************/
14062306a36Sopenharmony_ci
14162306a36Sopenharmony_ci/* 802.2 LLC/SNAP header used for Ethernet encapsulation over 802.11 */
14262306a36Sopenharmony_cistatic const u8 encaps_hdr[] = {0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00};
14362306a36Sopenharmony_ci#define ENCAPS_OVERHEAD		(sizeof(encaps_hdr) + 2)
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_ci#define ORINOCO_MIN_MTU		256
14662306a36Sopenharmony_ci#define ORINOCO_MAX_MTU		(IEEE80211_MAX_DATA_LEN - ENCAPS_OVERHEAD)
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_ci#define MAX_IRQLOOPS_PER_IRQ	10
14962306a36Sopenharmony_ci#define MAX_IRQLOOPS_PER_JIFFY	(20000 / HZ)	/* Based on a guestimate of
15062306a36Sopenharmony_ci						 * how many events the
15162306a36Sopenharmony_ci						 * device could
15262306a36Sopenharmony_ci						 * legitimately generate */
15362306a36Sopenharmony_ci
15462306a36Sopenharmony_ci#define DUMMY_FID		0xFFFF
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_ci/*#define MAX_MULTICAST(priv)	(priv->firmware_type == FIRMWARE_TYPE_AGERE ? \
15762306a36Sopenharmony_ci  HERMES_MAX_MULTICAST : 0)*/
15862306a36Sopenharmony_ci#define MAX_MULTICAST(priv)	(HERMES_MAX_MULTICAST)
15962306a36Sopenharmony_ci
16062306a36Sopenharmony_ci#define ORINOCO_INTEN		(HERMES_EV_RX | HERMES_EV_ALLOC \
16162306a36Sopenharmony_ci				 | HERMES_EV_TX | HERMES_EV_TXEXC \
16262306a36Sopenharmony_ci				 | HERMES_EV_WTERR | HERMES_EV_INFO \
16362306a36Sopenharmony_ci				 | HERMES_EV_INFDROP)
16462306a36Sopenharmony_ci
16562306a36Sopenharmony_ci/********************************************************************/
16662306a36Sopenharmony_ci/* Data types                                                       */
16762306a36Sopenharmony_ci/********************************************************************/
16862306a36Sopenharmony_ci
16962306a36Sopenharmony_ci/* Beginning of the Tx descriptor, used in TxExc handling */
17062306a36Sopenharmony_cistruct hermes_txexc_data {
17162306a36Sopenharmony_ci	struct hermes_tx_descriptor desc;
17262306a36Sopenharmony_ci	__le16 frame_ctl;
17362306a36Sopenharmony_ci	__le16 duration_id;
17462306a36Sopenharmony_ci	u8 addr1[ETH_ALEN];
17562306a36Sopenharmony_ci} __packed;
17662306a36Sopenharmony_ci
17762306a36Sopenharmony_ci/* Rx frame header except compatibility 802.3 header */
17862306a36Sopenharmony_cistruct hermes_rx_descriptor {
17962306a36Sopenharmony_ci	/* Control */
18062306a36Sopenharmony_ci	__le16 status;
18162306a36Sopenharmony_ci	__le32 time;
18262306a36Sopenharmony_ci	u8 silence;
18362306a36Sopenharmony_ci	u8 signal;
18462306a36Sopenharmony_ci	u8 rate;
18562306a36Sopenharmony_ci	u8 rxflow;
18662306a36Sopenharmony_ci	__le32 reserved;
18762306a36Sopenharmony_ci
18862306a36Sopenharmony_ci	/* 802.11 header */
18962306a36Sopenharmony_ci	__le16 frame_ctl;
19062306a36Sopenharmony_ci	__le16 duration_id;
19162306a36Sopenharmony_ci	u8 addr1[ETH_ALEN];
19262306a36Sopenharmony_ci	u8 addr2[ETH_ALEN];
19362306a36Sopenharmony_ci	u8 addr3[ETH_ALEN];
19462306a36Sopenharmony_ci	__le16 seq_ctl;
19562306a36Sopenharmony_ci	u8 addr4[ETH_ALEN];
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_ci	/* Data length */
19862306a36Sopenharmony_ci	__le16 data_len;
19962306a36Sopenharmony_ci} __packed;
20062306a36Sopenharmony_ci
20162306a36Sopenharmony_cistruct orinoco_rx_data {
20262306a36Sopenharmony_ci	struct hermes_rx_descriptor *desc;
20362306a36Sopenharmony_ci	struct sk_buff *skb;
20462306a36Sopenharmony_ci	struct list_head list;
20562306a36Sopenharmony_ci};
20662306a36Sopenharmony_ci
20762306a36Sopenharmony_cistruct orinoco_scan_data {
20862306a36Sopenharmony_ci	void *buf;
20962306a36Sopenharmony_ci	size_t len;
21062306a36Sopenharmony_ci	int type;
21162306a36Sopenharmony_ci	struct list_head list;
21262306a36Sopenharmony_ci};
21362306a36Sopenharmony_ci
21462306a36Sopenharmony_ci/********************************************************************/
21562306a36Sopenharmony_ci/* Function prototypes                                              */
21662306a36Sopenharmony_ci/********************************************************************/
21762306a36Sopenharmony_ci
21862306a36Sopenharmony_cistatic int __orinoco_set_multicast_list(struct net_device *dev);
21962306a36Sopenharmony_cistatic int __orinoco_up(struct orinoco_private *priv);
22062306a36Sopenharmony_cistatic int __orinoco_down(struct orinoco_private *priv);
22162306a36Sopenharmony_cistatic int __orinoco_commit(struct orinoco_private *priv);
22262306a36Sopenharmony_ci
22362306a36Sopenharmony_ci/********************************************************************/
22462306a36Sopenharmony_ci/* Internal helper functions                                        */
22562306a36Sopenharmony_ci/********************************************************************/
22662306a36Sopenharmony_ci
22762306a36Sopenharmony_civoid set_port_type(struct orinoco_private *priv)
22862306a36Sopenharmony_ci{
22962306a36Sopenharmony_ci	switch (priv->iw_mode) {
23062306a36Sopenharmony_ci	case NL80211_IFTYPE_STATION:
23162306a36Sopenharmony_ci		priv->port_type = 1;
23262306a36Sopenharmony_ci		priv->createibss = 0;
23362306a36Sopenharmony_ci		break;
23462306a36Sopenharmony_ci	case NL80211_IFTYPE_ADHOC:
23562306a36Sopenharmony_ci		if (priv->prefer_port3) {
23662306a36Sopenharmony_ci			priv->port_type = 3;
23762306a36Sopenharmony_ci			priv->createibss = 0;
23862306a36Sopenharmony_ci		} else {
23962306a36Sopenharmony_ci			priv->port_type = priv->ibss_port;
24062306a36Sopenharmony_ci			priv->createibss = 1;
24162306a36Sopenharmony_ci		}
24262306a36Sopenharmony_ci		break;
24362306a36Sopenharmony_ci	case NL80211_IFTYPE_MONITOR:
24462306a36Sopenharmony_ci		priv->port_type = 3;
24562306a36Sopenharmony_ci		priv->createibss = 0;
24662306a36Sopenharmony_ci		break;
24762306a36Sopenharmony_ci	default:
24862306a36Sopenharmony_ci		printk(KERN_ERR "%s: Invalid priv->iw_mode in set_port_type()\n",
24962306a36Sopenharmony_ci		       priv->ndev->name);
25062306a36Sopenharmony_ci	}
25162306a36Sopenharmony_ci}
25262306a36Sopenharmony_ci
25362306a36Sopenharmony_ci/********************************************************************/
25462306a36Sopenharmony_ci/* Device methods                                                   */
25562306a36Sopenharmony_ci/********************************************************************/
25662306a36Sopenharmony_ci
25762306a36Sopenharmony_ciint orinoco_open(struct net_device *dev)
25862306a36Sopenharmony_ci{
25962306a36Sopenharmony_ci	struct orinoco_private *priv = ndev_priv(dev);
26062306a36Sopenharmony_ci	unsigned long flags;
26162306a36Sopenharmony_ci	int err;
26262306a36Sopenharmony_ci
26362306a36Sopenharmony_ci	if (orinoco_lock(priv, &flags) != 0)
26462306a36Sopenharmony_ci		return -EBUSY;
26562306a36Sopenharmony_ci
26662306a36Sopenharmony_ci	err = __orinoco_up(priv);
26762306a36Sopenharmony_ci
26862306a36Sopenharmony_ci	if (!err)
26962306a36Sopenharmony_ci		priv->open = 1;
27062306a36Sopenharmony_ci
27162306a36Sopenharmony_ci	orinoco_unlock(priv, &flags);
27262306a36Sopenharmony_ci
27362306a36Sopenharmony_ci	return err;
27462306a36Sopenharmony_ci}
27562306a36Sopenharmony_ciEXPORT_SYMBOL(orinoco_open);
27662306a36Sopenharmony_ci
27762306a36Sopenharmony_ciint orinoco_stop(struct net_device *dev)
27862306a36Sopenharmony_ci{
27962306a36Sopenharmony_ci	struct orinoco_private *priv = ndev_priv(dev);
28062306a36Sopenharmony_ci	int err = 0;
28162306a36Sopenharmony_ci
28262306a36Sopenharmony_ci	/* We mustn't use orinoco_lock() here, because we need to be
28362306a36Sopenharmony_ci	   able to close the interface even if hw_unavailable is set
28462306a36Sopenharmony_ci	   (e.g. as we're released after a PC Card removal) */
28562306a36Sopenharmony_ci	orinoco_lock_irq(priv);
28662306a36Sopenharmony_ci
28762306a36Sopenharmony_ci	priv->open = 0;
28862306a36Sopenharmony_ci
28962306a36Sopenharmony_ci	err = __orinoco_down(priv);
29062306a36Sopenharmony_ci
29162306a36Sopenharmony_ci	orinoco_unlock_irq(priv);
29262306a36Sopenharmony_ci
29362306a36Sopenharmony_ci	return err;
29462306a36Sopenharmony_ci}
29562306a36Sopenharmony_ciEXPORT_SYMBOL(orinoco_stop);
29662306a36Sopenharmony_ci
29762306a36Sopenharmony_civoid orinoco_set_multicast_list(struct net_device *dev)
29862306a36Sopenharmony_ci{
29962306a36Sopenharmony_ci	struct orinoco_private *priv = ndev_priv(dev);
30062306a36Sopenharmony_ci	unsigned long flags;
30162306a36Sopenharmony_ci
30262306a36Sopenharmony_ci	if (orinoco_lock(priv, &flags) != 0) {
30362306a36Sopenharmony_ci		printk(KERN_DEBUG "%s: orinoco_set_multicast_list() "
30462306a36Sopenharmony_ci		       "called when hw_unavailable\n", dev->name);
30562306a36Sopenharmony_ci		return;
30662306a36Sopenharmony_ci	}
30762306a36Sopenharmony_ci
30862306a36Sopenharmony_ci	__orinoco_set_multicast_list(dev);
30962306a36Sopenharmony_ci	orinoco_unlock(priv, &flags);
31062306a36Sopenharmony_ci}
31162306a36Sopenharmony_ciEXPORT_SYMBOL(orinoco_set_multicast_list);
31262306a36Sopenharmony_ci
31362306a36Sopenharmony_ciint orinoco_change_mtu(struct net_device *dev, int new_mtu)
31462306a36Sopenharmony_ci{
31562306a36Sopenharmony_ci	struct orinoco_private *priv = ndev_priv(dev);
31662306a36Sopenharmony_ci
31762306a36Sopenharmony_ci	/* MTU + encapsulation + header length */
31862306a36Sopenharmony_ci	if ((new_mtu + ENCAPS_OVERHEAD + sizeof(struct ieee80211_hdr)) >
31962306a36Sopenharmony_ci	     (priv->nicbuf_size - ETH_HLEN))
32062306a36Sopenharmony_ci		return -EINVAL;
32162306a36Sopenharmony_ci
32262306a36Sopenharmony_ci	dev->mtu = new_mtu;
32362306a36Sopenharmony_ci
32462306a36Sopenharmony_ci	return 0;
32562306a36Sopenharmony_ci}
32662306a36Sopenharmony_ciEXPORT_SYMBOL(orinoco_change_mtu);
32762306a36Sopenharmony_ci
32862306a36Sopenharmony_ci/********************************************************************/
32962306a36Sopenharmony_ci/* Tx path                                                          */
33062306a36Sopenharmony_ci/********************************************************************/
33162306a36Sopenharmony_ci
33262306a36Sopenharmony_ci/* Add encapsulation and MIC to the existing SKB.
33362306a36Sopenharmony_ci * The main xmit routine will then send the whole lot to the card.
33462306a36Sopenharmony_ci * Need 8 bytes headroom
33562306a36Sopenharmony_ci * Need 8 bytes tailroom
33662306a36Sopenharmony_ci *
33762306a36Sopenharmony_ci *                          With encapsulated ethernet II frame
33862306a36Sopenharmony_ci *                          --------
33962306a36Sopenharmony_ci *                          803.3 header (14 bytes)
34062306a36Sopenharmony_ci *                           dst[6]
34162306a36Sopenharmony_ci * --------                  src[6]
34262306a36Sopenharmony_ci * 803.3 header (14 bytes)   len[2]
34362306a36Sopenharmony_ci *  dst[6]                  803.2 header (8 bytes)
34462306a36Sopenharmony_ci *  src[6]                   encaps[6]
34562306a36Sopenharmony_ci *  len[2] <- leave alone -> len[2]
34662306a36Sopenharmony_ci * --------                 -------- <-- 0
34762306a36Sopenharmony_ci * Payload                  Payload
34862306a36Sopenharmony_ci * ...                      ...
34962306a36Sopenharmony_ci *
35062306a36Sopenharmony_ci * --------                 --------
35162306a36Sopenharmony_ci *                          MIC (8 bytes)
35262306a36Sopenharmony_ci *                          --------
35362306a36Sopenharmony_ci *
35462306a36Sopenharmony_ci * returns 0 on success, -ENOMEM on error.
35562306a36Sopenharmony_ci */
35662306a36Sopenharmony_ciint orinoco_process_xmit_skb(struct sk_buff *skb,
35762306a36Sopenharmony_ci			     struct net_device *dev,
35862306a36Sopenharmony_ci			     struct orinoco_private *priv,
35962306a36Sopenharmony_ci			     int *tx_control,
36062306a36Sopenharmony_ci			     u8 *mic_buf)
36162306a36Sopenharmony_ci{
36262306a36Sopenharmony_ci	struct orinoco_tkip_key *key;
36362306a36Sopenharmony_ci	struct ethhdr *eh;
36462306a36Sopenharmony_ci	int do_mic;
36562306a36Sopenharmony_ci
36662306a36Sopenharmony_ci	key = (struct orinoco_tkip_key *) priv->keys[priv->tx_key].key;
36762306a36Sopenharmony_ci
36862306a36Sopenharmony_ci	do_mic = ((priv->encode_alg == ORINOCO_ALG_TKIP) &&
36962306a36Sopenharmony_ci		  (key != NULL));
37062306a36Sopenharmony_ci
37162306a36Sopenharmony_ci	if (do_mic)
37262306a36Sopenharmony_ci		*tx_control |= (priv->tx_key << HERMES_MIC_KEY_ID_SHIFT) |
37362306a36Sopenharmony_ci			HERMES_TXCTRL_MIC;
37462306a36Sopenharmony_ci
37562306a36Sopenharmony_ci	eh = (struct ethhdr *)skb->data;
37662306a36Sopenharmony_ci
37762306a36Sopenharmony_ci	/* Encapsulate Ethernet-II frames */
37862306a36Sopenharmony_ci	if (ntohs(eh->h_proto) > ETH_DATA_LEN) { /* Ethernet-II frame */
37962306a36Sopenharmony_ci		struct header_struct {
38062306a36Sopenharmony_ci			struct ethhdr eth;	/* 802.3 header */
38162306a36Sopenharmony_ci			u8 encap[6];		/* 802.2 header */
38262306a36Sopenharmony_ci		} __packed hdr;
38362306a36Sopenharmony_ci		int len = skb->len + sizeof(encaps_hdr) - (2 * ETH_ALEN);
38462306a36Sopenharmony_ci
38562306a36Sopenharmony_ci		if (skb_headroom(skb) < ENCAPS_OVERHEAD) {
38662306a36Sopenharmony_ci			if (net_ratelimit())
38762306a36Sopenharmony_ci				printk(KERN_ERR
38862306a36Sopenharmony_ci				       "%s: Not enough headroom for 802.2 headers %d\n",
38962306a36Sopenharmony_ci				       dev->name, skb_headroom(skb));
39062306a36Sopenharmony_ci			return -ENOMEM;
39162306a36Sopenharmony_ci		}
39262306a36Sopenharmony_ci
39362306a36Sopenharmony_ci		/* Fill in new header */
39462306a36Sopenharmony_ci		memcpy(&hdr.eth, eh, 2 * ETH_ALEN);
39562306a36Sopenharmony_ci		hdr.eth.h_proto = htons(len);
39662306a36Sopenharmony_ci		memcpy(hdr.encap, encaps_hdr, sizeof(encaps_hdr));
39762306a36Sopenharmony_ci
39862306a36Sopenharmony_ci		/* Make room for the new header, and copy it in */
39962306a36Sopenharmony_ci		eh = skb_push(skb, ENCAPS_OVERHEAD);
40062306a36Sopenharmony_ci		memcpy(eh, &hdr, sizeof(hdr));
40162306a36Sopenharmony_ci	}
40262306a36Sopenharmony_ci
40362306a36Sopenharmony_ci	/* Calculate Michael MIC */
40462306a36Sopenharmony_ci	if (do_mic) {
40562306a36Sopenharmony_ci		size_t len = skb->len - ETH_HLEN;
40662306a36Sopenharmony_ci		u8 *mic = &mic_buf[0];
40762306a36Sopenharmony_ci
40862306a36Sopenharmony_ci		/* Have to write to an even address, so copy the spare
40962306a36Sopenharmony_ci		 * byte across */
41062306a36Sopenharmony_ci		if (skb->len % 2) {
41162306a36Sopenharmony_ci			*mic = skb->data[skb->len - 1];
41262306a36Sopenharmony_ci			mic++;
41362306a36Sopenharmony_ci		}
41462306a36Sopenharmony_ci
41562306a36Sopenharmony_ci		orinoco_mic(priv->tx_tfm_mic, key->tx_mic,
41662306a36Sopenharmony_ci			    eh->h_dest, eh->h_source, 0 /* priority */,
41762306a36Sopenharmony_ci			    skb->data + ETH_HLEN,
41862306a36Sopenharmony_ci			    len, mic);
41962306a36Sopenharmony_ci	}
42062306a36Sopenharmony_ci
42162306a36Sopenharmony_ci	return 0;
42262306a36Sopenharmony_ci}
42362306a36Sopenharmony_ciEXPORT_SYMBOL(orinoco_process_xmit_skb);
42462306a36Sopenharmony_ci
42562306a36Sopenharmony_cistatic netdev_tx_t orinoco_xmit(struct sk_buff *skb, struct net_device *dev)
42662306a36Sopenharmony_ci{
42762306a36Sopenharmony_ci	struct orinoco_private *priv = ndev_priv(dev);
42862306a36Sopenharmony_ci	struct net_device_stats *stats = &dev->stats;
42962306a36Sopenharmony_ci	struct hermes *hw = &priv->hw;
43062306a36Sopenharmony_ci	int err = 0;
43162306a36Sopenharmony_ci	u16 txfid = priv->txfid;
43262306a36Sopenharmony_ci	int tx_control;
43362306a36Sopenharmony_ci	unsigned long flags;
43462306a36Sopenharmony_ci	u8 mic_buf[MICHAEL_MIC_LEN + 1];
43562306a36Sopenharmony_ci
43662306a36Sopenharmony_ci	if (!netif_running(dev)) {
43762306a36Sopenharmony_ci		printk(KERN_ERR "%s: Tx on stopped device!\n",
43862306a36Sopenharmony_ci		       dev->name);
43962306a36Sopenharmony_ci		return NETDEV_TX_BUSY;
44062306a36Sopenharmony_ci	}
44162306a36Sopenharmony_ci
44262306a36Sopenharmony_ci	if (netif_queue_stopped(dev)) {
44362306a36Sopenharmony_ci		printk(KERN_DEBUG "%s: Tx while transmitter busy!\n",
44462306a36Sopenharmony_ci		       dev->name);
44562306a36Sopenharmony_ci		return NETDEV_TX_BUSY;
44662306a36Sopenharmony_ci	}
44762306a36Sopenharmony_ci
44862306a36Sopenharmony_ci	if (orinoco_lock(priv, &flags) != 0) {
44962306a36Sopenharmony_ci		printk(KERN_ERR "%s: orinoco_xmit() called while hw_unavailable\n",
45062306a36Sopenharmony_ci		       dev->name);
45162306a36Sopenharmony_ci		return NETDEV_TX_BUSY;
45262306a36Sopenharmony_ci	}
45362306a36Sopenharmony_ci
45462306a36Sopenharmony_ci	if (!netif_carrier_ok(dev) ||
45562306a36Sopenharmony_ci	    (priv->iw_mode == NL80211_IFTYPE_MONITOR)) {
45662306a36Sopenharmony_ci		/* Oops, the firmware hasn't established a connection,
45762306a36Sopenharmony_ci		   silently drop the packet (this seems to be the
45862306a36Sopenharmony_ci		   safest approach). */
45962306a36Sopenharmony_ci		goto drop;
46062306a36Sopenharmony_ci	}
46162306a36Sopenharmony_ci
46262306a36Sopenharmony_ci	/* Check packet length */
46362306a36Sopenharmony_ci	if (skb->len < ETH_HLEN)
46462306a36Sopenharmony_ci		goto drop;
46562306a36Sopenharmony_ci
46662306a36Sopenharmony_ci	tx_control = HERMES_TXCTRL_TX_OK | HERMES_TXCTRL_TX_EX;
46762306a36Sopenharmony_ci
46862306a36Sopenharmony_ci	err = orinoco_process_xmit_skb(skb, dev, priv, &tx_control,
46962306a36Sopenharmony_ci				       &mic_buf[0]);
47062306a36Sopenharmony_ci	if (err)
47162306a36Sopenharmony_ci		goto drop;
47262306a36Sopenharmony_ci
47362306a36Sopenharmony_ci	if (priv->has_alt_txcntl) {
47462306a36Sopenharmony_ci		/* WPA enabled firmwares have tx_cntl at the end of
47562306a36Sopenharmony_ci		 * the 802.11 header.  So write zeroed descriptor and
47662306a36Sopenharmony_ci		 * 802.11 header at the same time
47762306a36Sopenharmony_ci		 */
47862306a36Sopenharmony_ci		char desc[HERMES_802_3_OFFSET];
47962306a36Sopenharmony_ci		__le16 *txcntl = (__le16 *) &desc[HERMES_TXCNTL2_OFFSET];
48062306a36Sopenharmony_ci
48162306a36Sopenharmony_ci		memset(&desc, 0, sizeof(desc));
48262306a36Sopenharmony_ci
48362306a36Sopenharmony_ci		*txcntl = cpu_to_le16(tx_control);
48462306a36Sopenharmony_ci		err = hw->ops->bap_pwrite(hw, USER_BAP, &desc, sizeof(desc),
48562306a36Sopenharmony_ci					  txfid, 0);
48662306a36Sopenharmony_ci		if (err) {
48762306a36Sopenharmony_ci			if (net_ratelimit())
48862306a36Sopenharmony_ci				printk(KERN_ERR "%s: Error %d writing Tx "
48962306a36Sopenharmony_ci				       "descriptor to BAP\n", dev->name, err);
49062306a36Sopenharmony_ci			goto busy;
49162306a36Sopenharmony_ci		}
49262306a36Sopenharmony_ci	} else {
49362306a36Sopenharmony_ci		struct hermes_tx_descriptor desc;
49462306a36Sopenharmony_ci
49562306a36Sopenharmony_ci		memset(&desc, 0, sizeof(desc));
49662306a36Sopenharmony_ci
49762306a36Sopenharmony_ci		desc.tx_control = cpu_to_le16(tx_control);
49862306a36Sopenharmony_ci		err = hw->ops->bap_pwrite(hw, USER_BAP, &desc, sizeof(desc),
49962306a36Sopenharmony_ci					  txfid, 0);
50062306a36Sopenharmony_ci		if (err) {
50162306a36Sopenharmony_ci			if (net_ratelimit())
50262306a36Sopenharmony_ci				printk(KERN_ERR "%s: Error %d writing Tx "
50362306a36Sopenharmony_ci				       "descriptor to BAP\n", dev->name, err);
50462306a36Sopenharmony_ci			goto busy;
50562306a36Sopenharmony_ci		}
50662306a36Sopenharmony_ci
50762306a36Sopenharmony_ci		/* Clear the 802.11 header and data length fields - some
50862306a36Sopenharmony_ci		 * firmwares (e.g. Lucent/Agere 8.xx) appear to get confused
50962306a36Sopenharmony_ci		 * if this isn't done. */
51062306a36Sopenharmony_ci		hermes_clear_words(hw, HERMES_DATA0,
51162306a36Sopenharmony_ci				   HERMES_802_3_OFFSET - HERMES_802_11_OFFSET);
51262306a36Sopenharmony_ci	}
51362306a36Sopenharmony_ci
51462306a36Sopenharmony_ci	err = hw->ops->bap_pwrite(hw, USER_BAP, skb->data, skb->len,
51562306a36Sopenharmony_ci				  txfid, HERMES_802_3_OFFSET);
51662306a36Sopenharmony_ci	if (err) {
51762306a36Sopenharmony_ci		printk(KERN_ERR "%s: Error %d writing packet to BAP\n",
51862306a36Sopenharmony_ci		       dev->name, err);
51962306a36Sopenharmony_ci		goto busy;
52062306a36Sopenharmony_ci	}
52162306a36Sopenharmony_ci
52262306a36Sopenharmony_ci	if (tx_control & HERMES_TXCTRL_MIC) {
52362306a36Sopenharmony_ci		size_t offset = HERMES_802_3_OFFSET + skb->len;
52462306a36Sopenharmony_ci		size_t len = MICHAEL_MIC_LEN;
52562306a36Sopenharmony_ci
52662306a36Sopenharmony_ci		if (offset % 2) {
52762306a36Sopenharmony_ci			offset--;
52862306a36Sopenharmony_ci			len++;
52962306a36Sopenharmony_ci		}
53062306a36Sopenharmony_ci		err = hw->ops->bap_pwrite(hw, USER_BAP, &mic_buf[0], len,
53162306a36Sopenharmony_ci					  txfid, offset);
53262306a36Sopenharmony_ci		if (err) {
53362306a36Sopenharmony_ci			printk(KERN_ERR "%s: Error %d writing MIC to BAP\n",
53462306a36Sopenharmony_ci			       dev->name, err);
53562306a36Sopenharmony_ci			goto busy;
53662306a36Sopenharmony_ci		}
53762306a36Sopenharmony_ci	}
53862306a36Sopenharmony_ci
53962306a36Sopenharmony_ci	/* Finally, we actually initiate the send */
54062306a36Sopenharmony_ci	netif_stop_queue(dev);
54162306a36Sopenharmony_ci
54262306a36Sopenharmony_ci	err = hw->ops->cmd_wait(hw, HERMES_CMD_TX | HERMES_CMD_RECL,
54362306a36Sopenharmony_ci				txfid, NULL);
54462306a36Sopenharmony_ci	if (err) {
54562306a36Sopenharmony_ci		netif_start_queue(dev);
54662306a36Sopenharmony_ci		if (net_ratelimit())
54762306a36Sopenharmony_ci			printk(KERN_ERR "%s: Error %d transmitting packet\n",
54862306a36Sopenharmony_ci				dev->name, err);
54962306a36Sopenharmony_ci		goto busy;
55062306a36Sopenharmony_ci	}
55162306a36Sopenharmony_ci
55262306a36Sopenharmony_ci	stats->tx_bytes += HERMES_802_3_OFFSET + skb->len;
55362306a36Sopenharmony_ci	goto ok;
55462306a36Sopenharmony_ci
55562306a36Sopenharmony_ci drop:
55662306a36Sopenharmony_ci	stats->tx_errors++;
55762306a36Sopenharmony_ci	stats->tx_dropped++;
55862306a36Sopenharmony_ci
55962306a36Sopenharmony_ci ok:
56062306a36Sopenharmony_ci	orinoco_unlock(priv, &flags);
56162306a36Sopenharmony_ci	dev_kfree_skb(skb);
56262306a36Sopenharmony_ci	return NETDEV_TX_OK;
56362306a36Sopenharmony_ci
56462306a36Sopenharmony_ci busy:
56562306a36Sopenharmony_ci	if (err == -EIO)
56662306a36Sopenharmony_ci		schedule_work(&priv->reset_work);
56762306a36Sopenharmony_ci	orinoco_unlock(priv, &flags);
56862306a36Sopenharmony_ci	return NETDEV_TX_BUSY;
56962306a36Sopenharmony_ci}
57062306a36Sopenharmony_ci
57162306a36Sopenharmony_cistatic void __orinoco_ev_alloc(struct net_device *dev, struct hermes *hw)
57262306a36Sopenharmony_ci{
57362306a36Sopenharmony_ci	struct orinoco_private *priv = ndev_priv(dev);
57462306a36Sopenharmony_ci	u16 fid = hermes_read_regn(hw, ALLOCFID);
57562306a36Sopenharmony_ci
57662306a36Sopenharmony_ci	if (fid != priv->txfid) {
57762306a36Sopenharmony_ci		if (fid != DUMMY_FID)
57862306a36Sopenharmony_ci			printk(KERN_WARNING "%s: Allocate event on unexpected fid (%04X)\n",
57962306a36Sopenharmony_ci			       dev->name, fid);
58062306a36Sopenharmony_ci		return;
58162306a36Sopenharmony_ci	}
58262306a36Sopenharmony_ci
58362306a36Sopenharmony_ci	hermes_write_regn(hw, ALLOCFID, DUMMY_FID);
58462306a36Sopenharmony_ci}
58562306a36Sopenharmony_ci
58662306a36Sopenharmony_cistatic void __orinoco_ev_tx(struct net_device *dev, struct hermes *hw)
58762306a36Sopenharmony_ci{
58862306a36Sopenharmony_ci	dev->stats.tx_packets++;
58962306a36Sopenharmony_ci
59062306a36Sopenharmony_ci	netif_wake_queue(dev);
59162306a36Sopenharmony_ci
59262306a36Sopenharmony_ci	hermes_write_regn(hw, TXCOMPLFID, DUMMY_FID);
59362306a36Sopenharmony_ci}
59462306a36Sopenharmony_ci
59562306a36Sopenharmony_cistatic void __orinoco_ev_txexc(struct net_device *dev, struct hermes *hw)
59662306a36Sopenharmony_ci{
59762306a36Sopenharmony_ci	struct net_device_stats *stats = &dev->stats;
59862306a36Sopenharmony_ci	u16 fid = hermes_read_regn(hw, TXCOMPLFID);
59962306a36Sopenharmony_ci	u16 status;
60062306a36Sopenharmony_ci	struct hermes_txexc_data hdr;
60162306a36Sopenharmony_ci	int err = 0;
60262306a36Sopenharmony_ci
60362306a36Sopenharmony_ci	if (fid == DUMMY_FID)
60462306a36Sopenharmony_ci		return; /* Nothing's really happened */
60562306a36Sopenharmony_ci
60662306a36Sopenharmony_ci	/* Read part of the frame header - we need status and addr1 */
60762306a36Sopenharmony_ci	err = hw->ops->bap_pread(hw, IRQ_BAP, &hdr,
60862306a36Sopenharmony_ci				 sizeof(struct hermes_txexc_data),
60962306a36Sopenharmony_ci				 fid, 0);
61062306a36Sopenharmony_ci
61162306a36Sopenharmony_ci	hermes_write_regn(hw, TXCOMPLFID, DUMMY_FID);
61262306a36Sopenharmony_ci	stats->tx_errors++;
61362306a36Sopenharmony_ci
61462306a36Sopenharmony_ci	if (err) {
61562306a36Sopenharmony_ci		printk(KERN_WARNING "%s: Unable to read descriptor on Tx error "
61662306a36Sopenharmony_ci		       "(FID=%04X error %d)\n",
61762306a36Sopenharmony_ci		       dev->name, fid, err);
61862306a36Sopenharmony_ci		return;
61962306a36Sopenharmony_ci	}
62062306a36Sopenharmony_ci
62162306a36Sopenharmony_ci	DEBUG(1, "%s: Tx error, err %d (FID=%04X)\n", dev->name,
62262306a36Sopenharmony_ci	      err, fid);
62362306a36Sopenharmony_ci
62462306a36Sopenharmony_ci	/* We produce a TXDROP event only for retry or lifetime
62562306a36Sopenharmony_ci	 * exceeded, because that's the only status that really mean
62662306a36Sopenharmony_ci	 * that this particular node went away.
62762306a36Sopenharmony_ci	 * Other errors means that *we* screwed up. - Jean II */
62862306a36Sopenharmony_ci	status = le16_to_cpu(hdr.desc.status);
62962306a36Sopenharmony_ci	if (status & (HERMES_TXSTAT_RETRYERR | HERMES_TXSTAT_AGEDERR)) {
63062306a36Sopenharmony_ci		union iwreq_data	wrqu;
63162306a36Sopenharmony_ci
63262306a36Sopenharmony_ci		/* Copy 802.11 dest address.
63362306a36Sopenharmony_ci		 * We use the 802.11 header because the frame may
63462306a36Sopenharmony_ci		 * not be 802.3 or may be mangled...
63562306a36Sopenharmony_ci		 * In Ad-Hoc mode, it will be the node address.
63662306a36Sopenharmony_ci		 * In managed mode, it will be most likely the AP addr
63762306a36Sopenharmony_ci		 * User space will figure out how to convert it to
63862306a36Sopenharmony_ci		 * whatever it needs (IP address or else).
63962306a36Sopenharmony_ci		 * - Jean II */
64062306a36Sopenharmony_ci		memcpy(wrqu.addr.sa_data, hdr.addr1, ETH_ALEN);
64162306a36Sopenharmony_ci		wrqu.addr.sa_family = ARPHRD_ETHER;
64262306a36Sopenharmony_ci
64362306a36Sopenharmony_ci		/* Send event to user space */
64462306a36Sopenharmony_ci		wireless_send_event(dev, IWEVTXDROP, &wrqu, NULL);
64562306a36Sopenharmony_ci	}
64662306a36Sopenharmony_ci
64762306a36Sopenharmony_ci	netif_wake_queue(dev);
64862306a36Sopenharmony_ci}
64962306a36Sopenharmony_ci
65062306a36Sopenharmony_civoid orinoco_tx_timeout(struct net_device *dev, unsigned int txqueue)
65162306a36Sopenharmony_ci{
65262306a36Sopenharmony_ci	struct orinoco_private *priv = ndev_priv(dev);
65362306a36Sopenharmony_ci	struct net_device_stats *stats = &dev->stats;
65462306a36Sopenharmony_ci	struct hermes *hw = &priv->hw;
65562306a36Sopenharmony_ci
65662306a36Sopenharmony_ci	printk(KERN_WARNING "%s: Tx timeout! "
65762306a36Sopenharmony_ci	       "ALLOCFID=%04x, TXCOMPLFID=%04x, EVSTAT=%04x\n",
65862306a36Sopenharmony_ci	       dev->name, hermes_read_regn(hw, ALLOCFID),
65962306a36Sopenharmony_ci	       hermes_read_regn(hw, TXCOMPLFID), hermes_read_regn(hw, EVSTAT));
66062306a36Sopenharmony_ci
66162306a36Sopenharmony_ci	stats->tx_errors++;
66262306a36Sopenharmony_ci
66362306a36Sopenharmony_ci	schedule_work(&priv->reset_work);
66462306a36Sopenharmony_ci}
66562306a36Sopenharmony_ciEXPORT_SYMBOL(orinoco_tx_timeout);
66662306a36Sopenharmony_ci
66762306a36Sopenharmony_ci/********************************************************************/
66862306a36Sopenharmony_ci/* Rx path (data frames)                                            */
66962306a36Sopenharmony_ci/********************************************************************/
67062306a36Sopenharmony_ci
67162306a36Sopenharmony_ci/* Does the frame have a SNAP header indicating it should be
67262306a36Sopenharmony_ci * de-encapsulated to Ethernet-II? */
67362306a36Sopenharmony_cistatic inline int is_ethersnap(void *_hdr)
67462306a36Sopenharmony_ci{
67562306a36Sopenharmony_ci	u8 *hdr = _hdr;
67662306a36Sopenharmony_ci
67762306a36Sopenharmony_ci	/* We de-encapsulate all packets which, a) have SNAP headers
67862306a36Sopenharmony_ci	 * (i.e. SSAP=DSAP=0xaa and CTRL=0x3 in the 802.2 LLC header
67962306a36Sopenharmony_ci	 * and where b) the OUI of the SNAP header is 00:00:00 or
68062306a36Sopenharmony_ci	 * 00:00:f8 - we need both because different APs appear to use
68162306a36Sopenharmony_ci	 * different OUIs for some reason */
68262306a36Sopenharmony_ci	return (memcmp(hdr, &encaps_hdr, 5) == 0)
68362306a36Sopenharmony_ci		&& ((hdr[5] == 0x00) || (hdr[5] == 0xf8));
68462306a36Sopenharmony_ci}
68562306a36Sopenharmony_ci
68662306a36Sopenharmony_cistatic inline void orinoco_spy_gather(struct net_device *dev, u_char *mac,
68762306a36Sopenharmony_ci				      int level, int noise)
68862306a36Sopenharmony_ci{
68962306a36Sopenharmony_ci	struct iw_quality wstats;
69062306a36Sopenharmony_ci	wstats.level = level - 0x95;
69162306a36Sopenharmony_ci	wstats.noise = noise - 0x95;
69262306a36Sopenharmony_ci	wstats.qual = (level > noise) ? (level - noise) : 0;
69362306a36Sopenharmony_ci	wstats.updated = IW_QUAL_ALL_UPDATED | IW_QUAL_DBM;
69462306a36Sopenharmony_ci	/* Update spy records */
69562306a36Sopenharmony_ci	wireless_spy_update(dev, mac, &wstats);
69662306a36Sopenharmony_ci}
69762306a36Sopenharmony_ci
69862306a36Sopenharmony_cistatic void orinoco_stat_gather(struct net_device *dev,
69962306a36Sopenharmony_ci				struct sk_buff *skb,
70062306a36Sopenharmony_ci				struct hermes_rx_descriptor *desc)
70162306a36Sopenharmony_ci{
70262306a36Sopenharmony_ci	struct orinoco_private *priv = ndev_priv(dev);
70362306a36Sopenharmony_ci
70462306a36Sopenharmony_ci	/* Using spy support with lots of Rx packets, like in an
70562306a36Sopenharmony_ci	 * infrastructure (AP), will really slow down everything, because
70662306a36Sopenharmony_ci	 * the MAC address must be compared to each entry of the spy list.
70762306a36Sopenharmony_ci	 * If the user really asks for it (set some address in the
70862306a36Sopenharmony_ci	 * spy list), we do it, but he will pay the price.
70962306a36Sopenharmony_ci	 * Note that to get here, you need both WIRELESS_SPY
71062306a36Sopenharmony_ci	 * compiled in AND some addresses in the list !!!
71162306a36Sopenharmony_ci	 */
71262306a36Sopenharmony_ci	/* Note : gcc will optimise the whole section away if
71362306a36Sopenharmony_ci	 * WIRELESS_SPY is not defined... - Jean II */
71462306a36Sopenharmony_ci	if (SPY_NUMBER(priv)) {
71562306a36Sopenharmony_ci		orinoco_spy_gather(dev, skb_mac_header(skb) + ETH_ALEN,
71662306a36Sopenharmony_ci				   desc->signal, desc->silence);
71762306a36Sopenharmony_ci	}
71862306a36Sopenharmony_ci}
71962306a36Sopenharmony_ci
72062306a36Sopenharmony_ci/*
72162306a36Sopenharmony_ci * orinoco_rx_monitor - handle received monitor frames.
72262306a36Sopenharmony_ci *
72362306a36Sopenharmony_ci * Arguments:
72462306a36Sopenharmony_ci *	dev		network device
72562306a36Sopenharmony_ci *	rxfid		received FID
72662306a36Sopenharmony_ci *	desc		rx descriptor of the frame
72762306a36Sopenharmony_ci *
72862306a36Sopenharmony_ci * Call context: interrupt
72962306a36Sopenharmony_ci */
73062306a36Sopenharmony_cistatic void orinoco_rx_monitor(struct net_device *dev, u16 rxfid,
73162306a36Sopenharmony_ci			       struct hermes_rx_descriptor *desc)
73262306a36Sopenharmony_ci{
73362306a36Sopenharmony_ci	u32 hdrlen = 30;	/* return full header by default */
73462306a36Sopenharmony_ci	u32 datalen = 0;
73562306a36Sopenharmony_ci	u16 fc;
73662306a36Sopenharmony_ci	int err;
73762306a36Sopenharmony_ci	int len;
73862306a36Sopenharmony_ci	struct sk_buff *skb;
73962306a36Sopenharmony_ci	struct orinoco_private *priv = ndev_priv(dev);
74062306a36Sopenharmony_ci	struct net_device_stats *stats = &dev->stats;
74162306a36Sopenharmony_ci	struct hermes *hw = &priv->hw;
74262306a36Sopenharmony_ci
74362306a36Sopenharmony_ci	len = le16_to_cpu(desc->data_len);
74462306a36Sopenharmony_ci
74562306a36Sopenharmony_ci	/* Determine the size of the header and the data */
74662306a36Sopenharmony_ci	fc = le16_to_cpu(desc->frame_ctl);
74762306a36Sopenharmony_ci	switch (fc & IEEE80211_FCTL_FTYPE) {
74862306a36Sopenharmony_ci	case IEEE80211_FTYPE_DATA:
74962306a36Sopenharmony_ci		if ((fc & IEEE80211_FCTL_TODS)
75062306a36Sopenharmony_ci		    && (fc & IEEE80211_FCTL_FROMDS))
75162306a36Sopenharmony_ci			hdrlen = 30;
75262306a36Sopenharmony_ci		else
75362306a36Sopenharmony_ci			hdrlen = 24;
75462306a36Sopenharmony_ci		datalen = len;
75562306a36Sopenharmony_ci		break;
75662306a36Sopenharmony_ci	case IEEE80211_FTYPE_MGMT:
75762306a36Sopenharmony_ci		hdrlen = 24;
75862306a36Sopenharmony_ci		datalen = len;
75962306a36Sopenharmony_ci		break;
76062306a36Sopenharmony_ci	case IEEE80211_FTYPE_CTL:
76162306a36Sopenharmony_ci		switch (fc & IEEE80211_FCTL_STYPE) {
76262306a36Sopenharmony_ci		case IEEE80211_STYPE_PSPOLL:
76362306a36Sopenharmony_ci		case IEEE80211_STYPE_RTS:
76462306a36Sopenharmony_ci		case IEEE80211_STYPE_CFEND:
76562306a36Sopenharmony_ci		case IEEE80211_STYPE_CFENDACK:
76662306a36Sopenharmony_ci			hdrlen = 16;
76762306a36Sopenharmony_ci			break;
76862306a36Sopenharmony_ci		case IEEE80211_STYPE_CTS:
76962306a36Sopenharmony_ci		case IEEE80211_STYPE_ACK:
77062306a36Sopenharmony_ci			hdrlen = 10;
77162306a36Sopenharmony_ci			break;
77262306a36Sopenharmony_ci		}
77362306a36Sopenharmony_ci		break;
77462306a36Sopenharmony_ci	default:
77562306a36Sopenharmony_ci		/* Unknown frame type */
77662306a36Sopenharmony_ci		break;
77762306a36Sopenharmony_ci	}
77862306a36Sopenharmony_ci
77962306a36Sopenharmony_ci	/* sanity check the length */
78062306a36Sopenharmony_ci	if (datalen > IEEE80211_MAX_DATA_LEN + 12) {
78162306a36Sopenharmony_ci		printk(KERN_DEBUG "%s: oversized monitor frame, "
78262306a36Sopenharmony_ci		       "data length = %d\n", dev->name, datalen);
78362306a36Sopenharmony_ci		stats->rx_length_errors++;
78462306a36Sopenharmony_ci		goto update_stats;
78562306a36Sopenharmony_ci	}
78662306a36Sopenharmony_ci
78762306a36Sopenharmony_ci	skb = dev_alloc_skb(hdrlen + datalen);
78862306a36Sopenharmony_ci	if (!skb) {
78962306a36Sopenharmony_ci		printk(KERN_WARNING "%s: Cannot allocate skb for monitor frame\n",
79062306a36Sopenharmony_ci		       dev->name);
79162306a36Sopenharmony_ci		goto update_stats;
79262306a36Sopenharmony_ci	}
79362306a36Sopenharmony_ci
79462306a36Sopenharmony_ci	/* Copy the 802.11 header to the skb */
79562306a36Sopenharmony_ci	skb_put_data(skb, &(desc->frame_ctl), hdrlen);
79662306a36Sopenharmony_ci	skb_reset_mac_header(skb);
79762306a36Sopenharmony_ci
79862306a36Sopenharmony_ci	/* If any, copy the data from the card to the skb */
79962306a36Sopenharmony_ci	if (datalen > 0) {
80062306a36Sopenharmony_ci		err = hw->ops->bap_pread(hw, IRQ_BAP, skb_put(skb, datalen),
80162306a36Sopenharmony_ci					 ALIGN(datalen, 2), rxfid,
80262306a36Sopenharmony_ci					 HERMES_802_2_OFFSET);
80362306a36Sopenharmony_ci		if (err) {
80462306a36Sopenharmony_ci			printk(KERN_ERR "%s: error %d reading monitor frame\n",
80562306a36Sopenharmony_ci			       dev->name, err);
80662306a36Sopenharmony_ci			goto drop;
80762306a36Sopenharmony_ci		}
80862306a36Sopenharmony_ci	}
80962306a36Sopenharmony_ci
81062306a36Sopenharmony_ci	skb->dev = dev;
81162306a36Sopenharmony_ci	skb->ip_summed = CHECKSUM_NONE;
81262306a36Sopenharmony_ci	skb->pkt_type = PACKET_OTHERHOST;
81362306a36Sopenharmony_ci	skb->protocol = cpu_to_be16(ETH_P_802_2);
81462306a36Sopenharmony_ci
81562306a36Sopenharmony_ci	stats->rx_packets++;
81662306a36Sopenharmony_ci	stats->rx_bytes += skb->len;
81762306a36Sopenharmony_ci
81862306a36Sopenharmony_ci	netif_rx(skb);
81962306a36Sopenharmony_ci	return;
82062306a36Sopenharmony_ci
82162306a36Sopenharmony_ci drop:
82262306a36Sopenharmony_ci	dev_kfree_skb_irq(skb);
82362306a36Sopenharmony_ci update_stats:
82462306a36Sopenharmony_ci	stats->rx_errors++;
82562306a36Sopenharmony_ci	stats->rx_dropped++;
82662306a36Sopenharmony_ci}
82762306a36Sopenharmony_ci
82862306a36Sopenharmony_civoid __orinoco_ev_rx(struct net_device *dev, struct hermes *hw)
82962306a36Sopenharmony_ci{
83062306a36Sopenharmony_ci	struct orinoco_private *priv = ndev_priv(dev);
83162306a36Sopenharmony_ci	struct net_device_stats *stats = &dev->stats;
83262306a36Sopenharmony_ci	struct iw_statistics *wstats = &priv->wstats;
83362306a36Sopenharmony_ci	struct sk_buff *skb = NULL;
83462306a36Sopenharmony_ci	u16 rxfid, status;
83562306a36Sopenharmony_ci	int length;
83662306a36Sopenharmony_ci	struct hermes_rx_descriptor *desc;
83762306a36Sopenharmony_ci	struct orinoco_rx_data *rx_data;
83862306a36Sopenharmony_ci	int err;
83962306a36Sopenharmony_ci
84062306a36Sopenharmony_ci	desc = kmalloc(sizeof(*desc), GFP_ATOMIC);
84162306a36Sopenharmony_ci	if (!desc)
84262306a36Sopenharmony_ci		goto update_stats;
84362306a36Sopenharmony_ci
84462306a36Sopenharmony_ci	rxfid = hermes_read_regn(hw, RXFID);
84562306a36Sopenharmony_ci
84662306a36Sopenharmony_ci	err = hw->ops->bap_pread(hw, IRQ_BAP, desc, sizeof(*desc),
84762306a36Sopenharmony_ci				 rxfid, 0);
84862306a36Sopenharmony_ci	if (err) {
84962306a36Sopenharmony_ci		printk(KERN_ERR "%s: error %d reading Rx descriptor. "
85062306a36Sopenharmony_ci		       "Frame dropped.\n", dev->name, err);
85162306a36Sopenharmony_ci		goto update_stats;
85262306a36Sopenharmony_ci	}
85362306a36Sopenharmony_ci
85462306a36Sopenharmony_ci	status = le16_to_cpu(desc->status);
85562306a36Sopenharmony_ci
85662306a36Sopenharmony_ci	if (status & HERMES_RXSTAT_BADCRC) {
85762306a36Sopenharmony_ci		DEBUG(1, "%s: Bad CRC on Rx. Frame dropped.\n",
85862306a36Sopenharmony_ci		      dev->name);
85962306a36Sopenharmony_ci		stats->rx_crc_errors++;
86062306a36Sopenharmony_ci		goto update_stats;
86162306a36Sopenharmony_ci	}
86262306a36Sopenharmony_ci
86362306a36Sopenharmony_ci	/* Handle frames in monitor mode */
86462306a36Sopenharmony_ci	if (priv->iw_mode == NL80211_IFTYPE_MONITOR) {
86562306a36Sopenharmony_ci		orinoco_rx_monitor(dev, rxfid, desc);
86662306a36Sopenharmony_ci		goto out;
86762306a36Sopenharmony_ci	}
86862306a36Sopenharmony_ci
86962306a36Sopenharmony_ci	if (status & HERMES_RXSTAT_UNDECRYPTABLE) {
87062306a36Sopenharmony_ci		DEBUG(1, "%s: Undecryptable frame on Rx. Frame dropped.\n",
87162306a36Sopenharmony_ci		      dev->name);
87262306a36Sopenharmony_ci		wstats->discard.code++;
87362306a36Sopenharmony_ci		goto update_stats;
87462306a36Sopenharmony_ci	}
87562306a36Sopenharmony_ci
87662306a36Sopenharmony_ci	length = le16_to_cpu(desc->data_len);
87762306a36Sopenharmony_ci
87862306a36Sopenharmony_ci	/* Sanity checks */
87962306a36Sopenharmony_ci	if (length < 3) { /* No for even an 802.2 LLC header */
88062306a36Sopenharmony_ci		/* At least on Symbol firmware with PCF we get quite a
88162306a36Sopenharmony_ci		   lot of these legitimately - Poll frames with no
88262306a36Sopenharmony_ci		   data. */
88362306a36Sopenharmony_ci		goto out;
88462306a36Sopenharmony_ci	}
88562306a36Sopenharmony_ci	if (length > IEEE80211_MAX_DATA_LEN) {
88662306a36Sopenharmony_ci		printk(KERN_WARNING "%s: Oversized frame received (%d bytes)\n",
88762306a36Sopenharmony_ci		       dev->name, length);
88862306a36Sopenharmony_ci		stats->rx_length_errors++;
88962306a36Sopenharmony_ci		goto update_stats;
89062306a36Sopenharmony_ci	}
89162306a36Sopenharmony_ci
89262306a36Sopenharmony_ci	/* Payload size does not include Michael MIC. Increase payload
89362306a36Sopenharmony_ci	 * size to read it together with the data. */
89462306a36Sopenharmony_ci	if (status & HERMES_RXSTAT_MIC)
89562306a36Sopenharmony_ci		length += MICHAEL_MIC_LEN;
89662306a36Sopenharmony_ci
89762306a36Sopenharmony_ci	/* We need space for the packet data itself, plus an ethernet
89862306a36Sopenharmony_ci	   header, plus 2 bytes so we can align the IP header on a
89962306a36Sopenharmony_ci	   32bit boundary, plus 1 byte so we can read in odd length
90062306a36Sopenharmony_ci	   packets from the card, which has an IO granularity of 16
90162306a36Sopenharmony_ci	   bits */
90262306a36Sopenharmony_ci	skb = dev_alloc_skb(length + ETH_HLEN + 2 + 1);
90362306a36Sopenharmony_ci	if (!skb) {
90462306a36Sopenharmony_ci		printk(KERN_WARNING "%s: Can't allocate skb for Rx\n",
90562306a36Sopenharmony_ci		       dev->name);
90662306a36Sopenharmony_ci		goto update_stats;
90762306a36Sopenharmony_ci	}
90862306a36Sopenharmony_ci
90962306a36Sopenharmony_ci	/* We'll prepend the header, so reserve space for it.  The worst
91062306a36Sopenharmony_ci	   case is no decapsulation, when 802.3 header is prepended and
91162306a36Sopenharmony_ci	   nothing is removed.  2 is for aligning the IP header.  */
91262306a36Sopenharmony_ci	skb_reserve(skb, ETH_HLEN + 2);
91362306a36Sopenharmony_ci
91462306a36Sopenharmony_ci	err = hw->ops->bap_pread(hw, IRQ_BAP, skb_put(skb, length),
91562306a36Sopenharmony_ci				 ALIGN(length, 2), rxfid,
91662306a36Sopenharmony_ci				 HERMES_802_2_OFFSET);
91762306a36Sopenharmony_ci	if (err) {
91862306a36Sopenharmony_ci		printk(KERN_ERR "%s: error %d reading frame. "
91962306a36Sopenharmony_ci		       "Frame dropped.\n", dev->name, err);
92062306a36Sopenharmony_ci		goto drop;
92162306a36Sopenharmony_ci	}
92262306a36Sopenharmony_ci
92362306a36Sopenharmony_ci	/* Add desc and skb to rx queue */
92462306a36Sopenharmony_ci	rx_data = kzalloc(sizeof(*rx_data), GFP_ATOMIC);
92562306a36Sopenharmony_ci	if (!rx_data)
92662306a36Sopenharmony_ci		goto drop;
92762306a36Sopenharmony_ci
92862306a36Sopenharmony_ci	rx_data->desc = desc;
92962306a36Sopenharmony_ci	rx_data->skb = skb;
93062306a36Sopenharmony_ci	list_add_tail(&rx_data->list, &priv->rx_list);
93162306a36Sopenharmony_ci	tasklet_schedule(&priv->rx_tasklet);
93262306a36Sopenharmony_ci
93362306a36Sopenharmony_ci	return;
93462306a36Sopenharmony_ci
93562306a36Sopenharmony_cidrop:
93662306a36Sopenharmony_ci	dev_kfree_skb_irq(skb);
93762306a36Sopenharmony_ciupdate_stats:
93862306a36Sopenharmony_ci	stats->rx_errors++;
93962306a36Sopenharmony_ci	stats->rx_dropped++;
94062306a36Sopenharmony_ciout:
94162306a36Sopenharmony_ci	kfree(desc);
94262306a36Sopenharmony_ci}
94362306a36Sopenharmony_ciEXPORT_SYMBOL(__orinoco_ev_rx);
94462306a36Sopenharmony_ci
94562306a36Sopenharmony_cistatic void orinoco_rx(struct net_device *dev,
94662306a36Sopenharmony_ci		       struct hermes_rx_descriptor *desc,
94762306a36Sopenharmony_ci		       struct sk_buff *skb)
94862306a36Sopenharmony_ci{
94962306a36Sopenharmony_ci	struct orinoco_private *priv = ndev_priv(dev);
95062306a36Sopenharmony_ci	struct net_device_stats *stats = &dev->stats;
95162306a36Sopenharmony_ci	u16 status, fc;
95262306a36Sopenharmony_ci	int length;
95362306a36Sopenharmony_ci	struct ethhdr *hdr;
95462306a36Sopenharmony_ci
95562306a36Sopenharmony_ci	status = le16_to_cpu(desc->status);
95662306a36Sopenharmony_ci	length = le16_to_cpu(desc->data_len);
95762306a36Sopenharmony_ci	fc = le16_to_cpu(desc->frame_ctl);
95862306a36Sopenharmony_ci
95962306a36Sopenharmony_ci	/* Calculate and check MIC */
96062306a36Sopenharmony_ci	if (status & HERMES_RXSTAT_MIC) {
96162306a36Sopenharmony_ci		struct orinoco_tkip_key *key;
96262306a36Sopenharmony_ci		int key_id = ((status & HERMES_RXSTAT_MIC_KEY_ID) >>
96362306a36Sopenharmony_ci			      HERMES_MIC_KEY_ID_SHIFT);
96462306a36Sopenharmony_ci		u8 mic[MICHAEL_MIC_LEN];
96562306a36Sopenharmony_ci		u8 *rxmic;
96662306a36Sopenharmony_ci		u8 *src = (fc & IEEE80211_FCTL_FROMDS) ?
96762306a36Sopenharmony_ci			desc->addr3 : desc->addr2;
96862306a36Sopenharmony_ci
96962306a36Sopenharmony_ci		/* Extract Michael MIC from payload */
97062306a36Sopenharmony_ci		rxmic = skb->data + skb->len - MICHAEL_MIC_LEN;
97162306a36Sopenharmony_ci
97262306a36Sopenharmony_ci		skb_trim(skb, skb->len - MICHAEL_MIC_LEN);
97362306a36Sopenharmony_ci		length -= MICHAEL_MIC_LEN;
97462306a36Sopenharmony_ci
97562306a36Sopenharmony_ci		key = (struct orinoco_tkip_key *) priv->keys[key_id].key;
97662306a36Sopenharmony_ci
97762306a36Sopenharmony_ci		if (!key) {
97862306a36Sopenharmony_ci			printk(KERN_WARNING "%s: Received encrypted frame from "
97962306a36Sopenharmony_ci			       "%pM using key %i, but key is not installed\n",
98062306a36Sopenharmony_ci			       dev->name, src, key_id);
98162306a36Sopenharmony_ci			goto drop;
98262306a36Sopenharmony_ci		}
98362306a36Sopenharmony_ci
98462306a36Sopenharmony_ci		orinoco_mic(priv->rx_tfm_mic, key->rx_mic, desc->addr1, src,
98562306a36Sopenharmony_ci			    0, /* priority or QoS? */
98662306a36Sopenharmony_ci			    skb->data, skb->len, &mic[0]);
98762306a36Sopenharmony_ci
98862306a36Sopenharmony_ci		if (memcmp(mic, rxmic,
98962306a36Sopenharmony_ci			   MICHAEL_MIC_LEN)) {
99062306a36Sopenharmony_ci			union iwreq_data wrqu;
99162306a36Sopenharmony_ci			struct iw_michaelmicfailure wxmic;
99262306a36Sopenharmony_ci
99362306a36Sopenharmony_ci			printk(KERN_WARNING "%s: "
99462306a36Sopenharmony_ci			       "Invalid Michael MIC in data frame from %pM, "
99562306a36Sopenharmony_ci			       "using key %i\n",
99662306a36Sopenharmony_ci			       dev->name, src, key_id);
99762306a36Sopenharmony_ci
99862306a36Sopenharmony_ci			/* TODO: update stats */
99962306a36Sopenharmony_ci
100062306a36Sopenharmony_ci			/* Notify userspace */
100162306a36Sopenharmony_ci			memset(&wxmic, 0, sizeof(wxmic));
100262306a36Sopenharmony_ci			wxmic.flags = key_id & IW_MICFAILURE_KEY_ID;
100362306a36Sopenharmony_ci			wxmic.flags |= (desc->addr1[0] & 1) ?
100462306a36Sopenharmony_ci				IW_MICFAILURE_GROUP : IW_MICFAILURE_PAIRWISE;
100562306a36Sopenharmony_ci			wxmic.src_addr.sa_family = ARPHRD_ETHER;
100662306a36Sopenharmony_ci			memcpy(wxmic.src_addr.sa_data, src, ETH_ALEN);
100762306a36Sopenharmony_ci
100862306a36Sopenharmony_ci			(void) orinoco_hw_get_tkip_iv(priv, key_id,
100962306a36Sopenharmony_ci						      &wxmic.tsc[0]);
101062306a36Sopenharmony_ci
101162306a36Sopenharmony_ci			memset(&wrqu, 0, sizeof(wrqu));
101262306a36Sopenharmony_ci			wrqu.data.length = sizeof(wxmic);
101362306a36Sopenharmony_ci			wireless_send_event(dev, IWEVMICHAELMICFAILURE, &wrqu,
101462306a36Sopenharmony_ci					    (char *) &wxmic);
101562306a36Sopenharmony_ci
101662306a36Sopenharmony_ci			goto drop;
101762306a36Sopenharmony_ci		}
101862306a36Sopenharmony_ci	}
101962306a36Sopenharmony_ci
102062306a36Sopenharmony_ci	/* Handle decapsulation
102162306a36Sopenharmony_ci	 * In most cases, the firmware tell us about SNAP frames.
102262306a36Sopenharmony_ci	 * For some reason, the SNAP frames sent by LinkSys APs
102362306a36Sopenharmony_ci	 * are not properly recognised by most firmwares.
102462306a36Sopenharmony_ci	 * So, check ourselves */
102562306a36Sopenharmony_ci	if (length >= ENCAPS_OVERHEAD &&
102662306a36Sopenharmony_ci	    (((status & HERMES_RXSTAT_MSGTYPE) == HERMES_RXSTAT_1042) ||
102762306a36Sopenharmony_ci	     ((status & HERMES_RXSTAT_MSGTYPE) == HERMES_RXSTAT_TUNNEL) ||
102862306a36Sopenharmony_ci	     is_ethersnap(skb->data))) {
102962306a36Sopenharmony_ci		/* These indicate a SNAP within 802.2 LLC within
103062306a36Sopenharmony_ci		   802.11 frame which we'll need to de-encapsulate to
103162306a36Sopenharmony_ci		   the original EthernetII frame. */
103262306a36Sopenharmony_ci		hdr = skb_push(skb, ETH_HLEN - ENCAPS_OVERHEAD);
103362306a36Sopenharmony_ci	} else {
103462306a36Sopenharmony_ci		/* 802.3 frame - prepend 802.3 header as is */
103562306a36Sopenharmony_ci		hdr = skb_push(skb, ETH_HLEN);
103662306a36Sopenharmony_ci		hdr->h_proto = htons(length);
103762306a36Sopenharmony_ci	}
103862306a36Sopenharmony_ci	memcpy(hdr->h_dest, desc->addr1, ETH_ALEN);
103962306a36Sopenharmony_ci	if (fc & IEEE80211_FCTL_FROMDS)
104062306a36Sopenharmony_ci		memcpy(hdr->h_source, desc->addr3, ETH_ALEN);
104162306a36Sopenharmony_ci	else
104262306a36Sopenharmony_ci		memcpy(hdr->h_source, desc->addr2, ETH_ALEN);
104362306a36Sopenharmony_ci
104462306a36Sopenharmony_ci	skb->protocol = eth_type_trans(skb, dev);
104562306a36Sopenharmony_ci	skb->ip_summed = CHECKSUM_NONE;
104662306a36Sopenharmony_ci	if (fc & IEEE80211_FCTL_TODS)
104762306a36Sopenharmony_ci		skb->pkt_type = PACKET_OTHERHOST;
104862306a36Sopenharmony_ci
104962306a36Sopenharmony_ci	/* Process the wireless stats if needed */
105062306a36Sopenharmony_ci	orinoco_stat_gather(dev, skb, desc);
105162306a36Sopenharmony_ci
105262306a36Sopenharmony_ci	/* Pass the packet to the networking stack */
105362306a36Sopenharmony_ci	netif_rx(skb);
105462306a36Sopenharmony_ci	stats->rx_packets++;
105562306a36Sopenharmony_ci	stats->rx_bytes += length;
105662306a36Sopenharmony_ci
105762306a36Sopenharmony_ci	return;
105862306a36Sopenharmony_ci
105962306a36Sopenharmony_ci drop:
106062306a36Sopenharmony_ci	dev_kfree_skb(skb);
106162306a36Sopenharmony_ci	stats->rx_errors++;
106262306a36Sopenharmony_ci	stats->rx_dropped++;
106362306a36Sopenharmony_ci}
106462306a36Sopenharmony_ci
106562306a36Sopenharmony_cistatic void orinoco_rx_isr_tasklet(struct tasklet_struct *t)
106662306a36Sopenharmony_ci{
106762306a36Sopenharmony_ci	struct orinoco_private *priv = from_tasklet(priv, t, rx_tasklet);
106862306a36Sopenharmony_ci	struct net_device *dev = priv->ndev;
106962306a36Sopenharmony_ci	struct orinoco_rx_data *rx_data, *temp;
107062306a36Sopenharmony_ci	struct hermes_rx_descriptor *desc;
107162306a36Sopenharmony_ci	struct sk_buff *skb;
107262306a36Sopenharmony_ci	unsigned long flags;
107362306a36Sopenharmony_ci
107462306a36Sopenharmony_ci	/* orinoco_rx requires the driver lock, and we also need to
107562306a36Sopenharmony_ci	 * protect priv->rx_list, so just hold the lock over the
107662306a36Sopenharmony_ci	 * lot.
107762306a36Sopenharmony_ci	 *
107862306a36Sopenharmony_ci	 * If orinoco_lock fails, we've unplugged the card. In this
107962306a36Sopenharmony_ci	 * case just abort. */
108062306a36Sopenharmony_ci	if (orinoco_lock(priv, &flags) != 0)
108162306a36Sopenharmony_ci		return;
108262306a36Sopenharmony_ci
108362306a36Sopenharmony_ci	/* extract desc and skb from queue */
108462306a36Sopenharmony_ci	list_for_each_entry_safe(rx_data, temp, &priv->rx_list, list) {
108562306a36Sopenharmony_ci		desc = rx_data->desc;
108662306a36Sopenharmony_ci		skb = rx_data->skb;
108762306a36Sopenharmony_ci		list_del(&rx_data->list);
108862306a36Sopenharmony_ci		kfree(rx_data);
108962306a36Sopenharmony_ci
109062306a36Sopenharmony_ci		orinoco_rx(dev, desc, skb);
109162306a36Sopenharmony_ci
109262306a36Sopenharmony_ci		kfree(desc);
109362306a36Sopenharmony_ci	}
109462306a36Sopenharmony_ci
109562306a36Sopenharmony_ci	orinoco_unlock(priv, &flags);
109662306a36Sopenharmony_ci}
109762306a36Sopenharmony_ci
109862306a36Sopenharmony_ci/********************************************************************/
109962306a36Sopenharmony_ci/* Rx path (info frames)                                            */
110062306a36Sopenharmony_ci/********************************************************************/
110162306a36Sopenharmony_ci
110262306a36Sopenharmony_cistatic void print_linkstatus(struct net_device *dev, u16 status)
110362306a36Sopenharmony_ci{
110462306a36Sopenharmony_ci	char *s;
110562306a36Sopenharmony_ci
110662306a36Sopenharmony_ci	if (suppress_linkstatus)
110762306a36Sopenharmony_ci		return;
110862306a36Sopenharmony_ci
110962306a36Sopenharmony_ci	switch (status) {
111062306a36Sopenharmony_ci	case HERMES_LINKSTATUS_NOT_CONNECTED:
111162306a36Sopenharmony_ci		s = "Not Connected";
111262306a36Sopenharmony_ci		break;
111362306a36Sopenharmony_ci	case HERMES_LINKSTATUS_CONNECTED:
111462306a36Sopenharmony_ci		s = "Connected";
111562306a36Sopenharmony_ci		break;
111662306a36Sopenharmony_ci	case HERMES_LINKSTATUS_DISCONNECTED:
111762306a36Sopenharmony_ci		s = "Disconnected";
111862306a36Sopenharmony_ci		break;
111962306a36Sopenharmony_ci	case HERMES_LINKSTATUS_AP_CHANGE:
112062306a36Sopenharmony_ci		s = "AP Changed";
112162306a36Sopenharmony_ci		break;
112262306a36Sopenharmony_ci	case HERMES_LINKSTATUS_AP_OUT_OF_RANGE:
112362306a36Sopenharmony_ci		s = "AP Out of Range";
112462306a36Sopenharmony_ci		break;
112562306a36Sopenharmony_ci	case HERMES_LINKSTATUS_AP_IN_RANGE:
112662306a36Sopenharmony_ci		s = "AP In Range";
112762306a36Sopenharmony_ci		break;
112862306a36Sopenharmony_ci	case HERMES_LINKSTATUS_ASSOC_FAILED:
112962306a36Sopenharmony_ci		s = "Association Failed";
113062306a36Sopenharmony_ci		break;
113162306a36Sopenharmony_ci	default:
113262306a36Sopenharmony_ci		s = "UNKNOWN";
113362306a36Sopenharmony_ci	}
113462306a36Sopenharmony_ci
113562306a36Sopenharmony_ci	printk(KERN_DEBUG "%s: New link status: %s (%04x)\n",
113662306a36Sopenharmony_ci	       dev->name, s, status);
113762306a36Sopenharmony_ci}
113862306a36Sopenharmony_ci
113962306a36Sopenharmony_ci/* Search scan results for requested BSSID, join it if found */
114062306a36Sopenharmony_cistatic void orinoco_join_ap(struct work_struct *work)
114162306a36Sopenharmony_ci{
114262306a36Sopenharmony_ci	struct orinoco_private *priv =
114362306a36Sopenharmony_ci		container_of(work, struct orinoco_private, join_work);
114462306a36Sopenharmony_ci	struct net_device *dev = priv->ndev;
114562306a36Sopenharmony_ci	struct hermes *hw = &priv->hw;
114662306a36Sopenharmony_ci	int err;
114762306a36Sopenharmony_ci	unsigned long flags;
114862306a36Sopenharmony_ci	struct join_req {
114962306a36Sopenharmony_ci		u8 bssid[ETH_ALEN];
115062306a36Sopenharmony_ci		__le16 channel;
115162306a36Sopenharmony_ci	} __packed req;
115262306a36Sopenharmony_ci	const int atom_len = offsetof(struct prism2_scan_apinfo, atim);
115362306a36Sopenharmony_ci	struct prism2_scan_apinfo *atom = NULL;
115462306a36Sopenharmony_ci	int offset = 4;
115562306a36Sopenharmony_ci	int found = 0;
115662306a36Sopenharmony_ci	u8 *buf;
115762306a36Sopenharmony_ci	u16 len;
115862306a36Sopenharmony_ci
115962306a36Sopenharmony_ci	/* Allocate buffer for scan results */
116062306a36Sopenharmony_ci	buf = kmalloc(MAX_SCAN_LEN, GFP_KERNEL);
116162306a36Sopenharmony_ci	if (!buf)
116262306a36Sopenharmony_ci		return;
116362306a36Sopenharmony_ci
116462306a36Sopenharmony_ci	if (orinoco_lock(priv, &flags) != 0)
116562306a36Sopenharmony_ci		goto fail_lock;
116662306a36Sopenharmony_ci
116762306a36Sopenharmony_ci	/* Sanity checks in case user changed something in the meantime */
116862306a36Sopenharmony_ci	if (!priv->bssid_fixed)
116962306a36Sopenharmony_ci		goto out;
117062306a36Sopenharmony_ci
117162306a36Sopenharmony_ci	if (strlen(priv->desired_essid) == 0)
117262306a36Sopenharmony_ci		goto out;
117362306a36Sopenharmony_ci
117462306a36Sopenharmony_ci	/* Read scan results from the firmware */
117562306a36Sopenharmony_ci	err = hw->ops->read_ltv(hw, USER_BAP,
117662306a36Sopenharmony_ci				HERMES_RID_SCANRESULTSTABLE,
117762306a36Sopenharmony_ci				MAX_SCAN_LEN, &len, buf);
117862306a36Sopenharmony_ci	if (err) {
117962306a36Sopenharmony_ci		printk(KERN_ERR "%s: Cannot read scan results\n",
118062306a36Sopenharmony_ci		       dev->name);
118162306a36Sopenharmony_ci		goto out;
118262306a36Sopenharmony_ci	}
118362306a36Sopenharmony_ci
118462306a36Sopenharmony_ci	len = HERMES_RECLEN_TO_BYTES(len);
118562306a36Sopenharmony_ci
118662306a36Sopenharmony_ci	/* Go through the scan results looking for the channel of the AP
118762306a36Sopenharmony_ci	 * we were requested to join */
118862306a36Sopenharmony_ci	for (; offset + atom_len <= len; offset += atom_len) {
118962306a36Sopenharmony_ci		atom = (struct prism2_scan_apinfo *) (buf + offset);
119062306a36Sopenharmony_ci		if (memcmp(&atom->bssid, priv->desired_bssid, ETH_ALEN) == 0) {
119162306a36Sopenharmony_ci			found = 1;
119262306a36Sopenharmony_ci			break;
119362306a36Sopenharmony_ci		}
119462306a36Sopenharmony_ci	}
119562306a36Sopenharmony_ci
119662306a36Sopenharmony_ci	if (!found) {
119762306a36Sopenharmony_ci		DEBUG(1, "%s: Requested AP not found in scan results\n",
119862306a36Sopenharmony_ci		      dev->name);
119962306a36Sopenharmony_ci		goto out;
120062306a36Sopenharmony_ci	}
120162306a36Sopenharmony_ci
120262306a36Sopenharmony_ci	memcpy(req.bssid, priv->desired_bssid, ETH_ALEN);
120362306a36Sopenharmony_ci	req.channel = atom->channel;	/* both are little-endian */
120462306a36Sopenharmony_ci	err = HERMES_WRITE_RECORD(hw, USER_BAP, HERMES_RID_CNFJOINREQUEST,
120562306a36Sopenharmony_ci				  &req);
120662306a36Sopenharmony_ci	if (err)
120762306a36Sopenharmony_ci		printk(KERN_ERR "%s: Error issuing join request\n", dev->name);
120862306a36Sopenharmony_ci
120962306a36Sopenharmony_ci out:
121062306a36Sopenharmony_ci	orinoco_unlock(priv, &flags);
121162306a36Sopenharmony_ci
121262306a36Sopenharmony_ci fail_lock:
121362306a36Sopenharmony_ci	kfree(buf);
121462306a36Sopenharmony_ci}
121562306a36Sopenharmony_ci
121662306a36Sopenharmony_ci/* Send new BSSID to userspace */
121762306a36Sopenharmony_cistatic void orinoco_send_bssid_wevent(struct orinoco_private *priv)
121862306a36Sopenharmony_ci{
121962306a36Sopenharmony_ci	struct net_device *dev = priv->ndev;
122062306a36Sopenharmony_ci	struct hermes *hw = &priv->hw;
122162306a36Sopenharmony_ci	union iwreq_data wrqu;
122262306a36Sopenharmony_ci	int err;
122362306a36Sopenharmony_ci
122462306a36Sopenharmony_ci	err = hw->ops->read_ltv(hw, USER_BAP, HERMES_RID_CURRENTBSSID,
122562306a36Sopenharmony_ci				ETH_ALEN, NULL, wrqu.ap_addr.sa_data);
122662306a36Sopenharmony_ci	if (err != 0)
122762306a36Sopenharmony_ci		return;
122862306a36Sopenharmony_ci
122962306a36Sopenharmony_ci	wrqu.ap_addr.sa_family = ARPHRD_ETHER;
123062306a36Sopenharmony_ci
123162306a36Sopenharmony_ci	/* Send event to user space */
123262306a36Sopenharmony_ci	wireless_send_event(dev, SIOCGIWAP, &wrqu, NULL);
123362306a36Sopenharmony_ci}
123462306a36Sopenharmony_ci
123562306a36Sopenharmony_cistatic void orinoco_send_assocreqie_wevent(struct orinoco_private *priv)
123662306a36Sopenharmony_ci{
123762306a36Sopenharmony_ci	struct net_device *dev = priv->ndev;
123862306a36Sopenharmony_ci	struct hermes *hw = &priv->hw;
123962306a36Sopenharmony_ci	union iwreq_data wrqu;
124062306a36Sopenharmony_ci	int err;
124162306a36Sopenharmony_ci	u8 buf[88];
124262306a36Sopenharmony_ci	u8 *ie;
124362306a36Sopenharmony_ci
124462306a36Sopenharmony_ci	if (!priv->has_wpa)
124562306a36Sopenharmony_ci		return;
124662306a36Sopenharmony_ci
124762306a36Sopenharmony_ci	err = hw->ops->read_ltv(hw, USER_BAP, HERMES_RID_CURRENT_ASSOC_REQ_INFO,
124862306a36Sopenharmony_ci				sizeof(buf), NULL, &buf);
124962306a36Sopenharmony_ci	if (err != 0)
125062306a36Sopenharmony_ci		return;
125162306a36Sopenharmony_ci
125262306a36Sopenharmony_ci	ie = orinoco_get_wpa_ie(buf, sizeof(buf));
125362306a36Sopenharmony_ci	if (ie) {
125462306a36Sopenharmony_ci		int rem = sizeof(buf) - (ie - &buf[0]);
125562306a36Sopenharmony_ci		wrqu.data.length = ie[1] + 2;
125662306a36Sopenharmony_ci		if (wrqu.data.length > rem)
125762306a36Sopenharmony_ci			wrqu.data.length = rem;
125862306a36Sopenharmony_ci
125962306a36Sopenharmony_ci		if (wrqu.data.length)
126062306a36Sopenharmony_ci			/* Send event to user space */
126162306a36Sopenharmony_ci			wireless_send_event(dev, IWEVASSOCREQIE, &wrqu, ie);
126262306a36Sopenharmony_ci	}
126362306a36Sopenharmony_ci}
126462306a36Sopenharmony_ci
126562306a36Sopenharmony_cistatic void orinoco_send_assocrespie_wevent(struct orinoco_private *priv)
126662306a36Sopenharmony_ci{
126762306a36Sopenharmony_ci	struct net_device *dev = priv->ndev;
126862306a36Sopenharmony_ci	struct hermes *hw = &priv->hw;
126962306a36Sopenharmony_ci	union iwreq_data wrqu;
127062306a36Sopenharmony_ci	int err;
127162306a36Sopenharmony_ci	u8 buf[88]; /* TODO: verify max size or IW_GENERIC_IE_MAX */
127262306a36Sopenharmony_ci	u8 *ie;
127362306a36Sopenharmony_ci
127462306a36Sopenharmony_ci	if (!priv->has_wpa)
127562306a36Sopenharmony_ci		return;
127662306a36Sopenharmony_ci
127762306a36Sopenharmony_ci	err = hw->ops->read_ltv(hw, USER_BAP,
127862306a36Sopenharmony_ci				HERMES_RID_CURRENT_ASSOC_RESP_INFO,
127962306a36Sopenharmony_ci				sizeof(buf), NULL, &buf);
128062306a36Sopenharmony_ci	if (err != 0)
128162306a36Sopenharmony_ci		return;
128262306a36Sopenharmony_ci
128362306a36Sopenharmony_ci	ie = orinoco_get_wpa_ie(buf, sizeof(buf));
128462306a36Sopenharmony_ci	if (ie) {
128562306a36Sopenharmony_ci		int rem = sizeof(buf) - (ie - &buf[0]);
128662306a36Sopenharmony_ci		wrqu.data.length = ie[1] + 2;
128762306a36Sopenharmony_ci		if (wrqu.data.length > rem)
128862306a36Sopenharmony_ci			wrqu.data.length = rem;
128962306a36Sopenharmony_ci
129062306a36Sopenharmony_ci		if (wrqu.data.length)
129162306a36Sopenharmony_ci			/* Send event to user space */
129262306a36Sopenharmony_ci			wireless_send_event(dev, IWEVASSOCRESPIE, &wrqu, ie);
129362306a36Sopenharmony_ci	}
129462306a36Sopenharmony_ci}
129562306a36Sopenharmony_ci
129662306a36Sopenharmony_cistatic void orinoco_send_wevents(struct work_struct *work)
129762306a36Sopenharmony_ci{
129862306a36Sopenharmony_ci	struct orinoco_private *priv =
129962306a36Sopenharmony_ci		container_of(work, struct orinoco_private, wevent_work);
130062306a36Sopenharmony_ci	unsigned long flags;
130162306a36Sopenharmony_ci
130262306a36Sopenharmony_ci	if (orinoco_lock(priv, &flags) != 0)
130362306a36Sopenharmony_ci		return;
130462306a36Sopenharmony_ci
130562306a36Sopenharmony_ci	orinoco_send_assocreqie_wevent(priv);
130662306a36Sopenharmony_ci	orinoco_send_assocrespie_wevent(priv);
130762306a36Sopenharmony_ci	orinoco_send_bssid_wevent(priv);
130862306a36Sopenharmony_ci
130962306a36Sopenharmony_ci	orinoco_unlock(priv, &flags);
131062306a36Sopenharmony_ci}
131162306a36Sopenharmony_ci
131262306a36Sopenharmony_cistatic void qbuf_scan(struct orinoco_private *priv, void *buf,
131362306a36Sopenharmony_ci		      int len, int type)
131462306a36Sopenharmony_ci{
131562306a36Sopenharmony_ci	struct orinoco_scan_data *sd;
131662306a36Sopenharmony_ci	unsigned long flags;
131762306a36Sopenharmony_ci
131862306a36Sopenharmony_ci	sd = kmalloc(sizeof(*sd), GFP_ATOMIC);
131962306a36Sopenharmony_ci	if (!sd)
132062306a36Sopenharmony_ci		return;
132162306a36Sopenharmony_ci
132262306a36Sopenharmony_ci	sd->buf = buf;
132362306a36Sopenharmony_ci	sd->len = len;
132462306a36Sopenharmony_ci	sd->type = type;
132562306a36Sopenharmony_ci
132662306a36Sopenharmony_ci	spin_lock_irqsave(&priv->scan_lock, flags);
132762306a36Sopenharmony_ci	list_add_tail(&sd->list, &priv->scan_list);
132862306a36Sopenharmony_ci	spin_unlock_irqrestore(&priv->scan_lock, flags);
132962306a36Sopenharmony_ci
133062306a36Sopenharmony_ci	schedule_work(&priv->process_scan);
133162306a36Sopenharmony_ci}
133262306a36Sopenharmony_ci
133362306a36Sopenharmony_cistatic void qabort_scan(struct orinoco_private *priv)
133462306a36Sopenharmony_ci{
133562306a36Sopenharmony_ci	struct orinoco_scan_data *sd;
133662306a36Sopenharmony_ci	unsigned long flags;
133762306a36Sopenharmony_ci
133862306a36Sopenharmony_ci	sd = kmalloc(sizeof(*sd), GFP_ATOMIC);
133962306a36Sopenharmony_ci	if (!sd)
134062306a36Sopenharmony_ci		return;
134162306a36Sopenharmony_ci
134262306a36Sopenharmony_ci	sd->len = -1; /* Abort */
134362306a36Sopenharmony_ci
134462306a36Sopenharmony_ci	spin_lock_irqsave(&priv->scan_lock, flags);
134562306a36Sopenharmony_ci	list_add_tail(&sd->list, &priv->scan_list);
134662306a36Sopenharmony_ci	spin_unlock_irqrestore(&priv->scan_lock, flags);
134762306a36Sopenharmony_ci
134862306a36Sopenharmony_ci	schedule_work(&priv->process_scan);
134962306a36Sopenharmony_ci}
135062306a36Sopenharmony_ci
135162306a36Sopenharmony_cistatic void orinoco_process_scan_results(struct work_struct *work)
135262306a36Sopenharmony_ci{
135362306a36Sopenharmony_ci	struct orinoco_private *priv =
135462306a36Sopenharmony_ci		container_of(work, struct orinoco_private, process_scan);
135562306a36Sopenharmony_ci	struct orinoco_scan_data *sd, *temp;
135662306a36Sopenharmony_ci	unsigned long flags;
135762306a36Sopenharmony_ci	void *buf;
135862306a36Sopenharmony_ci	int len;
135962306a36Sopenharmony_ci	int type;
136062306a36Sopenharmony_ci
136162306a36Sopenharmony_ci	spin_lock_irqsave(&priv->scan_lock, flags);
136262306a36Sopenharmony_ci	list_for_each_entry_safe(sd, temp, &priv->scan_list, list) {
136362306a36Sopenharmony_ci
136462306a36Sopenharmony_ci		buf = sd->buf;
136562306a36Sopenharmony_ci		len = sd->len;
136662306a36Sopenharmony_ci		type = sd->type;
136762306a36Sopenharmony_ci
136862306a36Sopenharmony_ci		list_del(&sd->list);
136962306a36Sopenharmony_ci		spin_unlock_irqrestore(&priv->scan_lock, flags);
137062306a36Sopenharmony_ci		kfree(sd);
137162306a36Sopenharmony_ci
137262306a36Sopenharmony_ci		if (len > 0) {
137362306a36Sopenharmony_ci			if (type == HERMES_INQ_CHANNELINFO)
137462306a36Sopenharmony_ci				orinoco_add_extscan_result(priv, buf, len);
137562306a36Sopenharmony_ci			else
137662306a36Sopenharmony_ci				orinoco_add_hostscan_results(priv, buf, len);
137762306a36Sopenharmony_ci
137862306a36Sopenharmony_ci			kfree(buf);
137962306a36Sopenharmony_ci		} else {
138062306a36Sopenharmony_ci			/* Either abort or complete the scan */
138162306a36Sopenharmony_ci			orinoco_scan_done(priv, (len < 0));
138262306a36Sopenharmony_ci		}
138362306a36Sopenharmony_ci
138462306a36Sopenharmony_ci		spin_lock_irqsave(&priv->scan_lock, flags);
138562306a36Sopenharmony_ci	}
138662306a36Sopenharmony_ci	spin_unlock_irqrestore(&priv->scan_lock, flags);
138762306a36Sopenharmony_ci}
138862306a36Sopenharmony_ci
138962306a36Sopenharmony_civoid __orinoco_ev_info(struct net_device *dev, struct hermes *hw)
139062306a36Sopenharmony_ci{
139162306a36Sopenharmony_ci	struct orinoco_private *priv = ndev_priv(dev);
139262306a36Sopenharmony_ci	u16 infofid;
139362306a36Sopenharmony_ci	struct {
139462306a36Sopenharmony_ci		__le16 len;
139562306a36Sopenharmony_ci		__le16 type;
139662306a36Sopenharmony_ci	} __packed info;
139762306a36Sopenharmony_ci	int len, type;
139862306a36Sopenharmony_ci	int err;
139962306a36Sopenharmony_ci
140062306a36Sopenharmony_ci	/* This is an answer to an INQUIRE command that we did earlier,
140162306a36Sopenharmony_ci	 * or an information "event" generated by the card
140262306a36Sopenharmony_ci	 * The controller return to us a pseudo frame containing
140362306a36Sopenharmony_ci	 * the information in question - Jean II */
140462306a36Sopenharmony_ci	infofid = hermes_read_regn(hw, INFOFID);
140562306a36Sopenharmony_ci
140662306a36Sopenharmony_ci	/* Read the info frame header - don't try too hard */
140762306a36Sopenharmony_ci	err = hw->ops->bap_pread(hw, IRQ_BAP, &info, sizeof(info),
140862306a36Sopenharmony_ci				 infofid, 0);
140962306a36Sopenharmony_ci	if (err) {
141062306a36Sopenharmony_ci		printk(KERN_ERR "%s: error %d reading info frame. "
141162306a36Sopenharmony_ci		       "Frame dropped.\n", dev->name, err);
141262306a36Sopenharmony_ci		return;
141362306a36Sopenharmony_ci	}
141462306a36Sopenharmony_ci
141562306a36Sopenharmony_ci	len = HERMES_RECLEN_TO_BYTES(le16_to_cpu(info.len));
141662306a36Sopenharmony_ci	type = le16_to_cpu(info.type);
141762306a36Sopenharmony_ci
141862306a36Sopenharmony_ci	switch (type) {
141962306a36Sopenharmony_ci	case HERMES_INQ_TALLIES: {
142062306a36Sopenharmony_ci		struct hermes_tallies_frame tallies;
142162306a36Sopenharmony_ci		struct iw_statistics *wstats = &priv->wstats;
142262306a36Sopenharmony_ci
142362306a36Sopenharmony_ci		if (len > sizeof(tallies)) {
142462306a36Sopenharmony_ci			printk(KERN_WARNING "%s: Tallies frame too long (%d bytes)\n",
142562306a36Sopenharmony_ci			       dev->name, len);
142662306a36Sopenharmony_ci			len = sizeof(tallies);
142762306a36Sopenharmony_ci		}
142862306a36Sopenharmony_ci
142962306a36Sopenharmony_ci		err = hw->ops->bap_pread(hw, IRQ_BAP, &tallies, len,
143062306a36Sopenharmony_ci					 infofid, sizeof(info));
143162306a36Sopenharmony_ci		if (err)
143262306a36Sopenharmony_ci			break;
143362306a36Sopenharmony_ci
143462306a36Sopenharmony_ci		/* Increment our various counters */
143562306a36Sopenharmony_ci		/* wstats->discard.nwid - no wrong BSSID stuff */
143662306a36Sopenharmony_ci		wstats->discard.code +=
143762306a36Sopenharmony_ci			le16_to_cpu(tallies.RxWEPUndecryptable);
143862306a36Sopenharmony_ci		if (len == sizeof(tallies))
143962306a36Sopenharmony_ci			wstats->discard.code +=
144062306a36Sopenharmony_ci				le16_to_cpu(tallies.RxDiscards_WEPICVError) +
144162306a36Sopenharmony_ci				le16_to_cpu(tallies.RxDiscards_WEPExcluded);
144262306a36Sopenharmony_ci		wstats->discard.misc +=
144362306a36Sopenharmony_ci			le16_to_cpu(tallies.TxDiscardsWrongSA);
144462306a36Sopenharmony_ci		wstats->discard.fragment +=
144562306a36Sopenharmony_ci			le16_to_cpu(tallies.RxMsgInBadMsgFragments);
144662306a36Sopenharmony_ci		wstats->discard.retries +=
144762306a36Sopenharmony_ci			le16_to_cpu(tallies.TxRetryLimitExceeded);
144862306a36Sopenharmony_ci		/* wstats->miss.beacon - no match */
144962306a36Sopenharmony_ci	}
145062306a36Sopenharmony_ci	break;
145162306a36Sopenharmony_ci	case HERMES_INQ_LINKSTATUS: {
145262306a36Sopenharmony_ci		struct hermes_linkstatus linkstatus;
145362306a36Sopenharmony_ci		u16 newstatus;
145462306a36Sopenharmony_ci		int connected;
145562306a36Sopenharmony_ci
145662306a36Sopenharmony_ci		if (priv->iw_mode == NL80211_IFTYPE_MONITOR)
145762306a36Sopenharmony_ci			break;
145862306a36Sopenharmony_ci
145962306a36Sopenharmony_ci		if (len != sizeof(linkstatus)) {
146062306a36Sopenharmony_ci			printk(KERN_WARNING "%s: Unexpected size for linkstatus frame (%d bytes)\n",
146162306a36Sopenharmony_ci			       dev->name, len);
146262306a36Sopenharmony_ci			break;
146362306a36Sopenharmony_ci		}
146462306a36Sopenharmony_ci
146562306a36Sopenharmony_ci		err = hw->ops->bap_pread(hw, IRQ_BAP, &linkstatus, len,
146662306a36Sopenharmony_ci					 infofid, sizeof(info));
146762306a36Sopenharmony_ci		if (err)
146862306a36Sopenharmony_ci			break;
146962306a36Sopenharmony_ci		newstatus = le16_to_cpu(linkstatus.linkstatus);
147062306a36Sopenharmony_ci
147162306a36Sopenharmony_ci		/* Symbol firmware uses "out of range" to signal that
147262306a36Sopenharmony_ci		 * the hostscan frame can be requested.  */
147362306a36Sopenharmony_ci		if (newstatus == HERMES_LINKSTATUS_AP_OUT_OF_RANGE &&
147462306a36Sopenharmony_ci		    priv->firmware_type == FIRMWARE_TYPE_SYMBOL &&
147562306a36Sopenharmony_ci		    priv->has_hostscan && priv->scan_request) {
147662306a36Sopenharmony_ci			hermes_inquire(hw, HERMES_INQ_HOSTSCAN_SYMBOL);
147762306a36Sopenharmony_ci			break;
147862306a36Sopenharmony_ci		}
147962306a36Sopenharmony_ci
148062306a36Sopenharmony_ci		connected = (newstatus == HERMES_LINKSTATUS_CONNECTED)
148162306a36Sopenharmony_ci			|| (newstatus == HERMES_LINKSTATUS_AP_CHANGE)
148262306a36Sopenharmony_ci			|| (newstatus == HERMES_LINKSTATUS_AP_IN_RANGE);
148362306a36Sopenharmony_ci
148462306a36Sopenharmony_ci		if (connected)
148562306a36Sopenharmony_ci			netif_carrier_on(dev);
148662306a36Sopenharmony_ci		else if (!ignore_disconnect)
148762306a36Sopenharmony_ci			netif_carrier_off(dev);
148862306a36Sopenharmony_ci
148962306a36Sopenharmony_ci		if (newstatus != priv->last_linkstatus) {
149062306a36Sopenharmony_ci			priv->last_linkstatus = newstatus;
149162306a36Sopenharmony_ci			print_linkstatus(dev, newstatus);
149262306a36Sopenharmony_ci			/* The info frame contains only one word which is the
149362306a36Sopenharmony_ci			 * status (see hermes.h). The status is pretty boring
149462306a36Sopenharmony_ci			 * in itself, that's why we export the new BSSID...
149562306a36Sopenharmony_ci			 * Jean II */
149662306a36Sopenharmony_ci			schedule_work(&priv->wevent_work);
149762306a36Sopenharmony_ci		}
149862306a36Sopenharmony_ci	}
149962306a36Sopenharmony_ci	break;
150062306a36Sopenharmony_ci	case HERMES_INQ_SCAN:
150162306a36Sopenharmony_ci		if (!priv->scan_request && priv->bssid_fixed &&
150262306a36Sopenharmony_ci		    priv->firmware_type == FIRMWARE_TYPE_INTERSIL) {
150362306a36Sopenharmony_ci			schedule_work(&priv->join_work);
150462306a36Sopenharmony_ci			break;
150562306a36Sopenharmony_ci		}
150662306a36Sopenharmony_ci		fallthrough;
150762306a36Sopenharmony_ci	case HERMES_INQ_HOSTSCAN:
150862306a36Sopenharmony_ci	case HERMES_INQ_HOSTSCAN_SYMBOL: {
150962306a36Sopenharmony_ci		/* Result of a scanning. Contains information about
151062306a36Sopenharmony_ci		 * cells in the vicinity - Jean II */
151162306a36Sopenharmony_ci		unsigned char *buf;
151262306a36Sopenharmony_ci
151362306a36Sopenharmony_ci		/* Sanity check */
151462306a36Sopenharmony_ci		if (len > 4096) {
151562306a36Sopenharmony_ci			printk(KERN_WARNING "%s: Scan results too large (%d bytes)\n",
151662306a36Sopenharmony_ci			       dev->name, len);
151762306a36Sopenharmony_ci			qabort_scan(priv);
151862306a36Sopenharmony_ci			break;
151962306a36Sopenharmony_ci		}
152062306a36Sopenharmony_ci
152162306a36Sopenharmony_ci		/* Allocate buffer for results */
152262306a36Sopenharmony_ci		buf = kmalloc(len, GFP_ATOMIC);
152362306a36Sopenharmony_ci		if (buf == NULL) {
152462306a36Sopenharmony_ci			/* No memory, so can't printk()... */
152562306a36Sopenharmony_ci			qabort_scan(priv);
152662306a36Sopenharmony_ci			break;
152762306a36Sopenharmony_ci		}
152862306a36Sopenharmony_ci
152962306a36Sopenharmony_ci		/* Read scan data */
153062306a36Sopenharmony_ci		err = hw->ops->bap_pread(hw, IRQ_BAP, (void *) buf, len,
153162306a36Sopenharmony_ci					 infofid, sizeof(info));
153262306a36Sopenharmony_ci		if (err) {
153362306a36Sopenharmony_ci			kfree(buf);
153462306a36Sopenharmony_ci			qabort_scan(priv);
153562306a36Sopenharmony_ci			break;
153662306a36Sopenharmony_ci		}
153762306a36Sopenharmony_ci
153862306a36Sopenharmony_ci#ifdef ORINOCO_DEBUG
153962306a36Sopenharmony_ci		{
154062306a36Sopenharmony_ci			int	i;
154162306a36Sopenharmony_ci			printk(KERN_DEBUG "Scan result [%02X", buf[0]);
154262306a36Sopenharmony_ci			for (i = 1; i < (len * 2); i++)
154362306a36Sopenharmony_ci				printk(":%02X", buf[i]);
154462306a36Sopenharmony_ci			printk("]\n");
154562306a36Sopenharmony_ci		}
154662306a36Sopenharmony_ci#endif	/* ORINOCO_DEBUG */
154762306a36Sopenharmony_ci
154862306a36Sopenharmony_ci		qbuf_scan(priv, buf, len, type);
154962306a36Sopenharmony_ci	}
155062306a36Sopenharmony_ci	break;
155162306a36Sopenharmony_ci	case HERMES_INQ_CHANNELINFO:
155262306a36Sopenharmony_ci	{
155362306a36Sopenharmony_ci		struct agere_ext_scan_info *bss;
155462306a36Sopenharmony_ci
155562306a36Sopenharmony_ci		if (!priv->scan_request) {
155662306a36Sopenharmony_ci			printk(KERN_DEBUG "%s: Got chaninfo without scan, "
155762306a36Sopenharmony_ci			       "len=%d\n", dev->name, len);
155862306a36Sopenharmony_ci			break;
155962306a36Sopenharmony_ci		}
156062306a36Sopenharmony_ci
156162306a36Sopenharmony_ci		/* An empty result indicates that the scan is complete */
156262306a36Sopenharmony_ci		if (len == 0) {
156362306a36Sopenharmony_ci			qbuf_scan(priv, NULL, len, type);
156462306a36Sopenharmony_ci			break;
156562306a36Sopenharmony_ci		}
156662306a36Sopenharmony_ci
156762306a36Sopenharmony_ci		/* Sanity check */
156862306a36Sopenharmony_ci		else if (len < (offsetof(struct agere_ext_scan_info,
156962306a36Sopenharmony_ci					   data) + 2)) {
157062306a36Sopenharmony_ci			/* Drop this result now so we don't have to
157162306a36Sopenharmony_ci			 * keep checking later */
157262306a36Sopenharmony_ci			printk(KERN_WARNING
157362306a36Sopenharmony_ci			       "%s: Ext scan results too short (%d bytes)\n",
157462306a36Sopenharmony_ci			       dev->name, len);
157562306a36Sopenharmony_ci			break;
157662306a36Sopenharmony_ci		}
157762306a36Sopenharmony_ci
157862306a36Sopenharmony_ci		bss = kmalloc(len, GFP_ATOMIC);
157962306a36Sopenharmony_ci		if (bss == NULL)
158062306a36Sopenharmony_ci			break;
158162306a36Sopenharmony_ci
158262306a36Sopenharmony_ci		/* Read scan data */
158362306a36Sopenharmony_ci		err = hw->ops->bap_pread(hw, IRQ_BAP, (void *) bss, len,
158462306a36Sopenharmony_ci					 infofid, sizeof(info));
158562306a36Sopenharmony_ci		if (err)
158662306a36Sopenharmony_ci			kfree(bss);
158762306a36Sopenharmony_ci		else
158862306a36Sopenharmony_ci			qbuf_scan(priv, bss, len, type);
158962306a36Sopenharmony_ci
159062306a36Sopenharmony_ci		break;
159162306a36Sopenharmony_ci	}
159262306a36Sopenharmony_ci	case HERMES_INQ_SEC_STAT_AGERE:
159362306a36Sopenharmony_ci		/* Security status (Agere specific) */
159462306a36Sopenharmony_ci		/* Ignore this frame for now */
159562306a36Sopenharmony_ci		if (priv->firmware_type == FIRMWARE_TYPE_AGERE)
159662306a36Sopenharmony_ci			break;
159762306a36Sopenharmony_ci		fallthrough;
159862306a36Sopenharmony_ci	default:
159962306a36Sopenharmony_ci		printk(KERN_DEBUG "%s: Unknown information frame received: "
160062306a36Sopenharmony_ci		       "type 0x%04x, length %d\n", dev->name, type, len);
160162306a36Sopenharmony_ci		/* We don't actually do anything about it */
160262306a36Sopenharmony_ci		break;
160362306a36Sopenharmony_ci	}
160462306a36Sopenharmony_ci}
160562306a36Sopenharmony_ciEXPORT_SYMBOL(__orinoco_ev_info);
160662306a36Sopenharmony_ci
160762306a36Sopenharmony_cistatic void __orinoco_ev_infdrop(struct net_device *dev, struct hermes *hw)
160862306a36Sopenharmony_ci{
160962306a36Sopenharmony_ci	if (net_ratelimit())
161062306a36Sopenharmony_ci		printk(KERN_DEBUG "%s: Information frame lost.\n", dev->name);
161162306a36Sopenharmony_ci}
161262306a36Sopenharmony_ci
161362306a36Sopenharmony_ci/********************************************************************/
161462306a36Sopenharmony_ci/* Internal hardware control routines                               */
161562306a36Sopenharmony_ci/********************************************************************/
161662306a36Sopenharmony_ci
161762306a36Sopenharmony_cistatic int __orinoco_up(struct orinoco_private *priv)
161862306a36Sopenharmony_ci{
161962306a36Sopenharmony_ci	struct net_device *dev = priv->ndev;
162062306a36Sopenharmony_ci	struct hermes *hw = &priv->hw;
162162306a36Sopenharmony_ci	int err;
162262306a36Sopenharmony_ci
162362306a36Sopenharmony_ci	netif_carrier_off(dev); /* just to make sure */
162462306a36Sopenharmony_ci
162562306a36Sopenharmony_ci	err = __orinoco_commit(priv);
162662306a36Sopenharmony_ci	if (err) {
162762306a36Sopenharmony_ci		printk(KERN_ERR "%s: Error %d configuring card\n",
162862306a36Sopenharmony_ci		       dev->name, err);
162962306a36Sopenharmony_ci		return err;
163062306a36Sopenharmony_ci	}
163162306a36Sopenharmony_ci
163262306a36Sopenharmony_ci	/* Fire things up again */
163362306a36Sopenharmony_ci	hermes_set_irqmask(hw, ORINOCO_INTEN);
163462306a36Sopenharmony_ci	err = hermes_enable_port(hw, 0);
163562306a36Sopenharmony_ci	if (err) {
163662306a36Sopenharmony_ci		printk(KERN_ERR "%s: Error %d enabling MAC port\n",
163762306a36Sopenharmony_ci		       dev->name, err);
163862306a36Sopenharmony_ci		return err;
163962306a36Sopenharmony_ci	}
164062306a36Sopenharmony_ci
164162306a36Sopenharmony_ci	netif_start_queue(dev);
164262306a36Sopenharmony_ci
164362306a36Sopenharmony_ci	return 0;
164462306a36Sopenharmony_ci}
164562306a36Sopenharmony_ci
164662306a36Sopenharmony_cistatic int __orinoco_down(struct orinoco_private *priv)
164762306a36Sopenharmony_ci{
164862306a36Sopenharmony_ci	struct net_device *dev = priv->ndev;
164962306a36Sopenharmony_ci	struct hermes *hw = &priv->hw;
165062306a36Sopenharmony_ci	int err;
165162306a36Sopenharmony_ci
165262306a36Sopenharmony_ci	netif_stop_queue(dev);
165362306a36Sopenharmony_ci
165462306a36Sopenharmony_ci	if (!priv->hw_unavailable) {
165562306a36Sopenharmony_ci		if (!priv->broken_disableport) {
165662306a36Sopenharmony_ci			err = hermes_disable_port(hw, 0);
165762306a36Sopenharmony_ci			if (err) {
165862306a36Sopenharmony_ci				/* Some firmwares (e.g. Intersil 1.3.x) seem
165962306a36Sopenharmony_ci				 * to have problems disabling the port, oh
166062306a36Sopenharmony_ci				 * well, too bad. */
166162306a36Sopenharmony_ci				printk(KERN_WARNING "%s: Error %d disabling MAC port\n",
166262306a36Sopenharmony_ci				       dev->name, err);
166362306a36Sopenharmony_ci				priv->broken_disableport = 1;
166462306a36Sopenharmony_ci			}
166562306a36Sopenharmony_ci		}
166662306a36Sopenharmony_ci		hermes_set_irqmask(hw, 0);
166762306a36Sopenharmony_ci		hermes_write_regn(hw, EVACK, 0xffff);
166862306a36Sopenharmony_ci	}
166962306a36Sopenharmony_ci
167062306a36Sopenharmony_ci	orinoco_scan_done(priv, true);
167162306a36Sopenharmony_ci
167262306a36Sopenharmony_ci	/* firmware will have to reassociate */
167362306a36Sopenharmony_ci	netif_carrier_off(dev);
167462306a36Sopenharmony_ci	priv->last_linkstatus = 0xffff;
167562306a36Sopenharmony_ci
167662306a36Sopenharmony_ci	return 0;
167762306a36Sopenharmony_ci}
167862306a36Sopenharmony_ci
167962306a36Sopenharmony_cistatic int orinoco_reinit_firmware(struct orinoco_private *priv)
168062306a36Sopenharmony_ci{
168162306a36Sopenharmony_ci	struct hermes *hw = &priv->hw;
168262306a36Sopenharmony_ci	int err;
168362306a36Sopenharmony_ci
168462306a36Sopenharmony_ci	err = hw->ops->init(hw);
168562306a36Sopenharmony_ci	if (priv->do_fw_download && !err) {
168662306a36Sopenharmony_ci		err = orinoco_download(priv);
168762306a36Sopenharmony_ci		if (err)
168862306a36Sopenharmony_ci			priv->do_fw_download = 0;
168962306a36Sopenharmony_ci	}
169062306a36Sopenharmony_ci	if (!err)
169162306a36Sopenharmony_ci		err = orinoco_hw_allocate_fid(priv);
169262306a36Sopenharmony_ci
169362306a36Sopenharmony_ci	return err;
169462306a36Sopenharmony_ci}
169562306a36Sopenharmony_ci
169662306a36Sopenharmony_cistatic int
169762306a36Sopenharmony_ci__orinoco_set_multicast_list(struct net_device *dev)
169862306a36Sopenharmony_ci{
169962306a36Sopenharmony_ci	struct orinoco_private *priv = ndev_priv(dev);
170062306a36Sopenharmony_ci	int err = 0;
170162306a36Sopenharmony_ci	int promisc, mc_count;
170262306a36Sopenharmony_ci
170362306a36Sopenharmony_ci	/* The Hermes doesn't seem to have an allmulti mode, so we go
170462306a36Sopenharmony_ci	 * into promiscuous mode and let the upper levels deal. */
170562306a36Sopenharmony_ci	if ((dev->flags & IFF_PROMISC) || (dev->flags & IFF_ALLMULTI) ||
170662306a36Sopenharmony_ci	    (netdev_mc_count(dev) > MAX_MULTICAST(priv))) {
170762306a36Sopenharmony_ci		promisc = 1;
170862306a36Sopenharmony_ci		mc_count = 0;
170962306a36Sopenharmony_ci	} else {
171062306a36Sopenharmony_ci		promisc = 0;
171162306a36Sopenharmony_ci		mc_count = netdev_mc_count(dev);
171262306a36Sopenharmony_ci	}
171362306a36Sopenharmony_ci
171462306a36Sopenharmony_ci	err = __orinoco_hw_set_multicast_list(priv, dev, mc_count, promisc);
171562306a36Sopenharmony_ci
171662306a36Sopenharmony_ci	return err;
171762306a36Sopenharmony_ci}
171862306a36Sopenharmony_ci
171962306a36Sopenharmony_ci/* This must be called from user context, without locks held - use
172062306a36Sopenharmony_ci * schedule_work() */
172162306a36Sopenharmony_civoid orinoco_reset(struct work_struct *work)
172262306a36Sopenharmony_ci{
172362306a36Sopenharmony_ci	struct orinoco_private *priv =
172462306a36Sopenharmony_ci		container_of(work, struct orinoco_private, reset_work);
172562306a36Sopenharmony_ci	struct net_device *dev = priv->ndev;
172662306a36Sopenharmony_ci	struct hermes *hw = &priv->hw;
172762306a36Sopenharmony_ci	int err;
172862306a36Sopenharmony_ci	unsigned long flags;
172962306a36Sopenharmony_ci
173062306a36Sopenharmony_ci	if (orinoco_lock(priv, &flags) != 0)
173162306a36Sopenharmony_ci		/* When the hardware becomes available again, whatever
173262306a36Sopenharmony_ci		 * detects that is responsible for re-initializing
173362306a36Sopenharmony_ci		 * it. So no need for anything further */
173462306a36Sopenharmony_ci		return;
173562306a36Sopenharmony_ci
173662306a36Sopenharmony_ci	netif_stop_queue(dev);
173762306a36Sopenharmony_ci
173862306a36Sopenharmony_ci	/* Shut off interrupts.  Depending on what state the hardware
173962306a36Sopenharmony_ci	 * is in, this might not work, but we'll try anyway */
174062306a36Sopenharmony_ci	hermes_set_irqmask(hw, 0);
174162306a36Sopenharmony_ci	hermes_write_regn(hw, EVACK, 0xffff);
174262306a36Sopenharmony_ci
174362306a36Sopenharmony_ci	priv->hw_unavailable++;
174462306a36Sopenharmony_ci	priv->last_linkstatus = 0xffff; /* firmware will have to reassociate */
174562306a36Sopenharmony_ci	netif_carrier_off(dev);
174662306a36Sopenharmony_ci
174762306a36Sopenharmony_ci	orinoco_unlock(priv, &flags);
174862306a36Sopenharmony_ci
174962306a36Sopenharmony_ci	/* Scanning support: Notify scan cancellation */
175062306a36Sopenharmony_ci	orinoco_scan_done(priv, true);
175162306a36Sopenharmony_ci
175262306a36Sopenharmony_ci	if (priv->hard_reset) {
175362306a36Sopenharmony_ci		err = (*priv->hard_reset)(priv);
175462306a36Sopenharmony_ci		if (err) {
175562306a36Sopenharmony_ci			printk(KERN_ERR "%s: orinoco_reset: Error %d "
175662306a36Sopenharmony_ci			       "performing hard reset\n", dev->name, err);
175762306a36Sopenharmony_ci			goto disable;
175862306a36Sopenharmony_ci		}
175962306a36Sopenharmony_ci	}
176062306a36Sopenharmony_ci
176162306a36Sopenharmony_ci	err = orinoco_reinit_firmware(priv);
176262306a36Sopenharmony_ci	if (err) {
176362306a36Sopenharmony_ci		printk(KERN_ERR "%s: orinoco_reset: Error %d re-initializing firmware\n",
176462306a36Sopenharmony_ci		       dev->name, err);
176562306a36Sopenharmony_ci		goto disable;
176662306a36Sopenharmony_ci	}
176762306a36Sopenharmony_ci
176862306a36Sopenharmony_ci	/* This has to be called from user context */
176962306a36Sopenharmony_ci	orinoco_lock_irq(priv);
177062306a36Sopenharmony_ci
177162306a36Sopenharmony_ci	priv->hw_unavailable--;
177262306a36Sopenharmony_ci
177362306a36Sopenharmony_ci	/* priv->open or priv->hw_unavailable might have changed while
177462306a36Sopenharmony_ci	 * we dropped the lock */
177562306a36Sopenharmony_ci	if (priv->open && (!priv->hw_unavailable)) {
177662306a36Sopenharmony_ci		err = __orinoco_up(priv);
177762306a36Sopenharmony_ci		if (err) {
177862306a36Sopenharmony_ci			printk(KERN_ERR "%s: orinoco_reset: Error %d reenabling card\n",
177962306a36Sopenharmony_ci			       dev->name, err);
178062306a36Sopenharmony_ci		} else
178162306a36Sopenharmony_ci			netif_trans_update(dev);
178262306a36Sopenharmony_ci	}
178362306a36Sopenharmony_ci
178462306a36Sopenharmony_ci	orinoco_unlock_irq(priv);
178562306a36Sopenharmony_ci
178662306a36Sopenharmony_ci	return;
178762306a36Sopenharmony_ci disable:
178862306a36Sopenharmony_ci	hermes_set_irqmask(hw, 0);
178962306a36Sopenharmony_ci	netif_device_detach(dev);
179062306a36Sopenharmony_ci	printk(KERN_ERR "%s: Device has been disabled!\n", dev->name);
179162306a36Sopenharmony_ci}
179262306a36Sopenharmony_ci
179362306a36Sopenharmony_cistatic int __orinoco_commit(struct orinoco_private *priv)
179462306a36Sopenharmony_ci{
179562306a36Sopenharmony_ci	struct net_device *dev = priv->ndev;
179662306a36Sopenharmony_ci	int err = 0;
179762306a36Sopenharmony_ci
179862306a36Sopenharmony_ci	/* If we've called commit, we are reconfiguring or bringing the
179962306a36Sopenharmony_ci	 * interface up. Maintaining countermeasures across this would
180062306a36Sopenharmony_ci	 * be confusing, so note that we've disabled them. The port will
180162306a36Sopenharmony_ci	 * be enabled later in orinoco_commit or __orinoco_up. */
180262306a36Sopenharmony_ci	priv->tkip_cm_active = 0;
180362306a36Sopenharmony_ci
180462306a36Sopenharmony_ci	err = orinoco_hw_program_rids(priv);
180562306a36Sopenharmony_ci
180662306a36Sopenharmony_ci	/* FIXME: what about netif_tx_lock */
180762306a36Sopenharmony_ci	(void) __orinoco_set_multicast_list(dev);
180862306a36Sopenharmony_ci
180962306a36Sopenharmony_ci	return err;
181062306a36Sopenharmony_ci}
181162306a36Sopenharmony_ci
181262306a36Sopenharmony_ci/* Ensures configuration changes are applied. May result in a reset.
181362306a36Sopenharmony_ci * The caller should hold priv->lock
181462306a36Sopenharmony_ci */
181562306a36Sopenharmony_ciint orinoco_commit(struct orinoco_private *priv)
181662306a36Sopenharmony_ci{
181762306a36Sopenharmony_ci	struct net_device *dev = priv->ndev;
181862306a36Sopenharmony_ci	struct hermes *hw = &priv->hw;
181962306a36Sopenharmony_ci	int err;
182062306a36Sopenharmony_ci
182162306a36Sopenharmony_ci	if (priv->broken_disableport) {
182262306a36Sopenharmony_ci		schedule_work(&priv->reset_work);
182362306a36Sopenharmony_ci		return 0;
182462306a36Sopenharmony_ci	}
182562306a36Sopenharmony_ci
182662306a36Sopenharmony_ci	err = hermes_disable_port(hw, 0);
182762306a36Sopenharmony_ci	if (err) {
182862306a36Sopenharmony_ci		printk(KERN_WARNING "%s: Unable to disable port "
182962306a36Sopenharmony_ci		       "while reconfiguring card\n", dev->name);
183062306a36Sopenharmony_ci		priv->broken_disableport = 1;
183162306a36Sopenharmony_ci		goto out;
183262306a36Sopenharmony_ci	}
183362306a36Sopenharmony_ci
183462306a36Sopenharmony_ci	err = __orinoco_commit(priv);
183562306a36Sopenharmony_ci	if (err) {
183662306a36Sopenharmony_ci		printk(KERN_WARNING "%s: Unable to reconfigure card\n",
183762306a36Sopenharmony_ci		       dev->name);
183862306a36Sopenharmony_ci		goto out;
183962306a36Sopenharmony_ci	}
184062306a36Sopenharmony_ci
184162306a36Sopenharmony_ci	err = hermes_enable_port(hw, 0);
184262306a36Sopenharmony_ci	if (err) {
184362306a36Sopenharmony_ci		printk(KERN_WARNING "%s: Unable to enable port while reconfiguring card\n",
184462306a36Sopenharmony_ci		       dev->name);
184562306a36Sopenharmony_ci		goto out;
184662306a36Sopenharmony_ci	}
184762306a36Sopenharmony_ci
184862306a36Sopenharmony_ci out:
184962306a36Sopenharmony_ci	if (err) {
185062306a36Sopenharmony_ci		printk(KERN_WARNING "%s: Resetting instead...\n", dev->name);
185162306a36Sopenharmony_ci		schedule_work(&priv->reset_work);
185262306a36Sopenharmony_ci		err = 0;
185362306a36Sopenharmony_ci	}
185462306a36Sopenharmony_ci	return err;
185562306a36Sopenharmony_ci}
185662306a36Sopenharmony_ci
185762306a36Sopenharmony_ci/********************************************************************/
185862306a36Sopenharmony_ci/* Interrupt handler                                                */
185962306a36Sopenharmony_ci/********************************************************************/
186062306a36Sopenharmony_ci
186162306a36Sopenharmony_cistatic void __orinoco_ev_tick(struct net_device *dev, struct hermes *hw)
186262306a36Sopenharmony_ci{
186362306a36Sopenharmony_ci	printk(KERN_DEBUG "%s: TICK\n", dev->name);
186462306a36Sopenharmony_ci}
186562306a36Sopenharmony_ci
186662306a36Sopenharmony_cistatic void __orinoco_ev_wterr(struct net_device *dev, struct hermes *hw)
186762306a36Sopenharmony_ci{
186862306a36Sopenharmony_ci	/* This seems to happen a fair bit under load, but ignoring it
186962306a36Sopenharmony_ci	   seems to work fine...*/
187062306a36Sopenharmony_ci	printk(KERN_DEBUG "%s: MAC controller error (WTERR). Ignoring.\n",
187162306a36Sopenharmony_ci	       dev->name);
187262306a36Sopenharmony_ci}
187362306a36Sopenharmony_ci
187462306a36Sopenharmony_ciirqreturn_t orinoco_interrupt(int irq, void *dev_id)
187562306a36Sopenharmony_ci{
187662306a36Sopenharmony_ci	struct orinoco_private *priv = dev_id;
187762306a36Sopenharmony_ci	struct net_device *dev = priv->ndev;
187862306a36Sopenharmony_ci	struct hermes *hw = &priv->hw;
187962306a36Sopenharmony_ci	int count = MAX_IRQLOOPS_PER_IRQ;
188062306a36Sopenharmony_ci	u16 evstat, events;
188162306a36Sopenharmony_ci	/* These are used to detect a runaway interrupt situation.
188262306a36Sopenharmony_ci	 *
188362306a36Sopenharmony_ci	 * If we get more than MAX_IRQLOOPS_PER_JIFFY iterations in a jiffy,
188462306a36Sopenharmony_ci	 * we panic and shut down the hardware
188562306a36Sopenharmony_ci	 */
188662306a36Sopenharmony_ci	/* jiffies value the last time we were called */
188762306a36Sopenharmony_ci	static int last_irq_jiffy; /* = 0 */
188862306a36Sopenharmony_ci	static int loops_this_jiffy; /* = 0 */
188962306a36Sopenharmony_ci	unsigned long flags;
189062306a36Sopenharmony_ci
189162306a36Sopenharmony_ci	if (orinoco_lock(priv, &flags) != 0) {
189262306a36Sopenharmony_ci		/* If hw is unavailable - we don't know if the irq was
189362306a36Sopenharmony_ci		 * for us or not */
189462306a36Sopenharmony_ci		return IRQ_HANDLED;
189562306a36Sopenharmony_ci	}
189662306a36Sopenharmony_ci
189762306a36Sopenharmony_ci	evstat = hermes_read_regn(hw, EVSTAT);
189862306a36Sopenharmony_ci	events = evstat & hw->inten;
189962306a36Sopenharmony_ci	if (!events) {
190062306a36Sopenharmony_ci		orinoco_unlock(priv, &flags);
190162306a36Sopenharmony_ci		return IRQ_NONE;
190262306a36Sopenharmony_ci	}
190362306a36Sopenharmony_ci
190462306a36Sopenharmony_ci	if (jiffies != last_irq_jiffy)
190562306a36Sopenharmony_ci		loops_this_jiffy = 0;
190662306a36Sopenharmony_ci	last_irq_jiffy = jiffies;
190762306a36Sopenharmony_ci
190862306a36Sopenharmony_ci	while (events && count--) {
190962306a36Sopenharmony_ci		if (++loops_this_jiffy > MAX_IRQLOOPS_PER_JIFFY) {
191062306a36Sopenharmony_ci			printk(KERN_WARNING "%s: IRQ handler is looping too "
191162306a36Sopenharmony_ci			       "much! Resetting.\n", dev->name);
191262306a36Sopenharmony_ci			/* Disable interrupts for now */
191362306a36Sopenharmony_ci			hermes_set_irqmask(hw, 0);
191462306a36Sopenharmony_ci			schedule_work(&priv->reset_work);
191562306a36Sopenharmony_ci			break;
191662306a36Sopenharmony_ci		}
191762306a36Sopenharmony_ci
191862306a36Sopenharmony_ci		/* Check the card hasn't been removed */
191962306a36Sopenharmony_ci		if (!hermes_present(hw)) {
192062306a36Sopenharmony_ci			DEBUG(0, "orinoco_interrupt(): card removed\n");
192162306a36Sopenharmony_ci			break;
192262306a36Sopenharmony_ci		}
192362306a36Sopenharmony_ci
192462306a36Sopenharmony_ci		if (events & HERMES_EV_TICK)
192562306a36Sopenharmony_ci			__orinoco_ev_tick(dev, hw);
192662306a36Sopenharmony_ci		if (events & HERMES_EV_WTERR)
192762306a36Sopenharmony_ci			__orinoco_ev_wterr(dev, hw);
192862306a36Sopenharmony_ci		if (events & HERMES_EV_INFDROP)
192962306a36Sopenharmony_ci			__orinoco_ev_infdrop(dev, hw);
193062306a36Sopenharmony_ci		if (events & HERMES_EV_INFO)
193162306a36Sopenharmony_ci			__orinoco_ev_info(dev, hw);
193262306a36Sopenharmony_ci		if (events & HERMES_EV_RX)
193362306a36Sopenharmony_ci			__orinoco_ev_rx(dev, hw);
193462306a36Sopenharmony_ci		if (events & HERMES_EV_TXEXC)
193562306a36Sopenharmony_ci			__orinoco_ev_txexc(dev, hw);
193662306a36Sopenharmony_ci		if (events & HERMES_EV_TX)
193762306a36Sopenharmony_ci			__orinoco_ev_tx(dev, hw);
193862306a36Sopenharmony_ci		if (events & HERMES_EV_ALLOC)
193962306a36Sopenharmony_ci			__orinoco_ev_alloc(dev, hw);
194062306a36Sopenharmony_ci
194162306a36Sopenharmony_ci		hermes_write_regn(hw, EVACK, evstat);
194262306a36Sopenharmony_ci
194362306a36Sopenharmony_ci		evstat = hermes_read_regn(hw, EVSTAT);
194462306a36Sopenharmony_ci		events = evstat & hw->inten;
194562306a36Sopenharmony_ci	}
194662306a36Sopenharmony_ci
194762306a36Sopenharmony_ci	orinoco_unlock(priv, &flags);
194862306a36Sopenharmony_ci	return IRQ_HANDLED;
194962306a36Sopenharmony_ci}
195062306a36Sopenharmony_ciEXPORT_SYMBOL(orinoco_interrupt);
195162306a36Sopenharmony_ci
195262306a36Sopenharmony_ci/********************************************************************/
195362306a36Sopenharmony_ci/* Power management                                                 */
195462306a36Sopenharmony_ci/********************************************************************/
195562306a36Sopenharmony_ci#if defined(CONFIG_PM_SLEEP) && !defined(CONFIG_HERMES_CACHE_FW_ON_INIT)
195662306a36Sopenharmony_cistatic int orinoco_pm_notifier(struct notifier_block *notifier,
195762306a36Sopenharmony_ci			       unsigned long pm_event,
195862306a36Sopenharmony_ci			       void *unused)
195962306a36Sopenharmony_ci{
196062306a36Sopenharmony_ci	struct orinoco_private *priv = container_of(notifier,
196162306a36Sopenharmony_ci						    struct orinoco_private,
196262306a36Sopenharmony_ci						    pm_notifier);
196362306a36Sopenharmony_ci
196462306a36Sopenharmony_ci	/* All we need to do is cache the firmware before suspend, and
196562306a36Sopenharmony_ci	 * release it when we come out.
196662306a36Sopenharmony_ci	 *
196762306a36Sopenharmony_ci	 * Only need to do this if we're downloading firmware. */
196862306a36Sopenharmony_ci	if (!priv->do_fw_download)
196962306a36Sopenharmony_ci		return NOTIFY_DONE;
197062306a36Sopenharmony_ci
197162306a36Sopenharmony_ci	switch (pm_event) {
197262306a36Sopenharmony_ci	case PM_HIBERNATION_PREPARE:
197362306a36Sopenharmony_ci	case PM_SUSPEND_PREPARE:
197462306a36Sopenharmony_ci		orinoco_cache_fw(priv, 0);
197562306a36Sopenharmony_ci		break;
197662306a36Sopenharmony_ci
197762306a36Sopenharmony_ci	case PM_POST_RESTORE:
197862306a36Sopenharmony_ci		/* Restore from hibernation failed. We need to clean
197962306a36Sopenharmony_ci		 * up in exactly the same way, so fall through. */
198062306a36Sopenharmony_ci	case PM_POST_HIBERNATION:
198162306a36Sopenharmony_ci	case PM_POST_SUSPEND:
198262306a36Sopenharmony_ci		orinoco_uncache_fw(priv);
198362306a36Sopenharmony_ci		break;
198462306a36Sopenharmony_ci
198562306a36Sopenharmony_ci	case PM_RESTORE_PREPARE:
198662306a36Sopenharmony_ci	default:
198762306a36Sopenharmony_ci		break;
198862306a36Sopenharmony_ci	}
198962306a36Sopenharmony_ci
199062306a36Sopenharmony_ci	return NOTIFY_DONE;
199162306a36Sopenharmony_ci}
199262306a36Sopenharmony_ci
199362306a36Sopenharmony_cistatic void orinoco_register_pm_notifier(struct orinoco_private *priv)
199462306a36Sopenharmony_ci{
199562306a36Sopenharmony_ci	priv->pm_notifier.notifier_call = orinoco_pm_notifier;
199662306a36Sopenharmony_ci	register_pm_notifier(&priv->pm_notifier);
199762306a36Sopenharmony_ci}
199862306a36Sopenharmony_ci
199962306a36Sopenharmony_cistatic void orinoco_unregister_pm_notifier(struct orinoco_private *priv)
200062306a36Sopenharmony_ci{
200162306a36Sopenharmony_ci	unregister_pm_notifier(&priv->pm_notifier);
200262306a36Sopenharmony_ci}
200362306a36Sopenharmony_ci#else /* !PM_SLEEP || HERMES_CACHE_FW_ON_INIT */
200462306a36Sopenharmony_ci#define orinoco_register_pm_notifier(priv) do { } while (0)
200562306a36Sopenharmony_ci#define orinoco_unregister_pm_notifier(priv) do { } while (0)
200662306a36Sopenharmony_ci#endif
200762306a36Sopenharmony_ci
200862306a36Sopenharmony_ci/********************************************************************/
200962306a36Sopenharmony_ci/* Initialization                                                   */
201062306a36Sopenharmony_ci/********************************************************************/
201162306a36Sopenharmony_ci
201262306a36Sopenharmony_ciint orinoco_init(struct orinoco_private *priv)
201362306a36Sopenharmony_ci{
201462306a36Sopenharmony_ci	struct device *dev = priv->dev;
201562306a36Sopenharmony_ci	struct wiphy *wiphy = priv_to_wiphy(priv);
201662306a36Sopenharmony_ci	struct hermes *hw = &priv->hw;
201762306a36Sopenharmony_ci	int err = 0;
201862306a36Sopenharmony_ci
201962306a36Sopenharmony_ci	/* No need to lock, the hw_unavailable flag is already set in
202062306a36Sopenharmony_ci	 * alloc_orinocodev() */
202162306a36Sopenharmony_ci	priv->nicbuf_size = IEEE80211_MAX_FRAME_LEN + ETH_HLEN;
202262306a36Sopenharmony_ci
202362306a36Sopenharmony_ci	/* Initialize the firmware */
202462306a36Sopenharmony_ci	err = hw->ops->init(hw);
202562306a36Sopenharmony_ci	if (err != 0) {
202662306a36Sopenharmony_ci		dev_err(dev, "Failed to initialize firmware (err = %d)\n",
202762306a36Sopenharmony_ci			err);
202862306a36Sopenharmony_ci		goto out;
202962306a36Sopenharmony_ci	}
203062306a36Sopenharmony_ci
203162306a36Sopenharmony_ci	err = determine_fw_capabilities(priv, wiphy->fw_version,
203262306a36Sopenharmony_ci					sizeof(wiphy->fw_version),
203362306a36Sopenharmony_ci					&wiphy->hw_version);
203462306a36Sopenharmony_ci	if (err != 0) {
203562306a36Sopenharmony_ci		dev_err(dev, "Incompatible firmware, aborting\n");
203662306a36Sopenharmony_ci		goto out;
203762306a36Sopenharmony_ci	}
203862306a36Sopenharmony_ci
203962306a36Sopenharmony_ci	if (priv->do_fw_download) {
204062306a36Sopenharmony_ci#ifdef CONFIG_HERMES_CACHE_FW_ON_INIT
204162306a36Sopenharmony_ci		orinoco_cache_fw(priv, 0);
204262306a36Sopenharmony_ci#endif
204362306a36Sopenharmony_ci
204462306a36Sopenharmony_ci		err = orinoco_download(priv);
204562306a36Sopenharmony_ci		if (err)
204662306a36Sopenharmony_ci			priv->do_fw_download = 0;
204762306a36Sopenharmony_ci
204862306a36Sopenharmony_ci		/* Check firmware version again */
204962306a36Sopenharmony_ci		err = determine_fw_capabilities(priv, wiphy->fw_version,
205062306a36Sopenharmony_ci						sizeof(wiphy->fw_version),
205162306a36Sopenharmony_ci						&wiphy->hw_version);
205262306a36Sopenharmony_ci		if (err != 0) {
205362306a36Sopenharmony_ci			dev_err(dev, "Incompatible firmware, aborting\n");
205462306a36Sopenharmony_ci			goto out;
205562306a36Sopenharmony_ci		}
205662306a36Sopenharmony_ci	}
205762306a36Sopenharmony_ci
205862306a36Sopenharmony_ci	if (priv->has_port3)
205962306a36Sopenharmony_ci		dev_info(dev, "Ad-hoc demo mode supported\n");
206062306a36Sopenharmony_ci	if (priv->has_ibss)
206162306a36Sopenharmony_ci		dev_info(dev, "IEEE standard IBSS ad-hoc mode supported\n");
206262306a36Sopenharmony_ci	if (priv->has_wep)
206362306a36Sopenharmony_ci		dev_info(dev, "WEP supported, %s-bit key\n",
206462306a36Sopenharmony_ci			 priv->has_big_wep ? "104" : "40");
206562306a36Sopenharmony_ci	if (priv->has_wpa) {
206662306a36Sopenharmony_ci		dev_info(dev, "WPA-PSK supported\n");
206762306a36Sopenharmony_ci		if (orinoco_mic_init(priv)) {
206862306a36Sopenharmony_ci			dev_err(dev, "Failed to setup MIC crypto algorithm. "
206962306a36Sopenharmony_ci				"Disabling WPA support\n");
207062306a36Sopenharmony_ci			priv->has_wpa = 0;
207162306a36Sopenharmony_ci		}
207262306a36Sopenharmony_ci	}
207362306a36Sopenharmony_ci
207462306a36Sopenharmony_ci	err = orinoco_hw_read_card_settings(priv, wiphy->perm_addr);
207562306a36Sopenharmony_ci	if (err)
207662306a36Sopenharmony_ci		goto out;
207762306a36Sopenharmony_ci
207862306a36Sopenharmony_ci	err = orinoco_hw_allocate_fid(priv);
207962306a36Sopenharmony_ci	if (err) {
208062306a36Sopenharmony_ci		dev_err(dev, "Failed to allocate NIC buffer!\n");
208162306a36Sopenharmony_ci		goto out;
208262306a36Sopenharmony_ci	}
208362306a36Sopenharmony_ci
208462306a36Sopenharmony_ci	/* Set up the default configuration */
208562306a36Sopenharmony_ci	priv->iw_mode = NL80211_IFTYPE_STATION;
208662306a36Sopenharmony_ci	/* By default use IEEE/IBSS ad-hoc mode if we have it */
208762306a36Sopenharmony_ci	priv->prefer_port3 = priv->has_port3 && (!priv->has_ibss);
208862306a36Sopenharmony_ci	set_port_type(priv);
208962306a36Sopenharmony_ci	priv->channel = 0; /* use firmware default */
209062306a36Sopenharmony_ci
209162306a36Sopenharmony_ci	priv->promiscuous = 0;
209262306a36Sopenharmony_ci	priv->encode_alg = ORINOCO_ALG_NONE;
209362306a36Sopenharmony_ci	priv->tx_key = 0;
209462306a36Sopenharmony_ci	priv->wpa_enabled = 0;
209562306a36Sopenharmony_ci	priv->tkip_cm_active = 0;
209662306a36Sopenharmony_ci	priv->key_mgmt = 0;
209762306a36Sopenharmony_ci	priv->wpa_ie_len = 0;
209862306a36Sopenharmony_ci	priv->wpa_ie = NULL;
209962306a36Sopenharmony_ci
210062306a36Sopenharmony_ci	if (orinoco_wiphy_register(wiphy)) {
210162306a36Sopenharmony_ci		err = -ENODEV;
210262306a36Sopenharmony_ci		goto out;
210362306a36Sopenharmony_ci	}
210462306a36Sopenharmony_ci
210562306a36Sopenharmony_ci	/* Make the hardware available, as long as it hasn't been
210662306a36Sopenharmony_ci	 * removed elsewhere (e.g. by PCMCIA hot unplug) */
210762306a36Sopenharmony_ci	orinoco_lock_irq(priv);
210862306a36Sopenharmony_ci	priv->hw_unavailable--;
210962306a36Sopenharmony_ci	orinoco_unlock_irq(priv);
211062306a36Sopenharmony_ci
211162306a36Sopenharmony_ci	dev_dbg(dev, "Ready\n");
211262306a36Sopenharmony_ci
211362306a36Sopenharmony_ci out:
211462306a36Sopenharmony_ci	return err;
211562306a36Sopenharmony_ci}
211662306a36Sopenharmony_ciEXPORT_SYMBOL(orinoco_init);
211762306a36Sopenharmony_ci
211862306a36Sopenharmony_cistatic const struct net_device_ops orinoco_netdev_ops = {
211962306a36Sopenharmony_ci	.ndo_open		= orinoco_open,
212062306a36Sopenharmony_ci	.ndo_stop		= orinoco_stop,
212162306a36Sopenharmony_ci	.ndo_start_xmit		= orinoco_xmit,
212262306a36Sopenharmony_ci	.ndo_set_rx_mode	= orinoco_set_multicast_list,
212362306a36Sopenharmony_ci	.ndo_change_mtu		= orinoco_change_mtu,
212462306a36Sopenharmony_ci	.ndo_set_mac_address	= eth_mac_addr,
212562306a36Sopenharmony_ci	.ndo_validate_addr	= eth_validate_addr,
212662306a36Sopenharmony_ci	.ndo_tx_timeout		= orinoco_tx_timeout,
212762306a36Sopenharmony_ci};
212862306a36Sopenharmony_ci
212962306a36Sopenharmony_ci/* Allocate private data.
213062306a36Sopenharmony_ci *
213162306a36Sopenharmony_ci * This driver has a number of structures associated with it
213262306a36Sopenharmony_ci *  netdev - Net device structure for each network interface
213362306a36Sopenharmony_ci *  wiphy - structure associated with wireless phy
213462306a36Sopenharmony_ci *  wireless_dev (wdev) - structure for each wireless interface
213562306a36Sopenharmony_ci *  hw - structure for hermes chip info
213662306a36Sopenharmony_ci *  card - card specific structure for use by the card driver
213762306a36Sopenharmony_ci *         (airport, orinoco_cs)
213862306a36Sopenharmony_ci *  priv - orinoco private data
213962306a36Sopenharmony_ci *  device - generic linux device structure
214062306a36Sopenharmony_ci *
214162306a36Sopenharmony_ci *  +---------+    +---------+
214262306a36Sopenharmony_ci *  |  wiphy  |    | netdev  |
214362306a36Sopenharmony_ci *  | +-------+    | +-------+
214462306a36Sopenharmony_ci *  | | priv  |    | | wdev  |
214562306a36Sopenharmony_ci *  | | +-----+    +-+-------+
214662306a36Sopenharmony_ci *  | | | hw  |
214762306a36Sopenharmony_ci *  | +-+-----+
214862306a36Sopenharmony_ci *  | | card  |
214962306a36Sopenharmony_ci *  +-+-------+
215062306a36Sopenharmony_ci *
215162306a36Sopenharmony_ci * priv has a link to netdev and device
215262306a36Sopenharmony_ci * wdev has a link to wiphy
215362306a36Sopenharmony_ci */
215462306a36Sopenharmony_cistruct orinoco_private
215562306a36Sopenharmony_ci*alloc_orinocodev(int sizeof_card,
215662306a36Sopenharmony_ci		  struct device *device,
215762306a36Sopenharmony_ci		  int (*hard_reset)(struct orinoco_private *),
215862306a36Sopenharmony_ci		  int (*stop_fw)(struct orinoco_private *, int))
215962306a36Sopenharmony_ci{
216062306a36Sopenharmony_ci	struct orinoco_private *priv;
216162306a36Sopenharmony_ci	struct wiphy *wiphy;
216262306a36Sopenharmony_ci
216362306a36Sopenharmony_ci	/* allocate wiphy
216462306a36Sopenharmony_ci	 * NOTE: We only support a single virtual interface
216562306a36Sopenharmony_ci	 *       but this may change when monitor mode is added
216662306a36Sopenharmony_ci	 */
216762306a36Sopenharmony_ci	wiphy = wiphy_new(&orinoco_cfg_ops,
216862306a36Sopenharmony_ci			  sizeof(struct orinoco_private) + sizeof_card);
216962306a36Sopenharmony_ci	if (!wiphy)
217062306a36Sopenharmony_ci		return NULL;
217162306a36Sopenharmony_ci
217262306a36Sopenharmony_ci	priv = wiphy_priv(wiphy);
217362306a36Sopenharmony_ci	priv->dev = device;
217462306a36Sopenharmony_ci
217562306a36Sopenharmony_ci	if (sizeof_card)
217662306a36Sopenharmony_ci		priv->card = (void *)((unsigned long)priv
217762306a36Sopenharmony_ci				      + sizeof(struct orinoco_private));
217862306a36Sopenharmony_ci	else
217962306a36Sopenharmony_ci		priv->card = NULL;
218062306a36Sopenharmony_ci
218162306a36Sopenharmony_ci	orinoco_wiphy_init(wiphy);
218262306a36Sopenharmony_ci
218362306a36Sopenharmony_ci#ifdef WIRELESS_SPY
218462306a36Sopenharmony_ci	priv->wireless_data.spy_data = &priv->spy_data;
218562306a36Sopenharmony_ci#endif
218662306a36Sopenharmony_ci
218762306a36Sopenharmony_ci	/* Set up default callbacks */
218862306a36Sopenharmony_ci	priv->hard_reset = hard_reset;
218962306a36Sopenharmony_ci	priv->stop_fw = stop_fw;
219062306a36Sopenharmony_ci
219162306a36Sopenharmony_ci	spin_lock_init(&priv->lock);
219262306a36Sopenharmony_ci	priv->open = 0;
219362306a36Sopenharmony_ci	priv->hw_unavailable = 1; /* orinoco_init() must clear this
219462306a36Sopenharmony_ci				   * before anything else touches the
219562306a36Sopenharmony_ci				   * hardware */
219662306a36Sopenharmony_ci	INIT_WORK(&priv->reset_work, orinoco_reset);
219762306a36Sopenharmony_ci	INIT_WORK(&priv->join_work, orinoco_join_ap);
219862306a36Sopenharmony_ci	INIT_WORK(&priv->wevent_work, orinoco_send_wevents);
219962306a36Sopenharmony_ci
220062306a36Sopenharmony_ci	INIT_LIST_HEAD(&priv->rx_list);
220162306a36Sopenharmony_ci	tasklet_setup(&priv->rx_tasklet, orinoco_rx_isr_tasklet);
220262306a36Sopenharmony_ci
220362306a36Sopenharmony_ci	spin_lock_init(&priv->scan_lock);
220462306a36Sopenharmony_ci	INIT_LIST_HEAD(&priv->scan_list);
220562306a36Sopenharmony_ci	INIT_WORK(&priv->process_scan, orinoco_process_scan_results);
220662306a36Sopenharmony_ci
220762306a36Sopenharmony_ci	priv->last_linkstatus = 0xffff;
220862306a36Sopenharmony_ci
220962306a36Sopenharmony_ci#if defined(CONFIG_HERMES_CACHE_FW_ON_INIT) || defined(CONFIG_PM_SLEEP)
221062306a36Sopenharmony_ci	priv->cached_pri_fw = NULL;
221162306a36Sopenharmony_ci	priv->cached_fw = NULL;
221262306a36Sopenharmony_ci#endif
221362306a36Sopenharmony_ci
221462306a36Sopenharmony_ci	/* Register PM notifiers */
221562306a36Sopenharmony_ci	orinoco_register_pm_notifier(priv);
221662306a36Sopenharmony_ci
221762306a36Sopenharmony_ci	return priv;
221862306a36Sopenharmony_ci}
221962306a36Sopenharmony_ciEXPORT_SYMBOL(alloc_orinocodev);
222062306a36Sopenharmony_ci
222162306a36Sopenharmony_ci/* We can only support a single interface. We provide a separate
222262306a36Sopenharmony_ci * function to set it up to distinguish between hardware
222362306a36Sopenharmony_ci * initialisation and interface setup.
222462306a36Sopenharmony_ci *
222562306a36Sopenharmony_ci * The base_addr and irq parameters are passed on to netdev for use
222662306a36Sopenharmony_ci * with SIOCGIFMAP.
222762306a36Sopenharmony_ci */
222862306a36Sopenharmony_ciint orinoco_if_add(struct orinoco_private *priv,
222962306a36Sopenharmony_ci		   unsigned long base_addr,
223062306a36Sopenharmony_ci		   unsigned int irq,
223162306a36Sopenharmony_ci		   const struct net_device_ops *ops)
223262306a36Sopenharmony_ci{
223362306a36Sopenharmony_ci	struct wiphy *wiphy = priv_to_wiphy(priv);
223462306a36Sopenharmony_ci	struct wireless_dev *wdev;
223562306a36Sopenharmony_ci	struct net_device *dev;
223662306a36Sopenharmony_ci	int ret;
223762306a36Sopenharmony_ci
223862306a36Sopenharmony_ci	dev = alloc_etherdev(sizeof(struct wireless_dev));
223962306a36Sopenharmony_ci
224062306a36Sopenharmony_ci	if (!dev)
224162306a36Sopenharmony_ci		return -ENOMEM;
224262306a36Sopenharmony_ci
224362306a36Sopenharmony_ci	/* Initialise wireless_dev */
224462306a36Sopenharmony_ci	wdev = netdev_priv(dev);
224562306a36Sopenharmony_ci	wdev->wiphy = wiphy;
224662306a36Sopenharmony_ci	wdev->iftype = NL80211_IFTYPE_STATION;
224762306a36Sopenharmony_ci
224862306a36Sopenharmony_ci	/* Setup / override net_device fields */
224962306a36Sopenharmony_ci	dev->ieee80211_ptr = wdev;
225062306a36Sopenharmony_ci	dev->watchdog_timeo = HZ; /* 1 second timeout */
225162306a36Sopenharmony_ci	dev->wireless_handlers = &orinoco_handler_def;
225262306a36Sopenharmony_ci#ifdef WIRELESS_SPY
225362306a36Sopenharmony_ci	dev->wireless_data = &priv->wireless_data;
225462306a36Sopenharmony_ci#endif
225562306a36Sopenharmony_ci	/* Default to standard ops if not set */
225662306a36Sopenharmony_ci	if (ops)
225762306a36Sopenharmony_ci		dev->netdev_ops = ops;
225862306a36Sopenharmony_ci	else
225962306a36Sopenharmony_ci		dev->netdev_ops = &orinoco_netdev_ops;
226062306a36Sopenharmony_ci
226162306a36Sopenharmony_ci	/* we use the default eth_mac_addr for setting the MAC addr */
226262306a36Sopenharmony_ci
226362306a36Sopenharmony_ci	/* Reserve space in skb for the SNAP header */
226462306a36Sopenharmony_ci	dev->needed_headroom = ENCAPS_OVERHEAD;
226562306a36Sopenharmony_ci
226662306a36Sopenharmony_ci	netif_carrier_off(dev);
226762306a36Sopenharmony_ci
226862306a36Sopenharmony_ci	eth_hw_addr_set(dev, wiphy->perm_addr);
226962306a36Sopenharmony_ci
227062306a36Sopenharmony_ci	dev->base_addr = base_addr;
227162306a36Sopenharmony_ci	dev->irq = irq;
227262306a36Sopenharmony_ci
227362306a36Sopenharmony_ci	dev->min_mtu = ORINOCO_MIN_MTU;
227462306a36Sopenharmony_ci	dev->max_mtu = ORINOCO_MAX_MTU;
227562306a36Sopenharmony_ci
227662306a36Sopenharmony_ci	SET_NETDEV_DEV(dev, priv->dev);
227762306a36Sopenharmony_ci	ret = register_netdev(dev);
227862306a36Sopenharmony_ci	if (ret)
227962306a36Sopenharmony_ci		goto fail;
228062306a36Sopenharmony_ci
228162306a36Sopenharmony_ci	priv->ndev = dev;
228262306a36Sopenharmony_ci
228362306a36Sopenharmony_ci	/* Report what we've done */
228462306a36Sopenharmony_ci	dev_dbg(priv->dev, "Registered interface %s.\n", dev->name);
228562306a36Sopenharmony_ci
228662306a36Sopenharmony_ci	return 0;
228762306a36Sopenharmony_ci
228862306a36Sopenharmony_ci fail:
228962306a36Sopenharmony_ci	free_netdev(dev);
229062306a36Sopenharmony_ci	return ret;
229162306a36Sopenharmony_ci}
229262306a36Sopenharmony_ciEXPORT_SYMBOL(orinoco_if_add);
229362306a36Sopenharmony_ci
229462306a36Sopenharmony_civoid orinoco_if_del(struct orinoco_private *priv)
229562306a36Sopenharmony_ci{
229662306a36Sopenharmony_ci	struct net_device *dev = priv->ndev;
229762306a36Sopenharmony_ci
229862306a36Sopenharmony_ci	unregister_netdev(dev);
229962306a36Sopenharmony_ci	free_netdev(dev);
230062306a36Sopenharmony_ci}
230162306a36Sopenharmony_ciEXPORT_SYMBOL(orinoco_if_del);
230262306a36Sopenharmony_ci
230362306a36Sopenharmony_civoid free_orinocodev(struct orinoco_private *priv)
230462306a36Sopenharmony_ci{
230562306a36Sopenharmony_ci	struct wiphy *wiphy = priv_to_wiphy(priv);
230662306a36Sopenharmony_ci	struct orinoco_rx_data *rx_data, *temp;
230762306a36Sopenharmony_ci	struct orinoco_scan_data *sd, *sdtemp;
230862306a36Sopenharmony_ci
230962306a36Sopenharmony_ci	/* If the tasklet is scheduled when we call tasklet_kill it
231062306a36Sopenharmony_ci	 * will run one final time. However the tasklet will only
231162306a36Sopenharmony_ci	 * drain priv->rx_list if the hw is still available. */
231262306a36Sopenharmony_ci	tasklet_kill(&priv->rx_tasklet);
231362306a36Sopenharmony_ci
231462306a36Sopenharmony_ci	/* Explicitly drain priv->rx_list */
231562306a36Sopenharmony_ci	list_for_each_entry_safe(rx_data, temp, &priv->rx_list, list) {
231662306a36Sopenharmony_ci		list_del(&rx_data->list);
231762306a36Sopenharmony_ci
231862306a36Sopenharmony_ci		dev_kfree_skb(rx_data->skb);
231962306a36Sopenharmony_ci		kfree(rx_data->desc);
232062306a36Sopenharmony_ci		kfree(rx_data);
232162306a36Sopenharmony_ci	}
232262306a36Sopenharmony_ci
232362306a36Sopenharmony_ci	cancel_work_sync(&priv->process_scan);
232462306a36Sopenharmony_ci	/* Explicitly drain priv->scan_list */
232562306a36Sopenharmony_ci	list_for_each_entry_safe(sd, sdtemp, &priv->scan_list, list) {
232662306a36Sopenharmony_ci		list_del(&sd->list);
232762306a36Sopenharmony_ci
232862306a36Sopenharmony_ci		if (sd->len > 0)
232962306a36Sopenharmony_ci			kfree(sd->buf);
233062306a36Sopenharmony_ci		kfree(sd);
233162306a36Sopenharmony_ci	}
233262306a36Sopenharmony_ci
233362306a36Sopenharmony_ci	orinoco_unregister_pm_notifier(priv);
233462306a36Sopenharmony_ci	orinoco_uncache_fw(priv);
233562306a36Sopenharmony_ci
233662306a36Sopenharmony_ci	priv->wpa_ie_len = 0;
233762306a36Sopenharmony_ci	kfree(priv->wpa_ie);
233862306a36Sopenharmony_ci	orinoco_mic_free(priv);
233962306a36Sopenharmony_ci	wiphy_free(wiphy);
234062306a36Sopenharmony_ci}
234162306a36Sopenharmony_ciEXPORT_SYMBOL(free_orinocodev);
234262306a36Sopenharmony_ci
234362306a36Sopenharmony_ciint orinoco_up(struct orinoco_private *priv)
234462306a36Sopenharmony_ci{
234562306a36Sopenharmony_ci	struct net_device *dev = priv->ndev;
234662306a36Sopenharmony_ci	unsigned long flags;
234762306a36Sopenharmony_ci	int err;
234862306a36Sopenharmony_ci
234962306a36Sopenharmony_ci	priv->hw.ops->lock_irqsave(&priv->lock, &flags);
235062306a36Sopenharmony_ci
235162306a36Sopenharmony_ci	err = orinoco_reinit_firmware(priv);
235262306a36Sopenharmony_ci	if (err) {
235362306a36Sopenharmony_ci		printk(KERN_ERR "%s: Error %d re-initializing firmware\n",
235462306a36Sopenharmony_ci		       dev->name, err);
235562306a36Sopenharmony_ci		goto exit;
235662306a36Sopenharmony_ci	}
235762306a36Sopenharmony_ci
235862306a36Sopenharmony_ci	netif_device_attach(dev);
235962306a36Sopenharmony_ci	priv->hw_unavailable--;
236062306a36Sopenharmony_ci
236162306a36Sopenharmony_ci	if (priv->open && !priv->hw_unavailable) {
236262306a36Sopenharmony_ci		err = __orinoco_up(priv);
236362306a36Sopenharmony_ci		if (err)
236462306a36Sopenharmony_ci			printk(KERN_ERR "%s: Error %d restarting card\n",
236562306a36Sopenharmony_ci			       dev->name, err);
236662306a36Sopenharmony_ci	}
236762306a36Sopenharmony_ci
236862306a36Sopenharmony_ciexit:
236962306a36Sopenharmony_ci	priv->hw.ops->unlock_irqrestore(&priv->lock, &flags);
237062306a36Sopenharmony_ci
237162306a36Sopenharmony_ci	return 0;
237262306a36Sopenharmony_ci}
237362306a36Sopenharmony_ciEXPORT_SYMBOL(orinoco_up);
237462306a36Sopenharmony_ci
237562306a36Sopenharmony_civoid orinoco_down(struct orinoco_private *priv)
237662306a36Sopenharmony_ci{
237762306a36Sopenharmony_ci	struct net_device *dev = priv->ndev;
237862306a36Sopenharmony_ci	unsigned long flags;
237962306a36Sopenharmony_ci	int err;
238062306a36Sopenharmony_ci
238162306a36Sopenharmony_ci	priv->hw.ops->lock_irqsave(&priv->lock, &flags);
238262306a36Sopenharmony_ci	err = __orinoco_down(priv);
238362306a36Sopenharmony_ci	if (err)
238462306a36Sopenharmony_ci		printk(KERN_WARNING "%s: Error %d downing interface\n",
238562306a36Sopenharmony_ci		       dev->name, err);
238662306a36Sopenharmony_ci
238762306a36Sopenharmony_ci	netif_device_detach(dev);
238862306a36Sopenharmony_ci	priv->hw_unavailable++;
238962306a36Sopenharmony_ci	priv->hw.ops->unlock_irqrestore(&priv->lock, &flags);
239062306a36Sopenharmony_ci}
239162306a36Sopenharmony_ciEXPORT_SYMBOL(orinoco_down);
239262306a36Sopenharmony_ci
239362306a36Sopenharmony_ci/********************************************************************/
239462306a36Sopenharmony_ci/* Module initialization                                            */
239562306a36Sopenharmony_ci/********************************************************************/
239662306a36Sopenharmony_ci
239762306a36Sopenharmony_ci/* Can't be declared "const" or the whole __initdata section will
239862306a36Sopenharmony_ci * become const */
239962306a36Sopenharmony_cistatic char version[] __initdata = DRIVER_NAME " " DRIVER_VERSION
240062306a36Sopenharmony_ci	" (David Gibson <hermes@gibson.dropbear.id.au>, "
240162306a36Sopenharmony_ci	"Pavel Roskin <proski@gnu.org>, et al)";
240262306a36Sopenharmony_ci
240362306a36Sopenharmony_cistatic int __init init_orinoco(void)
240462306a36Sopenharmony_ci{
240562306a36Sopenharmony_ci	printk(KERN_DEBUG "%s\n", version);
240662306a36Sopenharmony_ci	return 0;
240762306a36Sopenharmony_ci}
240862306a36Sopenharmony_ci
240962306a36Sopenharmony_cistatic void __exit exit_orinoco(void)
241062306a36Sopenharmony_ci{
241162306a36Sopenharmony_ci}
241262306a36Sopenharmony_ci
241362306a36Sopenharmony_cimodule_init(init_orinoco);
241462306a36Sopenharmony_cimodule_exit(exit_orinoco);
2415