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