162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Original code based Host AP (software wireless LAN access point) driver
462306a36Sopenharmony_ci * for Intersil Prism2/2.5/3 - hostap.o module, common routines
562306a36Sopenharmony_ci *
662306a36Sopenharmony_ci * Copyright (c) 2001-2002, SSH Communications Security Corp and Jouni Malinen
762306a36Sopenharmony_ci * <j@w1.fi>
862306a36Sopenharmony_ci * Copyright (c) 2002-2003, Jouni Malinen <j@w1.fi>
962306a36Sopenharmony_ci * Copyright (c) 2004-2005, Intel Corporation
1062306a36Sopenharmony_ci */
1162306a36Sopenharmony_ci
1262306a36Sopenharmony_ci#include <linux/compiler.h>
1362306a36Sopenharmony_ci#include <linux/errno.h>
1462306a36Sopenharmony_ci#include <linux/if_arp.h>
1562306a36Sopenharmony_ci#include <linux/in6.h>
1662306a36Sopenharmony_ci#include <linux/gfp.h>
1762306a36Sopenharmony_ci#include <linux/in.h>
1862306a36Sopenharmony_ci#include <linux/ip.h>
1962306a36Sopenharmony_ci#include <linux/kernel.h>
2062306a36Sopenharmony_ci#include <linux/module.h>
2162306a36Sopenharmony_ci#include <linux/netdevice.h>
2262306a36Sopenharmony_ci#include <linux/proc_fs.h>
2362306a36Sopenharmony_ci#include <linux/skbuff.h>
2462306a36Sopenharmony_ci#include <linux/tcp.h>
2562306a36Sopenharmony_ci#include <linux/types.h>
2662306a36Sopenharmony_ci#include <linux/wireless.h>
2762306a36Sopenharmony_ci#include <linux/etherdevice.h>
2862306a36Sopenharmony_ci#include <linux/uaccess.h>
2962306a36Sopenharmony_ci#include <linux/ctype.h>
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_ci#include <net/lib80211.h>
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_ci#include "libipw.h"
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_cistatic void libipw_monitor_rx(struct libipw_device *ieee,
3662306a36Sopenharmony_ci					struct sk_buff *skb,
3762306a36Sopenharmony_ci					struct libipw_rx_stats *rx_stats)
3862306a36Sopenharmony_ci{
3962306a36Sopenharmony_ci	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
4062306a36Sopenharmony_ci	u16 fc = le16_to_cpu(hdr->frame_control);
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_ci	skb->dev = ieee->dev;
4362306a36Sopenharmony_ci	skb_reset_mac_header(skb);
4462306a36Sopenharmony_ci	skb_pull(skb, libipw_get_hdrlen(fc));
4562306a36Sopenharmony_ci	skb->pkt_type = PACKET_OTHERHOST;
4662306a36Sopenharmony_ci	skb->protocol = htons(ETH_P_80211_RAW);
4762306a36Sopenharmony_ci	memset(skb->cb, 0, sizeof(skb->cb));
4862306a36Sopenharmony_ci	netif_rx(skb);
4962306a36Sopenharmony_ci}
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_ci/* Called only as a tasklet (software IRQ) */
5262306a36Sopenharmony_cistatic struct libipw_frag_entry *libipw_frag_cache_find(struct
5362306a36Sopenharmony_ci							      libipw_device
5462306a36Sopenharmony_ci							      *ieee,
5562306a36Sopenharmony_ci							      unsigned int seq,
5662306a36Sopenharmony_ci							      unsigned int frag,
5762306a36Sopenharmony_ci							      u8 * src,
5862306a36Sopenharmony_ci							      u8 * dst)
5962306a36Sopenharmony_ci{
6062306a36Sopenharmony_ci	struct libipw_frag_entry *entry;
6162306a36Sopenharmony_ci	int i;
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_ci	for (i = 0; i < LIBIPW_FRAG_CACHE_LEN; i++) {
6462306a36Sopenharmony_ci		entry = &ieee->frag_cache[i];
6562306a36Sopenharmony_ci		if (entry->skb != NULL &&
6662306a36Sopenharmony_ci		    time_after(jiffies, entry->first_frag_time + 2 * HZ)) {
6762306a36Sopenharmony_ci			LIBIPW_DEBUG_FRAG("expiring fragment cache entry "
6862306a36Sopenharmony_ci					     "seq=%u last_frag=%u\n",
6962306a36Sopenharmony_ci					     entry->seq, entry->last_frag);
7062306a36Sopenharmony_ci			dev_kfree_skb_any(entry->skb);
7162306a36Sopenharmony_ci			entry->skb = NULL;
7262306a36Sopenharmony_ci		}
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_ci		if (entry->skb != NULL && entry->seq == seq &&
7562306a36Sopenharmony_ci		    (entry->last_frag + 1 == frag || frag == -1) &&
7662306a36Sopenharmony_ci		    ether_addr_equal(entry->src_addr, src) &&
7762306a36Sopenharmony_ci		    ether_addr_equal(entry->dst_addr, dst))
7862306a36Sopenharmony_ci			return entry;
7962306a36Sopenharmony_ci	}
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_ci	return NULL;
8262306a36Sopenharmony_ci}
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_ci/* Called only as a tasklet (software IRQ) */
8562306a36Sopenharmony_cistatic struct sk_buff *libipw_frag_cache_get(struct libipw_device *ieee,
8662306a36Sopenharmony_ci						struct libipw_hdr_4addr *hdr)
8762306a36Sopenharmony_ci{
8862306a36Sopenharmony_ci	struct sk_buff *skb = NULL;
8962306a36Sopenharmony_ci	u16 sc;
9062306a36Sopenharmony_ci	unsigned int frag, seq;
9162306a36Sopenharmony_ci	struct libipw_frag_entry *entry;
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ci	sc = le16_to_cpu(hdr->seq_ctl);
9462306a36Sopenharmony_ci	frag = WLAN_GET_SEQ_FRAG(sc);
9562306a36Sopenharmony_ci	seq = WLAN_GET_SEQ_SEQ(sc);
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_ci	if (frag == 0) {
9862306a36Sopenharmony_ci		/* Reserve enough space to fit maximum frame length */
9962306a36Sopenharmony_ci		skb = dev_alloc_skb(ieee->dev->mtu +
10062306a36Sopenharmony_ci				    sizeof(struct libipw_hdr_4addr) +
10162306a36Sopenharmony_ci				    8 /* LLC */  +
10262306a36Sopenharmony_ci				    2 /* alignment */  +
10362306a36Sopenharmony_ci				    8 /* WEP */  + ETH_ALEN /* WDS */ );
10462306a36Sopenharmony_ci		if (skb == NULL)
10562306a36Sopenharmony_ci			return NULL;
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_ci		entry = &ieee->frag_cache[ieee->frag_next_idx];
10862306a36Sopenharmony_ci		ieee->frag_next_idx++;
10962306a36Sopenharmony_ci		if (ieee->frag_next_idx >= LIBIPW_FRAG_CACHE_LEN)
11062306a36Sopenharmony_ci			ieee->frag_next_idx = 0;
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_ci		if (entry->skb != NULL)
11362306a36Sopenharmony_ci			dev_kfree_skb_any(entry->skb);
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_ci		entry->first_frag_time = jiffies;
11662306a36Sopenharmony_ci		entry->seq = seq;
11762306a36Sopenharmony_ci		entry->last_frag = frag;
11862306a36Sopenharmony_ci		entry->skb = skb;
11962306a36Sopenharmony_ci		memcpy(entry->src_addr, hdr->addr2, ETH_ALEN);
12062306a36Sopenharmony_ci		memcpy(entry->dst_addr, hdr->addr1, ETH_ALEN);
12162306a36Sopenharmony_ci	} else {
12262306a36Sopenharmony_ci		/* received a fragment of a frame for which the head fragment
12362306a36Sopenharmony_ci		 * should have already been received */
12462306a36Sopenharmony_ci		entry = libipw_frag_cache_find(ieee, seq, frag, hdr->addr2,
12562306a36Sopenharmony_ci						  hdr->addr1);
12662306a36Sopenharmony_ci		if (entry != NULL) {
12762306a36Sopenharmony_ci			entry->last_frag = frag;
12862306a36Sopenharmony_ci			skb = entry->skb;
12962306a36Sopenharmony_ci		}
13062306a36Sopenharmony_ci	}
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_ci	return skb;
13362306a36Sopenharmony_ci}
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_ci/* Called only as a tasklet (software IRQ) */
13662306a36Sopenharmony_cistatic int libipw_frag_cache_invalidate(struct libipw_device *ieee,
13762306a36Sopenharmony_ci					   struct libipw_hdr_4addr *hdr)
13862306a36Sopenharmony_ci{
13962306a36Sopenharmony_ci	u16 sc;
14062306a36Sopenharmony_ci	unsigned int seq;
14162306a36Sopenharmony_ci	struct libipw_frag_entry *entry;
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_ci	sc = le16_to_cpu(hdr->seq_ctl);
14462306a36Sopenharmony_ci	seq = WLAN_GET_SEQ_SEQ(sc);
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_ci	entry = libipw_frag_cache_find(ieee, seq, -1, hdr->addr2,
14762306a36Sopenharmony_ci					  hdr->addr1);
14862306a36Sopenharmony_ci
14962306a36Sopenharmony_ci	if (entry == NULL) {
15062306a36Sopenharmony_ci		LIBIPW_DEBUG_FRAG("could not invalidate fragment cache "
15162306a36Sopenharmony_ci				     "entry (seq=%u)\n", seq);
15262306a36Sopenharmony_ci		return -1;
15362306a36Sopenharmony_ci	}
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_ci	entry->skb = NULL;
15662306a36Sopenharmony_ci	return 0;
15762306a36Sopenharmony_ci}
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_ci#ifdef NOT_YET
16062306a36Sopenharmony_ci/* libipw_rx_frame_mgtmt
16162306a36Sopenharmony_ci *
16262306a36Sopenharmony_ci * Responsible for handling management control frames
16362306a36Sopenharmony_ci *
16462306a36Sopenharmony_ci * Called by libipw_rx */
16562306a36Sopenharmony_cistatic int
16662306a36Sopenharmony_cilibipw_rx_frame_mgmt(struct libipw_device *ieee, struct sk_buff *skb,
16762306a36Sopenharmony_ci			struct libipw_rx_stats *rx_stats, u16 type,
16862306a36Sopenharmony_ci			u16 stype)
16962306a36Sopenharmony_ci{
17062306a36Sopenharmony_ci	if (ieee->iw_mode == IW_MODE_MASTER) {
17162306a36Sopenharmony_ci		printk(KERN_DEBUG "%s: Master mode not yet supported.\n",
17262306a36Sopenharmony_ci		       ieee->dev->name);
17362306a36Sopenharmony_ci		return 0;
17462306a36Sopenharmony_ci/*
17562306a36Sopenharmony_ci  hostap_update_sta_ps(ieee, (struct hostap_libipw_hdr_4addr *)
17662306a36Sopenharmony_ci  skb->data);*/
17762306a36Sopenharmony_ci	}
17862306a36Sopenharmony_ci
17962306a36Sopenharmony_ci	if (ieee->hostapd && type == WLAN_FC_TYPE_MGMT) {
18062306a36Sopenharmony_ci		if (stype == WLAN_FC_STYPE_BEACON &&
18162306a36Sopenharmony_ci		    ieee->iw_mode == IW_MODE_MASTER) {
18262306a36Sopenharmony_ci			struct sk_buff *skb2;
18362306a36Sopenharmony_ci			/* Process beacon frames also in kernel driver to
18462306a36Sopenharmony_ci			 * update STA(AP) table statistics */
18562306a36Sopenharmony_ci			skb2 = skb_clone(skb, GFP_ATOMIC);
18662306a36Sopenharmony_ci			if (skb2)
18762306a36Sopenharmony_ci				hostap_rx(skb2->dev, skb2, rx_stats);
18862306a36Sopenharmony_ci		}
18962306a36Sopenharmony_ci
19062306a36Sopenharmony_ci		/* send management frames to the user space daemon for
19162306a36Sopenharmony_ci		 * processing */
19262306a36Sopenharmony_ci		ieee->apdevstats.rx_packets++;
19362306a36Sopenharmony_ci		ieee->apdevstats.rx_bytes += skb->len;
19462306a36Sopenharmony_ci		prism2_rx_80211(ieee->apdev, skb, rx_stats, PRISM2_RX_MGMT);
19562306a36Sopenharmony_ci		return 0;
19662306a36Sopenharmony_ci	}
19762306a36Sopenharmony_ci
19862306a36Sopenharmony_ci	if (ieee->iw_mode == IW_MODE_MASTER) {
19962306a36Sopenharmony_ci		if (type != WLAN_FC_TYPE_MGMT && type != WLAN_FC_TYPE_CTRL) {
20062306a36Sopenharmony_ci			printk(KERN_DEBUG "%s: unknown management frame "
20162306a36Sopenharmony_ci			       "(type=0x%02x, stype=0x%02x) dropped\n",
20262306a36Sopenharmony_ci			       skb->dev->name, type, stype);
20362306a36Sopenharmony_ci			return -1;
20462306a36Sopenharmony_ci		}
20562306a36Sopenharmony_ci
20662306a36Sopenharmony_ci		hostap_rx(skb->dev, skb, rx_stats);
20762306a36Sopenharmony_ci		return 0;
20862306a36Sopenharmony_ci	}
20962306a36Sopenharmony_ci
21062306a36Sopenharmony_ci	printk(KERN_DEBUG "%s: hostap_rx_frame_mgmt: management frame "
21162306a36Sopenharmony_ci	       "received in non-Host AP mode\n", skb->dev->name);
21262306a36Sopenharmony_ci	return -1;
21362306a36Sopenharmony_ci}
21462306a36Sopenharmony_ci#endif
21562306a36Sopenharmony_ci
21662306a36Sopenharmony_ci/* See IEEE 802.1H for LLC/SNAP encapsulation/decapsulation */
21762306a36Sopenharmony_ci/* Ethernet-II snap header (RFC1042 for most EtherTypes) */
21862306a36Sopenharmony_cistatic unsigned char libipw_rfc1042_header[] =
21962306a36Sopenharmony_ci    { 0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00 };
22062306a36Sopenharmony_ci
22162306a36Sopenharmony_ci/* Bridge-Tunnel header (for EtherTypes ETH_P_AARP and ETH_P_IPX) */
22262306a36Sopenharmony_cistatic unsigned char libipw_bridge_tunnel_header[] =
22362306a36Sopenharmony_ci    { 0xaa, 0xaa, 0x03, 0x00, 0x00, 0xf8 };
22462306a36Sopenharmony_ci/* No encapsulation header if EtherType < 0x600 (=length) */
22562306a36Sopenharmony_ci
22662306a36Sopenharmony_ci/* Called by libipw_rx_frame_decrypt */
22762306a36Sopenharmony_cistatic int libipw_is_eapol_frame(struct libipw_device *ieee,
22862306a36Sopenharmony_ci				    struct sk_buff *skb)
22962306a36Sopenharmony_ci{
23062306a36Sopenharmony_ci	struct net_device *dev = ieee->dev;
23162306a36Sopenharmony_ci	u16 fc, ethertype;
23262306a36Sopenharmony_ci	struct libipw_hdr_3addr *hdr;
23362306a36Sopenharmony_ci	u8 *pos;
23462306a36Sopenharmony_ci
23562306a36Sopenharmony_ci	if (skb->len < 24)
23662306a36Sopenharmony_ci		return 0;
23762306a36Sopenharmony_ci
23862306a36Sopenharmony_ci	hdr = (struct libipw_hdr_3addr *)skb->data;
23962306a36Sopenharmony_ci	fc = le16_to_cpu(hdr->frame_ctl);
24062306a36Sopenharmony_ci
24162306a36Sopenharmony_ci	/* check that the frame is unicast frame to us */
24262306a36Sopenharmony_ci	if ((fc & (IEEE80211_FCTL_TODS | IEEE80211_FCTL_FROMDS)) ==
24362306a36Sopenharmony_ci	    IEEE80211_FCTL_TODS &&
24462306a36Sopenharmony_ci	    ether_addr_equal(hdr->addr1, dev->dev_addr) &&
24562306a36Sopenharmony_ci	    ether_addr_equal(hdr->addr3, dev->dev_addr)) {
24662306a36Sopenharmony_ci		/* ToDS frame with own addr BSSID and DA */
24762306a36Sopenharmony_ci	} else if ((fc & (IEEE80211_FCTL_TODS | IEEE80211_FCTL_FROMDS)) ==
24862306a36Sopenharmony_ci		   IEEE80211_FCTL_FROMDS &&
24962306a36Sopenharmony_ci		   ether_addr_equal(hdr->addr1, dev->dev_addr)) {
25062306a36Sopenharmony_ci		/* FromDS frame with own addr as DA */
25162306a36Sopenharmony_ci	} else
25262306a36Sopenharmony_ci		return 0;
25362306a36Sopenharmony_ci
25462306a36Sopenharmony_ci	if (skb->len < 24 + 8)
25562306a36Sopenharmony_ci		return 0;
25662306a36Sopenharmony_ci
25762306a36Sopenharmony_ci	/* check for port access entity Ethernet type */
25862306a36Sopenharmony_ci	pos = skb->data + 24;
25962306a36Sopenharmony_ci	ethertype = (pos[6] << 8) | pos[7];
26062306a36Sopenharmony_ci	if (ethertype == ETH_P_PAE)
26162306a36Sopenharmony_ci		return 1;
26262306a36Sopenharmony_ci
26362306a36Sopenharmony_ci	return 0;
26462306a36Sopenharmony_ci}
26562306a36Sopenharmony_ci
26662306a36Sopenharmony_ci/* Called only as a tasklet (software IRQ), by libipw_rx */
26762306a36Sopenharmony_cistatic int
26862306a36Sopenharmony_cilibipw_rx_frame_decrypt(struct libipw_device *ieee, struct sk_buff *skb,
26962306a36Sopenharmony_ci			   struct lib80211_crypt_data *crypt)
27062306a36Sopenharmony_ci{
27162306a36Sopenharmony_ci	struct libipw_hdr_3addr *hdr;
27262306a36Sopenharmony_ci	int res, hdrlen;
27362306a36Sopenharmony_ci
27462306a36Sopenharmony_ci	if (crypt == NULL || crypt->ops->decrypt_mpdu == NULL)
27562306a36Sopenharmony_ci		return 0;
27662306a36Sopenharmony_ci
27762306a36Sopenharmony_ci	hdr = (struct libipw_hdr_3addr *)skb->data;
27862306a36Sopenharmony_ci	hdrlen = libipw_get_hdrlen(le16_to_cpu(hdr->frame_ctl));
27962306a36Sopenharmony_ci
28062306a36Sopenharmony_ci	atomic_inc(&crypt->refcnt);
28162306a36Sopenharmony_ci	res = crypt->ops->decrypt_mpdu(skb, hdrlen, crypt->priv);
28262306a36Sopenharmony_ci	atomic_dec(&crypt->refcnt);
28362306a36Sopenharmony_ci	if (res < 0) {
28462306a36Sopenharmony_ci		LIBIPW_DEBUG_DROP("decryption failed (SA=%pM) res=%d\n",
28562306a36Sopenharmony_ci				     hdr->addr2, res);
28662306a36Sopenharmony_ci		if (res == -2)
28762306a36Sopenharmony_ci			LIBIPW_DEBUG_DROP("Decryption failed ICV "
28862306a36Sopenharmony_ci					     "mismatch (key %d)\n",
28962306a36Sopenharmony_ci					     skb->data[hdrlen + 3] >> 6);
29062306a36Sopenharmony_ci		ieee->ieee_stats.rx_discards_undecryptable++;
29162306a36Sopenharmony_ci		return -1;
29262306a36Sopenharmony_ci	}
29362306a36Sopenharmony_ci
29462306a36Sopenharmony_ci	return res;
29562306a36Sopenharmony_ci}
29662306a36Sopenharmony_ci
29762306a36Sopenharmony_ci/* Called only as a tasklet (software IRQ), by libipw_rx */
29862306a36Sopenharmony_cistatic int
29962306a36Sopenharmony_cilibipw_rx_frame_decrypt_msdu(struct libipw_device *ieee,
30062306a36Sopenharmony_ci				struct sk_buff *skb, int keyidx,
30162306a36Sopenharmony_ci				struct lib80211_crypt_data *crypt)
30262306a36Sopenharmony_ci{
30362306a36Sopenharmony_ci	struct libipw_hdr_3addr *hdr;
30462306a36Sopenharmony_ci	int res, hdrlen;
30562306a36Sopenharmony_ci
30662306a36Sopenharmony_ci	if (crypt == NULL || crypt->ops->decrypt_msdu == NULL)
30762306a36Sopenharmony_ci		return 0;
30862306a36Sopenharmony_ci
30962306a36Sopenharmony_ci	hdr = (struct libipw_hdr_3addr *)skb->data;
31062306a36Sopenharmony_ci	hdrlen = libipw_get_hdrlen(le16_to_cpu(hdr->frame_ctl));
31162306a36Sopenharmony_ci
31262306a36Sopenharmony_ci	atomic_inc(&crypt->refcnt);
31362306a36Sopenharmony_ci	res = crypt->ops->decrypt_msdu(skb, keyidx, hdrlen, crypt->priv);
31462306a36Sopenharmony_ci	atomic_dec(&crypt->refcnt);
31562306a36Sopenharmony_ci	if (res < 0) {
31662306a36Sopenharmony_ci		printk(KERN_DEBUG "%s: MSDU decryption/MIC verification failed"
31762306a36Sopenharmony_ci		       " (SA=%pM keyidx=%d)\n", ieee->dev->name, hdr->addr2,
31862306a36Sopenharmony_ci		       keyidx);
31962306a36Sopenharmony_ci		return -1;
32062306a36Sopenharmony_ci	}
32162306a36Sopenharmony_ci
32262306a36Sopenharmony_ci	return 0;
32362306a36Sopenharmony_ci}
32462306a36Sopenharmony_ci
32562306a36Sopenharmony_ci/* All received frames are sent to this function. @skb contains the frame in
32662306a36Sopenharmony_ci * IEEE 802.11 format, i.e., in the format it was sent over air.
32762306a36Sopenharmony_ci * This function is called only as a tasklet (software IRQ). */
32862306a36Sopenharmony_ciint libipw_rx(struct libipw_device *ieee, struct sk_buff *skb,
32962306a36Sopenharmony_ci		 struct libipw_rx_stats *rx_stats)
33062306a36Sopenharmony_ci{
33162306a36Sopenharmony_ci	struct net_device *dev = ieee->dev;
33262306a36Sopenharmony_ci	struct libipw_hdr_4addr *hdr;
33362306a36Sopenharmony_ci	size_t hdrlen;
33462306a36Sopenharmony_ci	u16 fc, type, stype, sc;
33562306a36Sopenharmony_ci	unsigned int frag;
33662306a36Sopenharmony_ci	u8 *payload;
33762306a36Sopenharmony_ci	u16 ethertype;
33862306a36Sopenharmony_ci#ifdef NOT_YET
33962306a36Sopenharmony_ci	struct net_device *wds = NULL;
34062306a36Sopenharmony_ci	struct sk_buff *skb2 = NULL;
34162306a36Sopenharmony_ci	struct net_device *wds = NULL;
34262306a36Sopenharmony_ci	int frame_authorized = 0;
34362306a36Sopenharmony_ci	int from_assoc_ap = 0;
34462306a36Sopenharmony_ci	void *sta = NULL;
34562306a36Sopenharmony_ci#endif
34662306a36Sopenharmony_ci	u8 dst[ETH_ALEN];
34762306a36Sopenharmony_ci	u8 src[ETH_ALEN];
34862306a36Sopenharmony_ci	struct lib80211_crypt_data *crypt = NULL;
34962306a36Sopenharmony_ci	int keyidx = 0;
35062306a36Sopenharmony_ci	int can_be_decrypted = 0;
35162306a36Sopenharmony_ci
35262306a36Sopenharmony_ci	hdr = (struct libipw_hdr_4addr *)skb->data;
35362306a36Sopenharmony_ci	if (skb->len < 10) {
35462306a36Sopenharmony_ci		printk(KERN_INFO "%s: SKB length < 10\n", dev->name);
35562306a36Sopenharmony_ci		goto rx_dropped;
35662306a36Sopenharmony_ci	}
35762306a36Sopenharmony_ci
35862306a36Sopenharmony_ci	fc = le16_to_cpu(hdr->frame_ctl);
35962306a36Sopenharmony_ci	type = WLAN_FC_GET_TYPE(fc);
36062306a36Sopenharmony_ci	stype = WLAN_FC_GET_STYPE(fc);
36162306a36Sopenharmony_ci	sc = le16_to_cpu(hdr->seq_ctl);
36262306a36Sopenharmony_ci	frag = WLAN_GET_SEQ_FRAG(sc);
36362306a36Sopenharmony_ci	hdrlen = libipw_get_hdrlen(fc);
36462306a36Sopenharmony_ci
36562306a36Sopenharmony_ci	if (skb->len < hdrlen) {
36662306a36Sopenharmony_ci		printk(KERN_INFO "%s: invalid SKB length %d\n",
36762306a36Sopenharmony_ci			dev->name, skb->len);
36862306a36Sopenharmony_ci		goto rx_dropped;
36962306a36Sopenharmony_ci	}
37062306a36Sopenharmony_ci
37162306a36Sopenharmony_ci	/* Put this code here so that we avoid duplicating it in all
37262306a36Sopenharmony_ci	 * Rx paths. - Jean II */
37362306a36Sopenharmony_ci#ifdef CONFIG_WIRELESS_EXT
37462306a36Sopenharmony_ci#ifdef IW_WIRELESS_SPY		/* defined in iw_handler.h */
37562306a36Sopenharmony_ci	/* If spy monitoring on */
37662306a36Sopenharmony_ci	if (ieee->spy_data.spy_number > 0) {
37762306a36Sopenharmony_ci		struct iw_quality wstats;
37862306a36Sopenharmony_ci
37962306a36Sopenharmony_ci		wstats.updated = 0;
38062306a36Sopenharmony_ci		if (rx_stats->mask & LIBIPW_STATMASK_RSSI) {
38162306a36Sopenharmony_ci			wstats.level = rx_stats->signal;
38262306a36Sopenharmony_ci			wstats.updated |= IW_QUAL_LEVEL_UPDATED;
38362306a36Sopenharmony_ci		} else
38462306a36Sopenharmony_ci			wstats.updated |= IW_QUAL_LEVEL_INVALID;
38562306a36Sopenharmony_ci
38662306a36Sopenharmony_ci		if (rx_stats->mask & LIBIPW_STATMASK_NOISE) {
38762306a36Sopenharmony_ci			wstats.noise = rx_stats->noise;
38862306a36Sopenharmony_ci			wstats.updated |= IW_QUAL_NOISE_UPDATED;
38962306a36Sopenharmony_ci		} else
39062306a36Sopenharmony_ci			wstats.updated |= IW_QUAL_NOISE_INVALID;
39162306a36Sopenharmony_ci
39262306a36Sopenharmony_ci		if (rx_stats->mask & LIBIPW_STATMASK_SIGNAL) {
39362306a36Sopenharmony_ci			wstats.qual = rx_stats->signal;
39462306a36Sopenharmony_ci			wstats.updated |= IW_QUAL_QUAL_UPDATED;
39562306a36Sopenharmony_ci		} else
39662306a36Sopenharmony_ci			wstats.updated |= IW_QUAL_QUAL_INVALID;
39762306a36Sopenharmony_ci
39862306a36Sopenharmony_ci		/* Update spy records */
39962306a36Sopenharmony_ci		wireless_spy_update(ieee->dev, hdr->addr2, &wstats);
40062306a36Sopenharmony_ci	}
40162306a36Sopenharmony_ci#endif				/* IW_WIRELESS_SPY */
40262306a36Sopenharmony_ci#endif				/* CONFIG_WIRELESS_EXT */
40362306a36Sopenharmony_ci
40462306a36Sopenharmony_ci#ifdef NOT_YET
40562306a36Sopenharmony_ci	hostap_update_rx_stats(local->ap, hdr, rx_stats);
40662306a36Sopenharmony_ci#endif
40762306a36Sopenharmony_ci
40862306a36Sopenharmony_ci	if (ieee->iw_mode == IW_MODE_MONITOR) {
40962306a36Sopenharmony_ci		dev->stats.rx_packets++;
41062306a36Sopenharmony_ci		dev->stats.rx_bytes += skb->len;
41162306a36Sopenharmony_ci		libipw_monitor_rx(ieee, skb, rx_stats);
41262306a36Sopenharmony_ci		return 1;
41362306a36Sopenharmony_ci	}
41462306a36Sopenharmony_ci
41562306a36Sopenharmony_ci	can_be_decrypted = (is_multicast_ether_addr(hdr->addr1) ||
41662306a36Sopenharmony_ci			    is_broadcast_ether_addr(hdr->addr2)) ?
41762306a36Sopenharmony_ci	    ieee->host_mc_decrypt : ieee->host_decrypt;
41862306a36Sopenharmony_ci
41962306a36Sopenharmony_ci	if (can_be_decrypted) {
42062306a36Sopenharmony_ci		if (skb->len >= hdrlen + 3) {
42162306a36Sopenharmony_ci			/* Top two-bits of byte 3 are the key index */
42262306a36Sopenharmony_ci			keyidx = skb->data[hdrlen + 3] >> 6;
42362306a36Sopenharmony_ci		}
42462306a36Sopenharmony_ci
42562306a36Sopenharmony_ci		/* ieee->crypt[] is WEP_KEY (4) in length.  Given that keyidx
42662306a36Sopenharmony_ci		 * is only allowed 2-bits of storage, no value of keyidx can
42762306a36Sopenharmony_ci		 * be provided via above code that would result in keyidx
42862306a36Sopenharmony_ci		 * being out of range */
42962306a36Sopenharmony_ci		crypt = ieee->crypt_info.crypt[keyidx];
43062306a36Sopenharmony_ci
43162306a36Sopenharmony_ci#ifdef NOT_YET
43262306a36Sopenharmony_ci		sta = NULL;
43362306a36Sopenharmony_ci
43462306a36Sopenharmony_ci		/* Use station specific key to override default keys if the
43562306a36Sopenharmony_ci		 * receiver address is a unicast address ("individual RA"). If
43662306a36Sopenharmony_ci		 * bcrx_sta_key parameter is set, station specific key is used
43762306a36Sopenharmony_ci		 * even with broad/multicast targets (this is against IEEE
43862306a36Sopenharmony_ci		 * 802.11, but makes it easier to use different keys with
43962306a36Sopenharmony_ci		 * stations that do not support WEP key mapping). */
44062306a36Sopenharmony_ci
44162306a36Sopenharmony_ci		if (is_unicast_ether_addr(hdr->addr1) || local->bcrx_sta_key)
44262306a36Sopenharmony_ci			(void)hostap_handle_sta_crypto(local, hdr, &crypt,
44362306a36Sopenharmony_ci						       &sta);
44462306a36Sopenharmony_ci#endif
44562306a36Sopenharmony_ci
44662306a36Sopenharmony_ci		/* allow NULL decrypt to indicate an station specific override
44762306a36Sopenharmony_ci		 * for default encryption */
44862306a36Sopenharmony_ci		if (crypt && (crypt->ops == NULL ||
44962306a36Sopenharmony_ci			      crypt->ops->decrypt_mpdu == NULL))
45062306a36Sopenharmony_ci			crypt = NULL;
45162306a36Sopenharmony_ci
45262306a36Sopenharmony_ci		if (!crypt && (fc & IEEE80211_FCTL_PROTECTED)) {
45362306a36Sopenharmony_ci			/* This seems to be triggered by some (multicast?)
45462306a36Sopenharmony_ci			 * frames from other than current BSS, so just drop the
45562306a36Sopenharmony_ci			 * frames silently instead of filling system log with
45662306a36Sopenharmony_ci			 * these reports. */
45762306a36Sopenharmony_ci			LIBIPW_DEBUG_DROP("Decryption failed (not set)"
45862306a36Sopenharmony_ci					     " (SA=%pM)\n", hdr->addr2);
45962306a36Sopenharmony_ci			ieee->ieee_stats.rx_discards_undecryptable++;
46062306a36Sopenharmony_ci			goto rx_dropped;
46162306a36Sopenharmony_ci		}
46262306a36Sopenharmony_ci	}
46362306a36Sopenharmony_ci#ifdef NOT_YET
46462306a36Sopenharmony_ci	if (type != WLAN_FC_TYPE_DATA) {
46562306a36Sopenharmony_ci		if (type == WLAN_FC_TYPE_MGMT && stype == WLAN_FC_STYPE_AUTH &&
46662306a36Sopenharmony_ci		    fc & IEEE80211_FCTL_PROTECTED && ieee->host_decrypt &&
46762306a36Sopenharmony_ci		    (keyidx = hostap_rx_frame_decrypt(ieee, skb, crypt)) < 0) {
46862306a36Sopenharmony_ci			printk(KERN_DEBUG "%s: failed to decrypt mgmt::auth "
46962306a36Sopenharmony_ci			       "from %pM\n", dev->name, hdr->addr2);
47062306a36Sopenharmony_ci			/* TODO: could inform hostapd about this so that it
47162306a36Sopenharmony_ci			 * could send auth failure report */
47262306a36Sopenharmony_ci			goto rx_dropped;
47362306a36Sopenharmony_ci		}
47462306a36Sopenharmony_ci
47562306a36Sopenharmony_ci		if (libipw_rx_frame_mgmt(ieee, skb, rx_stats, type, stype))
47662306a36Sopenharmony_ci			goto rx_dropped;
47762306a36Sopenharmony_ci		else
47862306a36Sopenharmony_ci			goto rx_exit;
47962306a36Sopenharmony_ci	}
48062306a36Sopenharmony_ci#endif
48162306a36Sopenharmony_ci	/* drop duplicate 802.11 retransmissions (IEEE 802.11 Chap. 9.29) */
48262306a36Sopenharmony_ci	if (sc == ieee->prev_seq_ctl)
48362306a36Sopenharmony_ci		goto rx_dropped;
48462306a36Sopenharmony_ci	else
48562306a36Sopenharmony_ci		ieee->prev_seq_ctl = sc;
48662306a36Sopenharmony_ci
48762306a36Sopenharmony_ci	/* Data frame - extract src/dst addresses */
48862306a36Sopenharmony_ci	if (skb->len < LIBIPW_3ADDR_LEN)
48962306a36Sopenharmony_ci		goto rx_dropped;
49062306a36Sopenharmony_ci
49162306a36Sopenharmony_ci	switch (fc & (IEEE80211_FCTL_FROMDS | IEEE80211_FCTL_TODS)) {
49262306a36Sopenharmony_ci	case IEEE80211_FCTL_FROMDS:
49362306a36Sopenharmony_ci		memcpy(dst, hdr->addr1, ETH_ALEN);
49462306a36Sopenharmony_ci		memcpy(src, hdr->addr3, ETH_ALEN);
49562306a36Sopenharmony_ci		break;
49662306a36Sopenharmony_ci	case IEEE80211_FCTL_TODS:
49762306a36Sopenharmony_ci		memcpy(dst, hdr->addr3, ETH_ALEN);
49862306a36Sopenharmony_ci		memcpy(src, hdr->addr2, ETH_ALEN);
49962306a36Sopenharmony_ci		break;
50062306a36Sopenharmony_ci	case IEEE80211_FCTL_FROMDS | IEEE80211_FCTL_TODS:
50162306a36Sopenharmony_ci		if (skb->len < LIBIPW_4ADDR_LEN)
50262306a36Sopenharmony_ci			goto rx_dropped;
50362306a36Sopenharmony_ci		memcpy(dst, hdr->addr3, ETH_ALEN);
50462306a36Sopenharmony_ci		memcpy(src, hdr->addr4, ETH_ALEN);
50562306a36Sopenharmony_ci		break;
50662306a36Sopenharmony_ci	default:
50762306a36Sopenharmony_ci		memcpy(dst, hdr->addr1, ETH_ALEN);
50862306a36Sopenharmony_ci		memcpy(src, hdr->addr2, ETH_ALEN);
50962306a36Sopenharmony_ci		break;
51062306a36Sopenharmony_ci	}
51162306a36Sopenharmony_ci
51262306a36Sopenharmony_ci#ifdef NOT_YET
51362306a36Sopenharmony_ci	if (hostap_rx_frame_wds(ieee, hdr, fc, &wds))
51462306a36Sopenharmony_ci		goto rx_dropped;
51562306a36Sopenharmony_ci	if (wds) {
51662306a36Sopenharmony_ci		skb->dev = dev = wds;
51762306a36Sopenharmony_ci		stats = hostap_get_stats(dev);
51862306a36Sopenharmony_ci	}
51962306a36Sopenharmony_ci
52062306a36Sopenharmony_ci	if (ieee->iw_mode == IW_MODE_MASTER && !wds &&
52162306a36Sopenharmony_ci	    (fc & (IEEE80211_FCTL_TODS | IEEE80211_FCTL_FROMDS)) ==
52262306a36Sopenharmony_ci	    IEEE80211_FCTL_FROMDS && ieee->stadev &&
52362306a36Sopenharmony_ci	    ether_addr_equal(hdr->addr2, ieee->assoc_ap_addr)) {
52462306a36Sopenharmony_ci		/* Frame from BSSID of the AP for which we are a client */
52562306a36Sopenharmony_ci		skb->dev = dev = ieee->stadev;
52662306a36Sopenharmony_ci		stats = hostap_get_stats(dev);
52762306a36Sopenharmony_ci		from_assoc_ap = 1;
52862306a36Sopenharmony_ci	}
52962306a36Sopenharmony_ci#endif
53062306a36Sopenharmony_ci
53162306a36Sopenharmony_ci#ifdef NOT_YET
53262306a36Sopenharmony_ci	if ((ieee->iw_mode == IW_MODE_MASTER ||
53362306a36Sopenharmony_ci	     ieee->iw_mode == IW_MODE_REPEAT) && !from_assoc_ap) {
53462306a36Sopenharmony_ci		switch (hostap_handle_sta_rx(ieee, dev, skb, rx_stats,
53562306a36Sopenharmony_ci					     wds != NULL)) {
53662306a36Sopenharmony_ci		case AP_RX_CONTINUE_NOT_AUTHORIZED:
53762306a36Sopenharmony_ci			frame_authorized = 0;
53862306a36Sopenharmony_ci			break;
53962306a36Sopenharmony_ci		case AP_RX_CONTINUE:
54062306a36Sopenharmony_ci			frame_authorized = 1;
54162306a36Sopenharmony_ci			break;
54262306a36Sopenharmony_ci		case AP_RX_DROP:
54362306a36Sopenharmony_ci			goto rx_dropped;
54462306a36Sopenharmony_ci		case AP_RX_EXIT:
54562306a36Sopenharmony_ci			goto rx_exit;
54662306a36Sopenharmony_ci		}
54762306a36Sopenharmony_ci	}
54862306a36Sopenharmony_ci#endif
54962306a36Sopenharmony_ci
55062306a36Sopenharmony_ci	/* Nullfunc frames may have PS-bit set, so they must be passed to
55162306a36Sopenharmony_ci	 * hostap_handle_sta_rx() before being dropped here. */
55262306a36Sopenharmony_ci
55362306a36Sopenharmony_ci	stype &= ~IEEE80211_STYPE_QOS_DATA;
55462306a36Sopenharmony_ci
55562306a36Sopenharmony_ci	if (stype != IEEE80211_STYPE_DATA &&
55662306a36Sopenharmony_ci	    stype != IEEE80211_STYPE_DATA_CFACK &&
55762306a36Sopenharmony_ci	    stype != IEEE80211_STYPE_DATA_CFPOLL &&
55862306a36Sopenharmony_ci	    stype != IEEE80211_STYPE_DATA_CFACKPOLL) {
55962306a36Sopenharmony_ci		if (stype != IEEE80211_STYPE_NULLFUNC)
56062306a36Sopenharmony_ci			LIBIPW_DEBUG_DROP("RX: dropped data frame "
56162306a36Sopenharmony_ci					     "with no data (type=0x%02x, "
56262306a36Sopenharmony_ci					     "subtype=0x%02x, len=%d)\n",
56362306a36Sopenharmony_ci					     type, stype, skb->len);
56462306a36Sopenharmony_ci		goto rx_dropped;
56562306a36Sopenharmony_ci	}
56662306a36Sopenharmony_ci
56762306a36Sopenharmony_ci	/* skb: hdr + (possibly fragmented, possibly encrypted) payload */
56862306a36Sopenharmony_ci
56962306a36Sopenharmony_ci	if ((fc & IEEE80211_FCTL_PROTECTED) && can_be_decrypted &&
57062306a36Sopenharmony_ci	    (keyidx = libipw_rx_frame_decrypt(ieee, skb, crypt)) < 0)
57162306a36Sopenharmony_ci		goto rx_dropped;
57262306a36Sopenharmony_ci
57362306a36Sopenharmony_ci	hdr = (struct libipw_hdr_4addr *)skb->data;
57462306a36Sopenharmony_ci
57562306a36Sopenharmony_ci	/* skb: hdr + (possibly fragmented) plaintext payload */
57662306a36Sopenharmony_ci	// PR: FIXME: hostap has additional conditions in the "if" below:
57762306a36Sopenharmony_ci	// ieee->host_decrypt && (fc & IEEE80211_FCTL_PROTECTED) &&
57862306a36Sopenharmony_ci	if ((frag != 0) || (fc & IEEE80211_FCTL_MOREFRAGS)) {
57962306a36Sopenharmony_ci		int flen;
58062306a36Sopenharmony_ci		struct sk_buff *frag_skb = libipw_frag_cache_get(ieee, hdr);
58162306a36Sopenharmony_ci		LIBIPW_DEBUG_FRAG("Rx Fragment received (%u)\n", frag);
58262306a36Sopenharmony_ci
58362306a36Sopenharmony_ci		if (!frag_skb) {
58462306a36Sopenharmony_ci			LIBIPW_DEBUG(LIBIPW_DL_RX | LIBIPW_DL_FRAG,
58562306a36Sopenharmony_ci					"Rx cannot get skb from fragment "
58662306a36Sopenharmony_ci					"cache (morefrag=%d seq=%u frag=%u)\n",
58762306a36Sopenharmony_ci					(fc & IEEE80211_FCTL_MOREFRAGS) != 0,
58862306a36Sopenharmony_ci					WLAN_GET_SEQ_SEQ(sc), frag);
58962306a36Sopenharmony_ci			goto rx_dropped;
59062306a36Sopenharmony_ci		}
59162306a36Sopenharmony_ci
59262306a36Sopenharmony_ci		flen = skb->len;
59362306a36Sopenharmony_ci		if (frag != 0)
59462306a36Sopenharmony_ci			flen -= hdrlen;
59562306a36Sopenharmony_ci
59662306a36Sopenharmony_ci		if (frag_skb->tail + flen > frag_skb->end) {
59762306a36Sopenharmony_ci			printk(KERN_WARNING "%s: host decrypted and "
59862306a36Sopenharmony_ci			       "reassembled frame did not fit skb\n",
59962306a36Sopenharmony_ci			       dev->name);
60062306a36Sopenharmony_ci			libipw_frag_cache_invalidate(ieee, hdr);
60162306a36Sopenharmony_ci			goto rx_dropped;
60262306a36Sopenharmony_ci		}
60362306a36Sopenharmony_ci
60462306a36Sopenharmony_ci		if (frag == 0) {
60562306a36Sopenharmony_ci			/* copy first fragment (including full headers) into
60662306a36Sopenharmony_ci			 * beginning of the fragment cache skb */
60762306a36Sopenharmony_ci			skb_copy_from_linear_data(skb, skb_put(frag_skb, flen), flen);
60862306a36Sopenharmony_ci		} else {
60962306a36Sopenharmony_ci			/* append frame payload to the end of the fragment
61062306a36Sopenharmony_ci			 * cache skb */
61162306a36Sopenharmony_ci			skb_copy_from_linear_data_offset(skb, hdrlen,
61262306a36Sopenharmony_ci				      skb_put(frag_skb, flen), flen);
61362306a36Sopenharmony_ci		}
61462306a36Sopenharmony_ci		dev_kfree_skb_any(skb);
61562306a36Sopenharmony_ci		skb = NULL;
61662306a36Sopenharmony_ci
61762306a36Sopenharmony_ci		if (fc & IEEE80211_FCTL_MOREFRAGS) {
61862306a36Sopenharmony_ci			/* more fragments expected - leave the skb in fragment
61962306a36Sopenharmony_ci			 * cache for now; it will be delivered to upper layers
62062306a36Sopenharmony_ci			 * after all fragments have been received */
62162306a36Sopenharmony_ci			goto rx_exit;
62262306a36Sopenharmony_ci		}
62362306a36Sopenharmony_ci
62462306a36Sopenharmony_ci		/* this was the last fragment and the frame will be
62562306a36Sopenharmony_ci		 * delivered, so remove skb from fragment cache */
62662306a36Sopenharmony_ci		skb = frag_skb;
62762306a36Sopenharmony_ci		hdr = (struct libipw_hdr_4addr *)skb->data;
62862306a36Sopenharmony_ci		libipw_frag_cache_invalidate(ieee, hdr);
62962306a36Sopenharmony_ci	}
63062306a36Sopenharmony_ci
63162306a36Sopenharmony_ci	/* skb: hdr + (possible reassembled) full MSDU payload; possibly still
63262306a36Sopenharmony_ci	 * encrypted/authenticated */
63362306a36Sopenharmony_ci	if ((fc & IEEE80211_FCTL_PROTECTED) && can_be_decrypted &&
63462306a36Sopenharmony_ci	    libipw_rx_frame_decrypt_msdu(ieee, skb, keyidx, crypt))
63562306a36Sopenharmony_ci		goto rx_dropped;
63662306a36Sopenharmony_ci
63762306a36Sopenharmony_ci	hdr = (struct libipw_hdr_4addr *)skb->data;
63862306a36Sopenharmony_ci	if (crypt && !(fc & IEEE80211_FCTL_PROTECTED) && !ieee->open_wep) {
63962306a36Sopenharmony_ci		if (		/*ieee->ieee802_1x && */
64062306a36Sopenharmony_ci			   libipw_is_eapol_frame(ieee, skb)) {
64162306a36Sopenharmony_ci			/* pass unencrypted EAPOL frames even if encryption is
64262306a36Sopenharmony_ci			 * configured */
64362306a36Sopenharmony_ci		} else {
64462306a36Sopenharmony_ci			LIBIPW_DEBUG_DROP("encryption configured, but RX "
64562306a36Sopenharmony_ci					     "frame not encrypted (SA=%pM)\n",
64662306a36Sopenharmony_ci					     hdr->addr2);
64762306a36Sopenharmony_ci			goto rx_dropped;
64862306a36Sopenharmony_ci		}
64962306a36Sopenharmony_ci	}
65062306a36Sopenharmony_ci
65162306a36Sopenharmony_ci	if (crypt && !(fc & IEEE80211_FCTL_PROTECTED) && !ieee->open_wep &&
65262306a36Sopenharmony_ci	    !libipw_is_eapol_frame(ieee, skb)) {
65362306a36Sopenharmony_ci		LIBIPW_DEBUG_DROP("dropped unencrypted RX data "
65462306a36Sopenharmony_ci				     "frame from %pM (drop_unencrypted=1)\n",
65562306a36Sopenharmony_ci				     hdr->addr2);
65662306a36Sopenharmony_ci		goto rx_dropped;
65762306a36Sopenharmony_ci	}
65862306a36Sopenharmony_ci
65962306a36Sopenharmony_ci	/* If the frame was decrypted in hardware, we may need to strip off
66062306a36Sopenharmony_ci	 * any security data (IV, ICV, etc) that was left behind */
66162306a36Sopenharmony_ci	if (!can_be_decrypted && (fc & IEEE80211_FCTL_PROTECTED) &&
66262306a36Sopenharmony_ci	    ieee->host_strip_iv_icv) {
66362306a36Sopenharmony_ci		int trimlen = 0;
66462306a36Sopenharmony_ci
66562306a36Sopenharmony_ci		/* Top two-bits of byte 3 are the key index */
66662306a36Sopenharmony_ci		if (skb->len >= hdrlen + 3)
66762306a36Sopenharmony_ci			keyidx = skb->data[hdrlen + 3] >> 6;
66862306a36Sopenharmony_ci
66962306a36Sopenharmony_ci		/* To strip off any security data which appears before the
67062306a36Sopenharmony_ci		 * payload, we simply increase hdrlen (as the header gets
67162306a36Sopenharmony_ci		 * chopped off immediately below). For the security data which
67262306a36Sopenharmony_ci		 * appears after the payload, we use skb_trim. */
67362306a36Sopenharmony_ci
67462306a36Sopenharmony_ci		switch (ieee->sec.encode_alg[keyidx]) {
67562306a36Sopenharmony_ci		case SEC_ALG_WEP:
67662306a36Sopenharmony_ci			/* 4 byte IV */
67762306a36Sopenharmony_ci			hdrlen += 4;
67862306a36Sopenharmony_ci			/* 4 byte ICV */
67962306a36Sopenharmony_ci			trimlen = 4;
68062306a36Sopenharmony_ci			break;
68162306a36Sopenharmony_ci		case SEC_ALG_TKIP:
68262306a36Sopenharmony_ci			/* 4 byte IV, 4 byte ExtIV */
68362306a36Sopenharmony_ci			hdrlen += 8;
68462306a36Sopenharmony_ci			/* 8 byte MIC, 4 byte ICV */
68562306a36Sopenharmony_ci			trimlen = 12;
68662306a36Sopenharmony_ci			break;
68762306a36Sopenharmony_ci		case SEC_ALG_CCMP:
68862306a36Sopenharmony_ci			/* 8 byte CCMP header */
68962306a36Sopenharmony_ci			hdrlen += 8;
69062306a36Sopenharmony_ci			/* 8 byte MIC */
69162306a36Sopenharmony_ci			trimlen = 8;
69262306a36Sopenharmony_ci			break;
69362306a36Sopenharmony_ci		}
69462306a36Sopenharmony_ci
69562306a36Sopenharmony_ci		if (skb->len < trimlen)
69662306a36Sopenharmony_ci			goto rx_dropped;
69762306a36Sopenharmony_ci
69862306a36Sopenharmony_ci		__skb_trim(skb, skb->len - trimlen);
69962306a36Sopenharmony_ci
70062306a36Sopenharmony_ci		if (skb->len < hdrlen)
70162306a36Sopenharmony_ci			goto rx_dropped;
70262306a36Sopenharmony_ci	}
70362306a36Sopenharmony_ci
70462306a36Sopenharmony_ci	/* skb: hdr + (possible reassembled) full plaintext payload */
70562306a36Sopenharmony_ci
70662306a36Sopenharmony_ci	payload = skb->data + hdrlen;
70762306a36Sopenharmony_ci	ethertype = (payload[6] << 8) | payload[7];
70862306a36Sopenharmony_ci
70962306a36Sopenharmony_ci#ifdef NOT_YET
71062306a36Sopenharmony_ci	/* If IEEE 802.1X is used, check whether the port is authorized to send
71162306a36Sopenharmony_ci	 * the received frame. */
71262306a36Sopenharmony_ci	if (ieee->ieee802_1x && ieee->iw_mode == IW_MODE_MASTER) {
71362306a36Sopenharmony_ci		if (ethertype == ETH_P_PAE) {
71462306a36Sopenharmony_ci			printk(KERN_DEBUG "%s: RX: IEEE 802.1X frame\n",
71562306a36Sopenharmony_ci			       dev->name);
71662306a36Sopenharmony_ci			if (ieee->hostapd && ieee->apdev) {
71762306a36Sopenharmony_ci				/* Send IEEE 802.1X frames to the user
71862306a36Sopenharmony_ci				 * space daemon for processing */
71962306a36Sopenharmony_ci				prism2_rx_80211(ieee->apdev, skb, rx_stats,
72062306a36Sopenharmony_ci						PRISM2_RX_MGMT);
72162306a36Sopenharmony_ci				ieee->apdevstats.rx_packets++;
72262306a36Sopenharmony_ci				ieee->apdevstats.rx_bytes += skb->len;
72362306a36Sopenharmony_ci				goto rx_exit;
72462306a36Sopenharmony_ci			}
72562306a36Sopenharmony_ci		} else if (!frame_authorized) {
72662306a36Sopenharmony_ci			printk(KERN_DEBUG "%s: dropped frame from "
72762306a36Sopenharmony_ci			       "unauthorized port (IEEE 802.1X): "
72862306a36Sopenharmony_ci			       "ethertype=0x%04x\n", dev->name, ethertype);
72962306a36Sopenharmony_ci			goto rx_dropped;
73062306a36Sopenharmony_ci		}
73162306a36Sopenharmony_ci	}
73262306a36Sopenharmony_ci#endif
73362306a36Sopenharmony_ci
73462306a36Sopenharmony_ci	/* convert hdr + possible LLC headers into Ethernet header */
73562306a36Sopenharmony_ci	if (skb->len - hdrlen >= 8 &&
73662306a36Sopenharmony_ci	    ((memcmp(payload, libipw_rfc1042_header, SNAP_SIZE) == 0 &&
73762306a36Sopenharmony_ci	      ethertype != ETH_P_AARP && ethertype != ETH_P_IPX) ||
73862306a36Sopenharmony_ci	     memcmp(payload, libipw_bridge_tunnel_header, SNAP_SIZE) == 0)) {
73962306a36Sopenharmony_ci		/* remove RFC1042 or Bridge-Tunnel encapsulation and
74062306a36Sopenharmony_ci		 * replace EtherType */
74162306a36Sopenharmony_ci		skb_pull(skb, hdrlen + SNAP_SIZE);
74262306a36Sopenharmony_ci		memcpy(skb_push(skb, ETH_ALEN), src, ETH_ALEN);
74362306a36Sopenharmony_ci		memcpy(skb_push(skb, ETH_ALEN), dst, ETH_ALEN);
74462306a36Sopenharmony_ci	} else {
74562306a36Sopenharmony_ci		__be16 len;
74662306a36Sopenharmony_ci		/* Leave Ethernet header part of hdr and full payload */
74762306a36Sopenharmony_ci		skb_pull(skb, hdrlen);
74862306a36Sopenharmony_ci		len = htons(skb->len);
74962306a36Sopenharmony_ci		memcpy(skb_push(skb, 2), &len, 2);
75062306a36Sopenharmony_ci		memcpy(skb_push(skb, ETH_ALEN), src, ETH_ALEN);
75162306a36Sopenharmony_ci		memcpy(skb_push(skb, ETH_ALEN), dst, ETH_ALEN);
75262306a36Sopenharmony_ci	}
75362306a36Sopenharmony_ci
75462306a36Sopenharmony_ci#ifdef NOT_YET
75562306a36Sopenharmony_ci	if (wds && ((fc & (IEEE80211_FCTL_TODS | IEEE80211_FCTL_FROMDS)) ==
75662306a36Sopenharmony_ci		    IEEE80211_FCTL_TODS) && skb->len >= ETH_HLEN + ETH_ALEN) {
75762306a36Sopenharmony_ci		/* Non-standard frame: get addr4 from its bogus location after
75862306a36Sopenharmony_ci		 * the payload */
75962306a36Sopenharmony_ci		skb_copy_to_linear_data_offset(skb, ETH_ALEN,
76062306a36Sopenharmony_ci					       skb->data + skb->len - ETH_ALEN,
76162306a36Sopenharmony_ci					       ETH_ALEN);
76262306a36Sopenharmony_ci		skb_trim(skb, skb->len - ETH_ALEN);
76362306a36Sopenharmony_ci	}
76462306a36Sopenharmony_ci#endif
76562306a36Sopenharmony_ci
76662306a36Sopenharmony_ci	dev->stats.rx_packets++;
76762306a36Sopenharmony_ci	dev->stats.rx_bytes += skb->len;
76862306a36Sopenharmony_ci
76962306a36Sopenharmony_ci#ifdef NOT_YET
77062306a36Sopenharmony_ci	if (ieee->iw_mode == IW_MODE_MASTER && !wds && ieee->ap->bridge_packets) {
77162306a36Sopenharmony_ci		if (is_multicast_ether_addr(dst)) {
77262306a36Sopenharmony_ci			/* copy multicast frame both to the higher layers and
77362306a36Sopenharmony_ci			 * to the wireless media */
77462306a36Sopenharmony_ci			ieee->ap->bridged_multicast++;
77562306a36Sopenharmony_ci			skb2 = skb_clone(skb, GFP_ATOMIC);
77662306a36Sopenharmony_ci			if (skb2 == NULL)
77762306a36Sopenharmony_ci				printk(KERN_DEBUG "%s: skb_clone failed for "
77862306a36Sopenharmony_ci				       "multicast frame\n", dev->name);
77962306a36Sopenharmony_ci		} else if (hostap_is_sta_assoc(ieee->ap, dst)) {
78062306a36Sopenharmony_ci			/* send frame directly to the associated STA using
78162306a36Sopenharmony_ci			 * wireless media and not passing to higher layers */
78262306a36Sopenharmony_ci			ieee->ap->bridged_unicast++;
78362306a36Sopenharmony_ci			skb2 = skb;
78462306a36Sopenharmony_ci			skb = NULL;
78562306a36Sopenharmony_ci		}
78662306a36Sopenharmony_ci	}
78762306a36Sopenharmony_ci
78862306a36Sopenharmony_ci	if (skb2 != NULL) {
78962306a36Sopenharmony_ci		/* send to wireless media */
79062306a36Sopenharmony_ci		skb2->dev = dev;
79162306a36Sopenharmony_ci		skb2->protocol = htons(ETH_P_802_3);
79262306a36Sopenharmony_ci		skb_reset_mac_header(skb2);
79362306a36Sopenharmony_ci		skb_reset_network_header(skb2);
79462306a36Sopenharmony_ci		/* skb2->network_header += ETH_HLEN; */
79562306a36Sopenharmony_ci		dev_queue_xmit(skb2);
79662306a36Sopenharmony_ci	}
79762306a36Sopenharmony_ci#endif
79862306a36Sopenharmony_ci
79962306a36Sopenharmony_ci	if (skb) {
80062306a36Sopenharmony_ci		skb->protocol = eth_type_trans(skb, dev);
80162306a36Sopenharmony_ci		memset(skb->cb, 0, sizeof(skb->cb));
80262306a36Sopenharmony_ci		skb->ip_summed = CHECKSUM_NONE;	/* 802.11 crc not sufficient */
80362306a36Sopenharmony_ci		if (netif_rx(skb) == NET_RX_DROP) {
80462306a36Sopenharmony_ci			/* netif_rx always succeeds, but it might drop
80562306a36Sopenharmony_ci			 * the packet.  If it drops the packet, we log that
80662306a36Sopenharmony_ci			 * in our stats. */
80762306a36Sopenharmony_ci			LIBIPW_DEBUG_DROP
80862306a36Sopenharmony_ci			    ("RX: netif_rx dropped the packet\n");
80962306a36Sopenharmony_ci			dev->stats.rx_dropped++;
81062306a36Sopenharmony_ci		}
81162306a36Sopenharmony_ci	}
81262306a36Sopenharmony_ci
81362306a36Sopenharmony_ci      rx_exit:
81462306a36Sopenharmony_ci#ifdef NOT_YET
81562306a36Sopenharmony_ci	if (sta)
81662306a36Sopenharmony_ci		hostap_handle_sta_release(sta);
81762306a36Sopenharmony_ci#endif
81862306a36Sopenharmony_ci	return 1;
81962306a36Sopenharmony_ci
82062306a36Sopenharmony_ci      rx_dropped:
82162306a36Sopenharmony_ci	dev->stats.rx_dropped++;
82262306a36Sopenharmony_ci
82362306a36Sopenharmony_ci	/* Returning 0 indicates to caller that we have not handled the SKB--
82462306a36Sopenharmony_ci	 * so it is still allocated and can be used again by underlying
82562306a36Sopenharmony_ci	 * hardware as a DMA target */
82662306a36Sopenharmony_ci	return 0;
82762306a36Sopenharmony_ci}
82862306a36Sopenharmony_ci
82962306a36Sopenharmony_ci/* Filter out unrelated packets, call libipw_rx[_mgt]
83062306a36Sopenharmony_ci * This function takes over the skb, it should not be used again after calling
83162306a36Sopenharmony_ci * this function. */
83262306a36Sopenharmony_civoid libipw_rx_any(struct libipw_device *ieee,
83362306a36Sopenharmony_ci		     struct sk_buff *skb, struct libipw_rx_stats *stats)
83462306a36Sopenharmony_ci{
83562306a36Sopenharmony_ci	struct libipw_hdr_4addr *hdr;
83662306a36Sopenharmony_ci	int is_packet_for_us;
83762306a36Sopenharmony_ci	u16 fc;
83862306a36Sopenharmony_ci
83962306a36Sopenharmony_ci	if (ieee->iw_mode == IW_MODE_MONITOR) {
84062306a36Sopenharmony_ci		if (!libipw_rx(ieee, skb, stats))
84162306a36Sopenharmony_ci			dev_kfree_skb_irq(skb);
84262306a36Sopenharmony_ci		return;
84362306a36Sopenharmony_ci	}
84462306a36Sopenharmony_ci
84562306a36Sopenharmony_ci	if (skb->len < sizeof(struct ieee80211_hdr))
84662306a36Sopenharmony_ci		goto drop_free;
84762306a36Sopenharmony_ci
84862306a36Sopenharmony_ci	hdr = (struct libipw_hdr_4addr *)skb->data;
84962306a36Sopenharmony_ci	fc = le16_to_cpu(hdr->frame_ctl);
85062306a36Sopenharmony_ci
85162306a36Sopenharmony_ci	if ((fc & IEEE80211_FCTL_VERS) != 0)
85262306a36Sopenharmony_ci		goto drop_free;
85362306a36Sopenharmony_ci
85462306a36Sopenharmony_ci	switch (fc & IEEE80211_FCTL_FTYPE) {
85562306a36Sopenharmony_ci	case IEEE80211_FTYPE_MGMT:
85662306a36Sopenharmony_ci		if (skb->len < sizeof(struct libipw_hdr_3addr))
85762306a36Sopenharmony_ci			goto drop_free;
85862306a36Sopenharmony_ci		libipw_rx_mgt(ieee, hdr, stats);
85962306a36Sopenharmony_ci		dev_kfree_skb_irq(skb);
86062306a36Sopenharmony_ci		return;
86162306a36Sopenharmony_ci	case IEEE80211_FTYPE_DATA:
86262306a36Sopenharmony_ci		break;
86362306a36Sopenharmony_ci	case IEEE80211_FTYPE_CTL:
86462306a36Sopenharmony_ci		return;
86562306a36Sopenharmony_ci	default:
86662306a36Sopenharmony_ci		return;
86762306a36Sopenharmony_ci	}
86862306a36Sopenharmony_ci
86962306a36Sopenharmony_ci	is_packet_for_us = 0;
87062306a36Sopenharmony_ci	switch (ieee->iw_mode) {
87162306a36Sopenharmony_ci	case IW_MODE_ADHOC:
87262306a36Sopenharmony_ci		/* our BSS and not from/to DS */
87362306a36Sopenharmony_ci		if (ether_addr_equal(hdr->addr3, ieee->bssid))
87462306a36Sopenharmony_ci		if ((fc & (IEEE80211_FCTL_TODS+IEEE80211_FCTL_FROMDS)) == 0) {
87562306a36Sopenharmony_ci			/* promisc: get all */
87662306a36Sopenharmony_ci			if (ieee->dev->flags & IFF_PROMISC)
87762306a36Sopenharmony_ci				is_packet_for_us = 1;
87862306a36Sopenharmony_ci			/* to us */
87962306a36Sopenharmony_ci			else if (ether_addr_equal(hdr->addr1, ieee->dev->dev_addr))
88062306a36Sopenharmony_ci				is_packet_for_us = 1;
88162306a36Sopenharmony_ci			/* mcast */
88262306a36Sopenharmony_ci			else if (is_multicast_ether_addr(hdr->addr1))
88362306a36Sopenharmony_ci				is_packet_for_us = 1;
88462306a36Sopenharmony_ci		}
88562306a36Sopenharmony_ci		break;
88662306a36Sopenharmony_ci	case IW_MODE_INFRA:
88762306a36Sopenharmony_ci		/* our BSS (== from our AP) and from DS */
88862306a36Sopenharmony_ci		if (ether_addr_equal(hdr->addr2, ieee->bssid))
88962306a36Sopenharmony_ci		if ((fc & (IEEE80211_FCTL_TODS+IEEE80211_FCTL_FROMDS)) == IEEE80211_FCTL_FROMDS) {
89062306a36Sopenharmony_ci			/* promisc: get all */
89162306a36Sopenharmony_ci			if (ieee->dev->flags & IFF_PROMISC)
89262306a36Sopenharmony_ci				is_packet_for_us = 1;
89362306a36Sopenharmony_ci			/* to us */
89462306a36Sopenharmony_ci			else if (ether_addr_equal(hdr->addr1, ieee->dev->dev_addr))
89562306a36Sopenharmony_ci				is_packet_for_us = 1;
89662306a36Sopenharmony_ci			/* mcast */
89762306a36Sopenharmony_ci			else if (is_multicast_ether_addr(hdr->addr1)) {
89862306a36Sopenharmony_ci				/* not our own packet bcasted from AP */
89962306a36Sopenharmony_ci				if (!ether_addr_equal(hdr->addr3, ieee->dev->dev_addr))
90062306a36Sopenharmony_ci					is_packet_for_us = 1;
90162306a36Sopenharmony_ci			}
90262306a36Sopenharmony_ci		}
90362306a36Sopenharmony_ci		break;
90462306a36Sopenharmony_ci	default:
90562306a36Sopenharmony_ci		/* ? */
90662306a36Sopenharmony_ci		break;
90762306a36Sopenharmony_ci	}
90862306a36Sopenharmony_ci
90962306a36Sopenharmony_ci	if (is_packet_for_us)
91062306a36Sopenharmony_ci		if (!libipw_rx(ieee, skb, stats))
91162306a36Sopenharmony_ci			dev_kfree_skb_irq(skb);
91262306a36Sopenharmony_ci	return;
91362306a36Sopenharmony_ci
91462306a36Sopenharmony_cidrop_free:
91562306a36Sopenharmony_ci	dev_kfree_skb_irq(skb);
91662306a36Sopenharmony_ci	ieee->dev->stats.rx_dropped++;
91762306a36Sopenharmony_ci}
91862306a36Sopenharmony_ci
91962306a36Sopenharmony_ci#define MGMT_FRAME_FIXED_PART_LENGTH		0x24
92062306a36Sopenharmony_ci
92162306a36Sopenharmony_cistatic u8 qos_oui[QOS_OUI_LEN] = { 0x00, 0x50, 0xF2 };
92262306a36Sopenharmony_ci
92362306a36Sopenharmony_ci/*
92462306a36Sopenharmony_ci* Make the structure we read from the beacon packet to have
92562306a36Sopenharmony_ci* the right values
92662306a36Sopenharmony_ci*/
92762306a36Sopenharmony_cistatic int libipw_verify_qos_info(struct libipw_qos_information_element
92862306a36Sopenharmony_ci				     *info_element, int sub_type)
92962306a36Sopenharmony_ci{
93062306a36Sopenharmony_ci	if (info_element->elementID != QOS_ELEMENT_ID)
93162306a36Sopenharmony_ci		return -1;
93262306a36Sopenharmony_ci	if (info_element->qui_subtype != sub_type)
93362306a36Sopenharmony_ci		return -1;
93462306a36Sopenharmony_ci	if (memcmp(info_element->qui, qos_oui, QOS_OUI_LEN))
93562306a36Sopenharmony_ci		return -1;
93662306a36Sopenharmony_ci	if (info_element->qui_type != QOS_OUI_TYPE)
93762306a36Sopenharmony_ci		return -1;
93862306a36Sopenharmony_ci	if (info_element->version != QOS_VERSION_1)
93962306a36Sopenharmony_ci		return -1;
94062306a36Sopenharmony_ci
94162306a36Sopenharmony_ci	return 0;
94262306a36Sopenharmony_ci}
94362306a36Sopenharmony_ci
94462306a36Sopenharmony_ci/*
94562306a36Sopenharmony_ci * Parse a QoS parameter element
94662306a36Sopenharmony_ci */
94762306a36Sopenharmony_cistatic int libipw_read_qos_param_element(
94862306a36Sopenharmony_ci			struct libipw_qos_parameter_info *element_param,
94962306a36Sopenharmony_ci			struct libipw_info_element *info_element)
95062306a36Sopenharmony_ci{
95162306a36Sopenharmony_ci	size_t size = sizeof(*element_param);
95262306a36Sopenharmony_ci
95362306a36Sopenharmony_ci	if (!element_param || !info_element || info_element->len != size - 2)
95462306a36Sopenharmony_ci		return -1;
95562306a36Sopenharmony_ci
95662306a36Sopenharmony_ci	memcpy(element_param, info_element, size);
95762306a36Sopenharmony_ci	return libipw_verify_qos_info(&element_param->info_element,
95862306a36Sopenharmony_ci				      QOS_OUI_PARAM_SUB_TYPE);
95962306a36Sopenharmony_ci}
96062306a36Sopenharmony_ci
96162306a36Sopenharmony_ci/*
96262306a36Sopenharmony_ci * Parse a QoS information element
96362306a36Sopenharmony_ci */
96462306a36Sopenharmony_cistatic int libipw_read_qos_info_element(
96562306a36Sopenharmony_ci			struct libipw_qos_information_element *element_info,
96662306a36Sopenharmony_ci			struct libipw_info_element *info_element)
96762306a36Sopenharmony_ci{
96862306a36Sopenharmony_ci	size_t size = sizeof(struct libipw_qos_information_element) - 2;
96962306a36Sopenharmony_ci
97062306a36Sopenharmony_ci	if (!element_info || !info_element || info_element->len != size - 2)
97162306a36Sopenharmony_ci		return -1;
97262306a36Sopenharmony_ci
97362306a36Sopenharmony_ci	memcpy(element_info, info_element, size);
97462306a36Sopenharmony_ci	return libipw_verify_qos_info(element_info, QOS_OUI_INFO_SUB_TYPE);
97562306a36Sopenharmony_ci}
97662306a36Sopenharmony_ci
97762306a36Sopenharmony_ci/*
97862306a36Sopenharmony_ci * Write QoS parameters from the ac parameters.
97962306a36Sopenharmony_ci */
98062306a36Sopenharmony_cistatic void libipw_qos_convert_ac_to_parameters(struct
98162306a36Sopenharmony_ci						  libipw_qos_parameter_info
98262306a36Sopenharmony_ci						  *param_elm, struct
98362306a36Sopenharmony_ci						  libipw_qos_parameters
98462306a36Sopenharmony_ci						  *qos_param)
98562306a36Sopenharmony_ci{
98662306a36Sopenharmony_ci	int i;
98762306a36Sopenharmony_ci	struct libipw_qos_ac_parameter *ac_params;
98862306a36Sopenharmony_ci	u32 txop;
98962306a36Sopenharmony_ci	u8 cw_min;
99062306a36Sopenharmony_ci	u8 cw_max;
99162306a36Sopenharmony_ci
99262306a36Sopenharmony_ci	for (i = 0; i < QOS_QUEUE_NUM; i++) {
99362306a36Sopenharmony_ci		ac_params = &(param_elm->ac_params_record[i]);
99462306a36Sopenharmony_ci
99562306a36Sopenharmony_ci		qos_param->aifs[i] = (ac_params->aci_aifsn) & 0x0F;
99662306a36Sopenharmony_ci		qos_param->aifs[i] -= (qos_param->aifs[i] < 2) ? 0 : 2;
99762306a36Sopenharmony_ci
99862306a36Sopenharmony_ci		cw_min = ac_params->ecw_min_max & 0x0F;
99962306a36Sopenharmony_ci		qos_param->cw_min[i] = cpu_to_le16((1 << cw_min) - 1);
100062306a36Sopenharmony_ci
100162306a36Sopenharmony_ci		cw_max = (ac_params->ecw_min_max & 0xF0) >> 4;
100262306a36Sopenharmony_ci		qos_param->cw_max[i] = cpu_to_le16((1 << cw_max) - 1);
100362306a36Sopenharmony_ci
100462306a36Sopenharmony_ci		qos_param->flag[i] =
100562306a36Sopenharmony_ci		    (ac_params->aci_aifsn & 0x10) ? 0x01 : 0x00;
100662306a36Sopenharmony_ci
100762306a36Sopenharmony_ci		txop = le16_to_cpu(ac_params->tx_op_limit) * 32;
100862306a36Sopenharmony_ci		qos_param->tx_op_limit[i] = cpu_to_le16(txop);
100962306a36Sopenharmony_ci	}
101062306a36Sopenharmony_ci}
101162306a36Sopenharmony_ci
101262306a36Sopenharmony_ci/*
101362306a36Sopenharmony_ci * we have a generic data element which it may contain QoS information or
101462306a36Sopenharmony_ci * parameters element. check the information element length to decide
101562306a36Sopenharmony_ci * which type to read
101662306a36Sopenharmony_ci */
101762306a36Sopenharmony_cistatic int libipw_parse_qos_info_param_IE(struct libipw_info_element
101862306a36Sopenharmony_ci					     *info_element,
101962306a36Sopenharmony_ci					     struct libipw_network *network)
102062306a36Sopenharmony_ci{
102162306a36Sopenharmony_ci	int rc = 0;
102262306a36Sopenharmony_ci	struct libipw_qos_parameters *qos_param = NULL;
102362306a36Sopenharmony_ci	struct libipw_qos_information_element qos_info_element;
102462306a36Sopenharmony_ci
102562306a36Sopenharmony_ci	rc = libipw_read_qos_info_element(&qos_info_element, info_element);
102662306a36Sopenharmony_ci
102762306a36Sopenharmony_ci	if (rc == 0) {
102862306a36Sopenharmony_ci		network->qos_data.param_count = qos_info_element.ac_info & 0x0F;
102962306a36Sopenharmony_ci		network->flags |= NETWORK_HAS_QOS_INFORMATION;
103062306a36Sopenharmony_ci	} else {
103162306a36Sopenharmony_ci		struct libipw_qos_parameter_info param_element;
103262306a36Sopenharmony_ci
103362306a36Sopenharmony_ci		rc = libipw_read_qos_param_element(&param_element,
103462306a36Sopenharmony_ci						      info_element);
103562306a36Sopenharmony_ci		if (rc == 0) {
103662306a36Sopenharmony_ci			qos_param = &(network->qos_data.parameters);
103762306a36Sopenharmony_ci			libipw_qos_convert_ac_to_parameters(&param_element,
103862306a36Sopenharmony_ci							       qos_param);
103962306a36Sopenharmony_ci			network->flags |= NETWORK_HAS_QOS_PARAMETERS;
104062306a36Sopenharmony_ci			network->qos_data.param_count =
104162306a36Sopenharmony_ci			    param_element.info_element.ac_info & 0x0F;
104262306a36Sopenharmony_ci		}
104362306a36Sopenharmony_ci	}
104462306a36Sopenharmony_ci
104562306a36Sopenharmony_ci	if (rc == 0) {
104662306a36Sopenharmony_ci		LIBIPW_DEBUG_QOS("QoS is supported\n");
104762306a36Sopenharmony_ci		network->qos_data.supported = 1;
104862306a36Sopenharmony_ci	}
104962306a36Sopenharmony_ci	return rc;
105062306a36Sopenharmony_ci}
105162306a36Sopenharmony_ci
105262306a36Sopenharmony_ci#ifdef CONFIG_LIBIPW_DEBUG
105362306a36Sopenharmony_ci#define MFIE_STRING(x) case WLAN_EID_ ##x: return #x
105462306a36Sopenharmony_ci
105562306a36Sopenharmony_cistatic const char *get_info_element_string(u16 id)
105662306a36Sopenharmony_ci{
105762306a36Sopenharmony_ci	switch (id) {
105862306a36Sopenharmony_ci		MFIE_STRING(SSID);
105962306a36Sopenharmony_ci		MFIE_STRING(SUPP_RATES);
106062306a36Sopenharmony_ci		MFIE_STRING(FH_PARAMS);
106162306a36Sopenharmony_ci		MFIE_STRING(DS_PARAMS);
106262306a36Sopenharmony_ci		MFIE_STRING(CF_PARAMS);
106362306a36Sopenharmony_ci		MFIE_STRING(TIM);
106462306a36Sopenharmony_ci		MFIE_STRING(IBSS_PARAMS);
106562306a36Sopenharmony_ci		MFIE_STRING(COUNTRY);
106662306a36Sopenharmony_ci		MFIE_STRING(REQUEST);
106762306a36Sopenharmony_ci		MFIE_STRING(CHALLENGE);
106862306a36Sopenharmony_ci		MFIE_STRING(PWR_CONSTRAINT);
106962306a36Sopenharmony_ci		MFIE_STRING(PWR_CAPABILITY);
107062306a36Sopenharmony_ci		MFIE_STRING(TPC_REQUEST);
107162306a36Sopenharmony_ci		MFIE_STRING(TPC_REPORT);
107262306a36Sopenharmony_ci		MFIE_STRING(SUPPORTED_CHANNELS);
107362306a36Sopenharmony_ci		MFIE_STRING(CHANNEL_SWITCH);
107462306a36Sopenharmony_ci		MFIE_STRING(MEASURE_REQUEST);
107562306a36Sopenharmony_ci		MFIE_STRING(MEASURE_REPORT);
107662306a36Sopenharmony_ci		MFIE_STRING(QUIET);
107762306a36Sopenharmony_ci		MFIE_STRING(IBSS_DFS);
107862306a36Sopenharmony_ci		MFIE_STRING(ERP_INFO);
107962306a36Sopenharmony_ci		MFIE_STRING(RSN);
108062306a36Sopenharmony_ci		MFIE_STRING(EXT_SUPP_RATES);
108162306a36Sopenharmony_ci		MFIE_STRING(VENDOR_SPECIFIC);
108262306a36Sopenharmony_ci		MFIE_STRING(QOS_PARAMETER);
108362306a36Sopenharmony_ci	default:
108462306a36Sopenharmony_ci		return "UNKNOWN";
108562306a36Sopenharmony_ci	}
108662306a36Sopenharmony_ci}
108762306a36Sopenharmony_ci#endif
108862306a36Sopenharmony_ci
108962306a36Sopenharmony_cistatic int libipw_parse_info_param(struct libipw_info_element
109062306a36Sopenharmony_ci				      *info_element, u16 length,
109162306a36Sopenharmony_ci				      struct libipw_network *network)
109262306a36Sopenharmony_ci{
109362306a36Sopenharmony_ci	u8 i;
109462306a36Sopenharmony_ci#ifdef CONFIG_LIBIPW_DEBUG
109562306a36Sopenharmony_ci	char rates_str[64];
109662306a36Sopenharmony_ci	char *p;
109762306a36Sopenharmony_ci#endif
109862306a36Sopenharmony_ci
109962306a36Sopenharmony_ci	while (length >= sizeof(*info_element)) {
110062306a36Sopenharmony_ci		if (sizeof(*info_element) + info_element->len > length) {
110162306a36Sopenharmony_ci			LIBIPW_DEBUG_MGMT("Info elem: parse failed: "
110262306a36Sopenharmony_ci					     "info_element->len + 2 > left : "
110362306a36Sopenharmony_ci					     "info_element->len+2=%zd left=%d, id=%d.\n",
110462306a36Sopenharmony_ci					     info_element->len +
110562306a36Sopenharmony_ci					     sizeof(*info_element),
110662306a36Sopenharmony_ci					     length, info_element->id);
110762306a36Sopenharmony_ci			/* We stop processing but don't return an error here
110862306a36Sopenharmony_ci			 * because some misbehaviour APs break this rule. ie.
110962306a36Sopenharmony_ci			 * Orinoco AP1000. */
111062306a36Sopenharmony_ci			break;
111162306a36Sopenharmony_ci		}
111262306a36Sopenharmony_ci
111362306a36Sopenharmony_ci		switch (info_element->id) {
111462306a36Sopenharmony_ci		case WLAN_EID_SSID:
111562306a36Sopenharmony_ci			network->ssid_len = min(info_element->len,
111662306a36Sopenharmony_ci						(u8) IW_ESSID_MAX_SIZE);
111762306a36Sopenharmony_ci			memcpy(network->ssid, info_element->data,
111862306a36Sopenharmony_ci			       network->ssid_len);
111962306a36Sopenharmony_ci			if (network->ssid_len < IW_ESSID_MAX_SIZE)
112062306a36Sopenharmony_ci				memset(network->ssid + network->ssid_len, 0,
112162306a36Sopenharmony_ci				       IW_ESSID_MAX_SIZE - network->ssid_len);
112262306a36Sopenharmony_ci
112362306a36Sopenharmony_ci			LIBIPW_DEBUG_MGMT("WLAN_EID_SSID: '%*pE' len=%d.\n",
112462306a36Sopenharmony_ci					  network->ssid_len, network->ssid,
112562306a36Sopenharmony_ci					  network->ssid_len);
112662306a36Sopenharmony_ci			break;
112762306a36Sopenharmony_ci
112862306a36Sopenharmony_ci		case WLAN_EID_SUPP_RATES:
112962306a36Sopenharmony_ci#ifdef CONFIG_LIBIPW_DEBUG
113062306a36Sopenharmony_ci			p = rates_str;
113162306a36Sopenharmony_ci#endif
113262306a36Sopenharmony_ci			network->rates_len = min(info_element->len,
113362306a36Sopenharmony_ci						 MAX_RATES_LENGTH);
113462306a36Sopenharmony_ci			for (i = 0; i < network->rates_len; i++) {
113562306a36Sopenharmony_ci				network->rates[i] = info_element->data[i];
113662306a36Sopenharmony_ci#ifdef CONFIG_LIBIPW_DEBUG
113762306a36Sopenharmony_ci				p += scnprintf(p, sizeof(rates_str) -
113862306a36Sopenharmony_ci					      (p - rates_str), "%02X ",
113962306a36Sopenharmony_ci					      network->rates[i]);
114062306a36Sopenharmony_ci#endif
114162306a36Sopenharmony_ci				if (libipw_is_ofdm_rate
114262306a36Sopenharmony_ci				    (info_element->data[i])) {
114362306a36Sopenharmony_ci					network->flags |= NETWORK_HAS_OFDM;
114462306a36Sopenharmony_ci					if (info_element->data[i] &
114562306a36Sopenharmony_ci					    LIBIPW_BASIC_RATE_MASK)
114662306a36Sopenharmony_ci						network->flags &=
114762306a36Sopenharmony_ci						    ~NETWORK_HAS_CCK;
114862306a36Sopenharmony_ci				}
114962306a36Sopenharmony_ci			}
115062306a36Sopenharmony_ci
115162306a36Sopenharmony_ci			LIBIPW_DEBUG_MGMT("WLAN_EID_SUPP_RATES: '%s' (%d)\n",
115262306a36Sopenharmony_ci					     rates_str, network->rates_len);
115362306a36Sopenharmony_ci			break;
115462306a36Sopenharmony_ci
115562306a36Sopenharmony_ci		case WLAN_EID_EXT_SUPP_RATES:
115662306a36Sopenharmony_ci#ifdef CONFIG_LIBIPW_DEBUG
115762306a36Sopenharmony_ci			p = rates_str;
115862306a36Sopenharmony_ci#endif
115962306a36Sopenharmony_ci			network->rates_ex_len = min(info_element->len,
116062306a36Sopenharmony_ci						    MAX_RATES_EX_LENGTH);
116162306a36Sopenharmony_ci			for (i = 0; i < network->rates_ex_len; i++) {
116262306a36Sopenharmony_ci				network->rates_ex[i] = info_element->data[i];
116362306a36Sopenharmony_ci#ifdef CONFIG_LIBIPW_DEBUG
116462306a36Sopenharmony_ci				p += scnprintf(p, sizeof(rates_str) -
116562306a36Sopenharmony_ci					      (p - rates_str), "%02X ",
116662306a36Sopenharmony_ci					      network->rates_ex[i]);
116762306a36Sopenharmony_ci#endif
116862306a36Sopenharmony_ci				if (libipw_is_ofdm_rate
116962306a36Sopenharmony_ci				    (info_element->data[i])) {
117062306a36Sopenharmony_ci					network->flags |= NETWORK_HAS_OFDM;
117162306a36Sopenharmony_ci					if (info_element->data[i] &
117262306a36Sopenharmony_ci					    LIBIPW_BASIC_RATE_MASK)
117362306a36Sopenharmony_ci						network->flags &=
117462306a36Sopenharmony_ci						    ~NETWORK_HAS_CCK;
117562306a36Sopenharmony_ci				}
117662306a36Sopenharmony_ci			}
117762306a36Sopenharmony_ci
117862306a36Sopenharmony_ci			LIBIPW_DEBUG_MGMT("WLAN_EID_EXT_SUPP_RATES: '%s' (%d)\n",
117962306a36Sopenharmony_ci					     rates_str, network->rates_ex_len);
118062306a36Sopenharmony_ci			break;
118162306a36Sopenharmony_ci
118262306a36Sopenharmony_ci		case WLAN_EID_DS_PARAMS:
118362306a36Sopenharmony_ci			LIBIPW_DEBUG_MGMT("WLAN_EID_DS_PARAMS: %d\n",
118462306a36Sopenharmony_ci					     info_element->data[0]);
118562306a36Sopenharmony_ci			network->channel = info_element->data[0];
118662306a36Sopenharmony_ci			break;
118762306a36Sopenharmony_ci
118862306a36Sopenharmony_ci		case WLAN_EID_FH_PARAMS:
118962306a36Sopenharmony_ci			LIBIPW_DEBUG_MGMT("WLAN_EID_FH_PARAMS: ignored\n");
119062306a36Sopenharmony_ci			break;
119162306a36Sopenharmony_ci
119262306a36Sopenharmony_ci		case WLAN_EID_CF_PARAMS:
119362306a36Sopenharmony_ci			LIBIPW_DEBUG_MGMT("WLAN_EID_CF_PARAMS: ignored\n");
119462306a36Sopenharmony_ci			break;
119562306a36Sopenharmony_ci
119662306a36Sopenharmony_ci		case WLAN_EID_TIM:
119762306a36Sopenharmony_ci			network->tim.tim_count = info_element->data[0];
119862306a36Sopenharmony_ci			network->tim.tim_period = info_element->data[1];
119962306a36Sopenharmony_ci			LIBIPW_DEBUG_MGMT("WLAN_EID_TIM: partially ignored\n");
120062306a36Sopenharmony_ci			break;
120162306a36Sopenharmony_ci
120262306a36Sopenharmony_ci		case WLAN_EID_ERP_INFO:
120362306a36Sopenharmony_ci			network->erp_value = info_element->data[0];
120462306a36Sopenharmony_ci			network->flags |= NETWORK_HAS_ERP_VALUE;
120562306a36Sopenharmony_ci			LIBIPW_DEBUG_MGMT("MFIE_TYPE_ERP_SET: %d\n",
120662306a36Sopenharmony_ci					     network->erp_value);
120762306a36Sopenharmony_ci			break;
120862306a36Sopenharmony_ci
120962306a36Sopenharmony_ci		case WLAN_EID_IBSS_PARAMS:
121062306a36Sopenharmony_ci			network->atim_window = info_element->data[0];
121162306a36Sopenharmony_ci			LIBIPW_DEBUG_MGMT("WLAN_EID_IBSS_PARAMS: %d\n",
121262306a36Sopenharmony_ci					     network->atim_window);
121362306a36Sopenharmony_ci			break;
121462306a36Sopenharmony_ci
121562306a36Sopenharmony_ci		case WLAN_EID_CHALLENGE:
121662306a36Sopenharmony_ci			LIBIPW_DEBUG_MGMT("WLAN_EID_CHALLENGE: ignored\n");
121762306a36Sopenharmony_ci			break;
121862306a36Sopenharmony_ci
121962306a36Sopenharmony_ci		case WLAN_EID_VENDOR_SPECIFIC:
122062306a36Sopenharmony_ci			LIBIPW_DEBUG_MGMT("WLAN_EID_VENDOR_SPECIFIC: %d bytes\n",
122162306a36Sopenharmony_ci					     info_element->len);
122262306a36Sopenharmony_ci			if (!libipw_parse_qos_info_param_IE(info_element,
122362306a36Sopenharmony_ci							       network))
122462306a36Sopenharmony_ci				break;
122562306a36Sopenharmony_ci
122662306a36Sopenharmony_ci			if (info_element->len >= 4 &&
122762306a36Sopenharmony_ci			    info_element->data[0] == 0x00 &&
122862306a36Sopenharmony_ci			    info_element->data[1] == 0x50 &&
122962306a36Sopenharmony_ci			    info_element->data[2] == 0xf2 &&
123062306a36Sopenharmony_ci			    info_element->data[3] == 0x01) {
123162306a36Sopenharmony_ci				network->wpa_ie_len = min(info_element->len + 2,
123262306a36Sopenharmony_ci							  MAX_WPA_IE_LEN);
123362306a36Sopenharmony_ci				memcpy(network->wpa_ie, info_element,
123462306a36Sopenharmony_ci				       network->wpa_ie_len);
123562306a36Sopenharmony_ci			}
123662306a36Sopenharmony_ci			break;
123762306a36Sopenharmony_ci
123862306a36Sopenharmony_ci		case WLAN_EID_RSN:
123962306a36Sopenharmony_ci			LIBIPW_DEBUG_MGMT("WLAN_EID_RSN: %d bytes\n",
124062306a36Sopenharmony_ci					     info_element->len);
124162306a36Sopenharmony_ci			network->rsn_ie_len = min(info_element->len + 2,
124262306a36Sopenharmony_ci						  MAX_WPA_IE_LEN);
124362306a36Sopenharmony_ci			memcpy(network->rsn_ie, info_element,
124462306a36Sopenharmony_ci			       network->rsn_ie_len);
124562306a36Sopenharmony_ci			break;
124662306a36Sopenharmony_ci
124762306a36Sopenharmony_ci		case WLAN_EID_QOS_PARAMETER:
124862306a36Sopenharmony_ci			printk(KERN_ERR
124962306a36Sopenharmony_ci			       "QoS Error need to parse QOS_PARAMETER IE\n");
125062306a36Sopenharmony_ci			break;
125162306a36Sopenharmony_ci			/* 802.11h */
125262306a36Sopenharmony_ci		case WLAN_EID_PWR_CONSTRAINT:
125362306a36Sopenharmony_ci			network->power_constraint = info_element->data[0];
125462306a36Sopenharmony_ci			network->flags |= NETWORK_HAS_POWER_CONSTRAINT;
125562306a36Sopenharmony_ci			break;
125662306a36Sopenharmony_ci
125762306a36Sopenharmony_ci		case WLAN_EID_CHANNEL_SWITCH:
125862306a36Sopenharmony_ci			network->power_constraint = info_element->data[0];
125962306a36Sopenharmony_ci			network->flags |= NETWORK_HAS_CSA;
126062306a36Sopenharmony_ci			break;
126162306a36Sopenharmony_ci
126262306a36Sopenharmony_ci		case WLAN_EID_QUIET:
126362306a36Sopenharmony_ci			network->quiet.count = info_element->data[0];
126462306a36Sopenharmony_ci			network->quiet.period = info_element->data[1];
126562306a36Sopenharmony_ci			network->quiet.duration = info_element->data[2];
126662306a36Sopenharmony_ci			network->quiet.offset = info_element->data[3];
126762306a36Sopenharmony_ci			network->flags |= NETWORK_HAS_QUIET;
126862306a36Sopenharmony_ci			break;
126962306a36Sopenharmony_ci
127062306a36Sopenharmony_ci		case WLAN_EID_IBSS_DFS:
127162306a36Sopenharmony_ci			network->flags |= NETWORK_HAS_IBSS_DFS;
127262306a36Sopenharmony_ci			break;
127362306a36Sopenharmony_ci
127462306a36Sopenharmony_ci		case WLAN_EID_TPC_REPORT:
127562306a36Sopenharmony_ci			network->tpc_report.transmit_power =
127662306a36Sopenharmony_ci			    info_element->data[0];
127762306a36Sopenharmony_ci			network->tpc_report.link_margin = info_element->data[1];
127862306a36Sopenharmony_ci			network->flags |= NETWORK_HAS_TPC_REPORT;
127962306a36Sopenharmony_ci			break;
128062306a36Sopenharmony_ci
128162306a36Sopenharmony_ci		default:
128262306a36Sopenharmony_ci			LIBIPW_DEBUG_MGMT
128362306a36Sopenharmony_ci			    ("Unsupported info element: %s (%d)\n",
128462306a36Sopenharmony_ci			     get_info_element_string(info_element->id),
128562306a36Sopenharmony_ci			     info_element->id);
128662306a36Sopenharmony_ci			break;
128762306a36Sopenharmony_ci		}
128862306a36Sopenharmony_ci
128962306a36Sopenharmony_ci		length -= sizeof(*info_element) + info_element->len;
129062306a36Sopenharmony_ci		info_element =
129162306a36Sopenharmony_ci		    (struct libipw_info_element *)&info_element->
129262306a36Sopenharmony_ci		    data[info_element->len];
129362306a36Sopenharmony_ci	}
129462306a36Sopenharmony_ci
129562306a36Sopenharmony_ci	return 0;
129662306a36Sopenharmony_ci}
129762306a36Sopenharmony_ci
129862306a36Sopenharmony_cistatic int libipw_handle_assoc_resp(struct libipw_device *ieee, struct libipw_assoc_response
129962306a36Sopenharmony_ci				       *frame, struct libipw_rx_stats *stats)
130062306a36Sopenharmony_ci{
130162306a36Sopenharmony_ci	struct libipw_network network_resp = { };
130262306a36Sopenharmony_ci	struct libipw_network *network = &network_resp;
130362306a36Sopenharmony_ci	struct net_device *dev = ieee->dev;
130462306a36Sopenharmony_ci
130562306a36Sopenharmony_ci	network->flags = 0;
130662306a36Sopenharmony_ci	network->qos_data.active = 0;
130762306a36Sopenharmony_ci	network->qos_data.supported = 0;
130862306a36Sopenharmony_ci	network->qos_data.param_count = 0;
130962306a36Sopenharmony_ci	network->qos_data.old_param_count = 0;
131062306a36Sopenharmony_ci
131162306a36Sopenharmony_ci	//network->atim_window = le16_to_cpu(frame->aid) & (0x3FFF);
131262306a36Sopenharmony_ci	network->atim_window = le16_to_cpu(frame->aid);
131362306a36Sopenharmony_ci	network->listen_interval = le16_to_cpu(frame->status);
131462306a36Sopenharmony_ci	memcpy(network->bssid, frame->header.addr3, ETH_ALEN);
131562306a36Sopenharmony_ci	network->capability = le16_to_cpu(frame->capability);
131662306a36Sopenharmony_ci	network->last_scanned = jiffies;
131762306a36Sopenharmony_ci	network->rates_len = network->rates_ex_len = 0;
131862306a36Sopenharmony_ci	network->last_associate = 0;
131962306a36Sopenharmony_ci	network->ssid_len = 0;
132062306a36Sopenharmony_ci	network->erp_value =
132162306a36Sopenharmony_ci	    (network->capability & WLAN_CAPABILITY_IBSS) ? 0x3 : 0x0;
132262306a36Sopenharmony_ci
132362306a36Sopenharmony_ci	if (stats->freq == LIBIPW_52GHZ_BAND) {
132462306a36Sopenharmony_ci		/* for A band (No DS info) */
132562306a36Sopenharmony_ci		network->channel = stats->received_channel;
132662306a36Sopenharmony_ci	} else
132762306a36Sopenharmony_ci		network->flags |= NETWORK_HAS_CCK;
132862306a36Sopenharmony_ci
132962306a36Sopenharmony_ci	network->wpa_ie_len = 0;
133062306a36Sopenharmony_ci	network->rsn_ie_len = 0;
133162306a36Sopenharmony_ci
133262306a36Sopenharmony_ci	if (libipw_parse_info_param((void *)frame->variable,
133362306a36Sopenharmony_ci				    stats->len - sizeof(*frame), network))
133462306a36Sopenharmony_ci		return 1;
133562306a36Sopenharmony_ci
133662306a36Sopenharmony_ci	network->mode = 0;
133762306a36Sopenharmony_ci	if (stats->freq == LIBIPW_52GHZ_BAND)
133862306a36Sopenharmony_ci		network->mode = IEEE_A;
133962306a36Sopenharmony_ci	else {
134062306a36Sopenharmony_ci		if (network->flags & NETWORK_HAS_OFDM)
134162306a36Sopenharmony_ci			network->mode |= IEEE_G;
134262306a36Sopenharmony_ci		if (network->flags & NETWORK_HAS_CCK)
134362306a36Sopenharmony_ci			network->mode |= IEEE_B;
134462306a36Sopenharmony_ci	}
134562306a36Sopenharmony_ci
134662306a36Sopenharmony_ci	memcpy(&network->stats, stats, sizeof(network->stats));
134762306a36Sopenharmony_ci
134862306a36Sopenharmony_ci	if (ieee->handle_assoc_response != NULL)
134962306a36Sopenharmony_ci		ieee->handle_assoc_response(dev, frame, network);
135062306a36Sopenharmony_ci
135162306a36Sopenharmony_ci	return 0;
135262306a36Sopenharmony_ci}
135362306a36Sopenharmony_ci
135462306a36Sopenharmony_ci/***************************************************/
135562306a36Sopenharmony_ci
135662306a36Sopenharmony_cistatic int libipw_network_init(struct libipw_device *ieee, struct libipw_probe_response
135762306a36Sopenharmony_ci					 *beacon,
135862306a36Sopenharmony_ci					 struct libipw_network *network,
135962306a36Sopenharmony_ci					 struct libipw_rx_stats *stats)
136062306a36Sopenharmony_ci{
136162306a36Sopenharmony_ci	network->qos_data.active = 0;
136262306a36Sopenharmony_ci	network->qos_data.supported = 0;
136362306a36Sopenharmony_ci	network->qos_data.param_count = 0;
136462306a36Sopenharmony_ci	network->qos_data.old_param_count = 0;
136562306a36Sopenharmony_ci
136662306a36Sopenharmony_ci	/* Pull out fixed field data */
136762306a36Sopenharmony_ci	memcpy(network->bssid, beacon->header.addr3, ETH_ALEN);
136862306a36Sopenharmony_ci	network->capability = le16_to_cpu(beacon->capability);
136962306a36Sopenharmony_ci	network->last_scanned = jiffies;
137062306a36Sopenharmony_ci	network->time_stamp[0] = le32_to_cpu(beacon->time_stamp[0]);
137162306a36Sopenharmony_ci	network->time_stamp[1] = le32_to_cpu(beacon->time_stamp[1]);
137262306a36Sopenharmony_ci	network->beacon_interval = le16_to_cpu(beacon->beacon_interval);
137362306a36Sopenharmony_ci	/* Where to pull this? beacon->listen_interval; */
137462306a36Sopenharmony_ci	network->listen_interval = 0x0A;
137562306a36Sopenharmony_ci	network->rates_len = network->rates_ex_len = 0;
137662306a36Sopenharmony_ci	network->last_associate = 0;
137762306a36Sopenharmony_ci	network->ssid_len = 0;
137862306a36Sopenharmony_ci	network->flags = 0;
137962306a36Sopenharmony_ci	network->atim_window = 0;
138062306a36Sopenharmony_ci	network->erp_value = (network->capability & WLAN_CAPABILITY_IBSS) ?
138162306a36Sopenharmony_ci	    0x3 : 0x0;
138262306a36Sopenharmony_ci
138362306a36Sopenharmony_ci	if (stats->freq == LIBIPW_52GHZ_BAND) {
138462306a36Sopenharmony_ci		/* for A band (No DS info) */
138562306a36Sopenharmony_ci		network->channel = stats->received_channel;
138662306a36Sopenharmony_ci	} else
138762306a36Sopenharmony_ci		network->flags |= NETWORK_HAS_CCK;
138862306a36Sopenharmony_ci
138962306a36Sopenharmony_ci	network->wpa_ie_len = 0;
139062306a36Sopenharmony_ci	network->rsn_ie_len = 0;
139162306a36Sopenharmony_ci
139262306a36Sopenharmony_ci	if (libipw_parse_info_param((void *)beacon->variable,
139362306a36Sopenharmony_ci				    stats->len - sizeof(*beacon), network))
139462306a36Sopenharmony_ci		return 1;
139562306a36Sopenharmony_ci
139662306a36Sopenharmony_ci	network->mode = 0;
139762306a36Sopenharmony_ci	if (stats->freq == LIBIPW_52GHZ_BAND)
139862306a36Sopenharmony_ci		network->mode = IEEE_A;
139962306a36Sopenharmony_ci	else {
140062306a36Sopenharmony_ci		if (network->flags & NETWORK_HAS_OFDM)
140162306a36Sopenharmony_ci			network->mode |= IEEE_G;
140262306a36Sopenharmony_ci		if (network->flags & NETWORK_HAS_CCK)
140362306a36Sopenharmony_ci			network->mode |= IEEE_B;
140462306a36Sopenharmony_ci	}
140562306a36Sopenharmony_ci
140662306a36Sopenharmony_ci	if (network->mode == 0) {
140762306a36Sopenharmony_ci		LIBIPW_DEBUG_SCAN("Filtered out '%*pE (%pM)' network.\n",
140862306a36Sopenharmony_ci				  network->ssid_len, network->ssid,
140962306a36Sopenharmony_ci				  network->bssid);
141062306a36Sopenharmony_ci		return 1;
141162306a36Sopenharmony_ci	}
141262306a36Sopenharmony_ci
141362306a36Sopenharmony_ci	memcpy(&network->stats, stats, sizeof(network->stats));
141462306a36Sopenharmony_ci
141562306a36Sopenharmony_ci	return 0;
141662306a36Sopenharmony_ci}
141762306a36Sopenharmony_ci
141862306a36Sopenharmony_cistatic inline int is_same_network(struct libipw_network *src,
141962306a36Sopenharmony_ci				  struct libipw_network *dst)
142062306a36Sopenharmony_ci{
142162306a36Sopenharmony_ci	/* A network is only a duplicate if the channel, BSSID, and ESSID
142262306a36Sopenharmony_ci	 * all match.  We treat all <hidden> with the same BSSID and channel
142362306a36Sopenharmony_ci	 * as one network */
142462306a36Sopenharmony_ci	return ((src->ssid_len == dst->ssid_len) &&
142562306a36Sopenharmony_ci		(src->channel == dst->channel) &&
142662306a36Sopenharmony_ci		ether_addr_equal_64bits(src->bssid, dst->bssid) &&
142762306a36Sopenharmony_ci		!memcmp(src->ssid, dst->ssid, src->ssid_len));
142862306a36Sopenharmony_ci}
142962306a36Sopenharmony_ci
143062306a36Sopenharmony_cistatic void update_network(struct libipw_network *dst,
143162306a36Sopenharmony_ci				  struct libipw_network *src)
143262306a36Sopenharmony_ci{
143362306a36Sopenharmony_ci	int qos_active;
143462306a36Sopenharmony_ci	u8 old_param;
143562306a36Sopenharmony_ci
143662306a36Sopenharmony_ci	/* We only update the statistics if they were created by receiving
143762306a36Sopenharmony_ci	 * the network information on the actual channel the network is on.
143862306a36Sopenharmony_ci	 *
143962306a36Sopenharmony_ci	 * This keeps beacons received on neighbor channels from bringing
144062306a36Sopenharmony_ci	 * down the signal level of an AP. */
144162306a36Sopenharmony_ci	if (dst->channel == src->stats.received_channel)
144262306a36Sopenharmony_ci		memcpy(&dst->stats, &src->stats,
144362306a36Sopenharmony_ci		       sizeof(struct libipw_rx_stats));
144462306a36Sopenharmony_ci	else
144562306a36Sopenharmony_ci		LIBIPW_DEBUG_SCAN("Network %pM info received "
144662306a36Sopenharmony_ci			"off channel (%d vs. %d)\n", src->bssid,
144762306a36Sopenharmony_ci			dst->channel, src->stats.received_channel);
144862306a36Sopenharmony_ci
144962306a36Sopenharmony_ci	dst->capability = src->capability;
145062306a36Sopenharmony_ci	memcpy(dst->rates, src->rates, src->rates_len);
145162306a36Sopenharmony_ci	dst->rates_len = src->rates_len;
145262306a36Sopenharmony_ci	memcpy(dst->rates_ex, src->rates_ex, src->rates_ex_len);
145362306a36Sopenharmony_ci	dst->rates_ex_len = src->rates_ex_len;
145462306a36Sopenharmony_ci
145562306a36Sopenharmony_ci	dst->mode = src->mode;
145662306a36Sopenharmony_ci	dst->flags = src->flags;
145762306a36Sopenharmony_ci	dst->time_stamp[0] = src->time_stamp[0];
145862306a36Sopenharmony_ci	dst->time_stamp[1] = src->time_stamp[1];
145962306a36Sopenharmony_ci
146062306a36Sopenharmony_ci	dst->beacon_interval = src->beacon_interval;
146162306a36Sopenharmony_ci	dst->listen_interval = src->listen_interval;
146262306a36Sopenharmony_ci	dst->atim_window = src->atim_window;
146362306a36Sopenharmony_ci	dst->erp_value = src->erp_value;
146462306a36Sopenharmony_ci	dst->tim = src->tim;
146562306a36Sopenharmony_ci
146662306a36Sopenharmony_ci	memcpy(dst->wpa_ie, src->wpa_ie, src->wpa_ie_len);
146762306a36Sopenharmony_ci	dst->wpa_ie_len = src->wpa_ie_len;
146862306a36Sopenharmony_ci	memcpy(dst->rsn_ie, src->rsn_ie, src->rsn_ie_len);
146962306a36Sopenharmony_ci	dst->rsn_ie_len = src->rsn_ie_len;
147062306a36Sopenharmony_ci
147162306a36Sopenharmony_ci	dst->last_scanned = jiffies;
147262306a36Sopenharmony_ci	qos_active = src->qos_data.active;
147362306a36Sopenharmony_ci	old_param = dst->qos_data.old_param_count;
147462306a36Sopenharmony_ci	if (dst->flags & NETWORK_HAS_QOS_MASK)
147562306a36Sopenharmony_ci		memcpy(&dst->qos_data, &src->qos_data,
147662306a36Sopenharmony_ci		       sizeof(struct libipw_qos_data));
147762306a36Sopenharmony_ci	else {
147862306a36Sopenharmony_ci		dst->qos_data.supported = src->qos_data.supported;
147962306a36Sopenharmony_ci		dst->qos_data.param_count = src->qos_data.param_count;
148062306a36Sopenharmony_ci	}
148162306a36Sopenharmony_ci
148262306a36Sopenharmony_ci	if (dst->qos_data.supported == 1) {
148362306a36Sopenharmony_ci		if (dst->ssid_len)
148462306a36Sopenharmony_ci			LIBIPW_DEBUG_QOS
148562306a36Sopenharmony_ci			    ("QoS the network %s is QoS supported\n",
148662306a36Sopenharmony_ci			     dst->ssid);
148762306a36Sopenharmony_ci		else
148862306a36Sopenharmony_ci			LIBIPW_DEBUG_QOS
148962306a36Sopenharmony_ci			    ("QoS the network is QoS supported\n");
149062306a36Sopenharmony_ci	}
149162306a36Sopenharmony_ci	dst->qos_data.active = qos_active;
149262306a36Sopenharmony_ci	dst->qos_data.old_param_count = old_param;
149362306a36Sopenharmony_ci
149462306a36Sopenharmony_ci	/* dst->last_associate is not overwritten */
149562306a36Sopenharmony_ci}
149662306a36Sopenharmony_ci
149762306a36Sopenharmony_cistatic inline int is_beacon(__le16 fc)
149862306a36Sopenharmony_ci{
149962306a36Sopenharmony_ci	return (WLAN_FC_GET_STYPE(le16_to_cpu(fc)) == IEEE80211_STYPE_BEACON);
150062306a36Sopenharmony_ci}
150162306a36Sopenharmony_ci
150262306a36Sopenharmony_cistatic void libipw_process_probe_response(struct libipw_device
150362306a36Sopenharmony_ci						    *ieee, struct
150462306a36Sopenharmony_ci						    libipw_probe_response
150562306a36Sopenharmony_ci						    *beacon, struct libipw_rx_stats
150662306a36Sopenharmony_ci						    *stats)
150762306a36Sopenharmony_ci{
150862306a36Sopenharmony_ci	struct net_device *dev = ieee->dev;
150962306a36Sopenharmony_ci	struct libipw_network network = { };
151062306a36Sopenharmony_ci	struct libipw_network *target;
151162306a36Sopenharmony_ci	struct libipw_network *oldest = NULL;
151262306a36Sopenharmony_ci#ifdef CONFIG_LIBIPW_DEBUG
151362306a36Sopenharmony_ci	struct libipw_info_element *info_element = (void *)beacon->variable;
151462306a36Sopenharmony_ci#endif
151562306a36Sopenharmony_ci	unsigned long flags;
151662306a36Sopenharmony_ci
151762306a36Sopenharmony_ci	LIBIPW_DEBUG_SCAN("'%*pE' (%pM): %c%c%c%c %c%c%c%c-%c%c%c%c %c%c%c%c\n",
151862306a36Sopenharmony_ci		     info_element->len, info_element->data,
151962306a36Sopenharmony_ci		     beacon->header.addr3,
152062306a36Sopenharmony_ci		     (beacon->capability & cpu_to_le16(1 << 0xf)) ? '1' : '0',
152162306a36Sopenharmony_ci		     (beacon->capability & cpu_to_le16(1 << 0xe)) ? '1' : '0',
152262306a36Sopenharmony_ci		     (beacon->capability & cpu_to_le16(1 << 0xd)) ? '1' : '0',
152362306a36Sopenharmony_ci		     (beacon->capability & cpu_to_le16(1 << 0xc)) ? '1' : '0',
152462306a36Sopenharmony_ci		     (beacon->capability & cpu_to_le16(1 << 0xb)) ? '1' : '0',
152562306a36Sopenharmony_ci		     (beacon->capability & cpu_to_le16(1 << 0xa)) ? '1' : '0',
152662306a36Sopenharmony_ci		     (beacon->capability & cpu_to_le16(1 << 0x9)) ? '1' : '0',
152762306a36Sopenharmony_ci		     (beacon->capability & cpu_to_le16(1 << 0x8)) ? '1' : '0',
152862306a36Sopenharmony_ci		     (beacon->capability & cpu_to_le16(1 << 0x7)) ? '1' : '0',
152962306a36Sopenharmony_ci		     (beacon->capability & cpu_to_le16(1 << 0x6)) ? '1' : '0',
153062306a36Sopenharmony_ci		     (beacon->capability & cpu_to_le16(1 << 0x5)) ? '1' : '0',
153162306a36Sopenharmony_ci		     (beacon->capability & cpu_to_le16(1 << 0x4)) ? '1' : '0',
153262306a36Sopenharmony_ci		     (beacon->capability & cpu_to_le16(1 << 0x3)) ? '1' : '0',
153362306a36Sopenharmony_ci		     (beacon->capability & cpu_to_le16(1 << 0x2)) ? '1' : '0',
153462306a36Sopenharmony_ci		     (beacon->capability & cpu_to_le16(1 << 0x1)) ? '1' : '0',
153562306a36Sopenharmony_ci		     (beacon->capability & cpu_to_le16(1 << 0x0)) ? '1' : '0');
153662306a36Sopenharmony_ci
153762306a36Sopenharmony_ci	if (libipw_network_init(ieee, beacon, &network, stats)) {
153862306a36Sopenharmony_ci		LIBIPW_DEBUG_SCAN("Dropped '%*pE' (%pM) via %s.\n",
153962306a36Sopenharmony_ci				  info_element->len, info_element->data,
154062306a36Sopenharmony_ci				  beacon->header.addr3,
154162306a36Sopenharmony_ci				  is_beacon(beacon->header.frame_ctl) ?
154262306a36Sopenharmony_ci				  "BEACON" : "PROBE RESPONSE");
154362306a36Sopenharmony_ci		return;
154462306a36Sopenharmony_ci	}
154562306a36Sopenharmony_ci
154662306a36Sopenharmony_ci	/* The network parsed correctly -- so now we scan our known networks
154762306a36Sopenharmony_ci	 * to see if we can find it in our list.
154862306a36Sopenharmony_ci	 *
154962306a36Sopenharmony_ci	 * NOTE:  This search is definitely not optimized.  Once its doing
155062306a36Sopenharmony_ci	 *        the "right thing" we'll optimize it for efficiency if
155162306a36Sopenharmony_ci	 *        necessary */
155262306a36Sopenharmony_ci
155362306a36Sopenharmony_ci	/* Search for this entry in the list and update it if it is
155462306a36Sopenharmony_ci	 * already there. */
155562306a36Sopenharmony_ci
155662306a36Sopenharmony_ci	spin_lock_irqsave(&ieee->lock, flags);
155762306a36Sopenharmony_ci
155862306a36Sopenharmony_ci	list_for_each_entry(target, &ieee->network_list, list) {
155962306a36Sopenharmony_ci		if (is_same_network(target, &network))
156062306a36Sopenharmony_ci			break;
156162306a36Sopenharmony_ci
156262306a36Sopenharmony_ci		if ((oldest == NULL) ||
156362306a36Sopenharmony_ci		    time_before(target->last_scanned, oldest->last_scanned))
156462306a36Sopenharmony_ci			oldest = target;
156562306a36Sopenharmony_ci	}
156662306a36Sopenharmony_ci
156762306a36Sopenharmony_ci	/* If we didn't find a match, then get a new network slot to initialize
156862306a36Sopenharmony_ci	 * with this beacon's information */
156962306a36Sopenharmony_ci	if (&target->list == &ieee->network_list) {
157062306a36Sopenharmony_ci		if (list_empty(&ieee->network_free_list)) {
157162306a36Sopenharmony_ci			/* If there are no more slots, expire the oldest */
157262306a36Sopenharmony_ci			list_del(&oldest->list);
157362306a36Sopenharmony_ci			target = oldest;
157462306a36Sopenharmony_ci			LIBIPW_DEBUG_SCAN("Expired '%*pE' (%pM) from network list.\n",
157562306a36Sopenharmony_ci					  target->ssid_len, target->ssid,
157662306a36Sopenharmony_ci					  target->bssid);
157762306a36Sopenharmony_ci		} else {
157862306a36Sopenharmony_ci			/* Otherwise just pull from the free list */
157962306a36Sopenharmony_ci			target = list_entry(ieee->network_free_list.next,
158062306a36Sopenharmony_ci					    struct libipw_network, list);
158162306a36Sopenharmony_ci			list_del(ieee->network_free_list.next);
158262306a36Sopenharmony_ci		}
158362306a36Sopenharmony_ci
158462306a36Sopenharmony_ci#ifdef CONFIG_LIBIPW_DEBUG
158562306a36Sopenharmony_ci		LIBIPW_DEBUG_SCAN("Adding '%*pE' (%pM) via %s.\n",
158662306a36Sopenharmony_ci				  network.ssid_len, network.ssid,
158762306a36Sopenharmony_ci				  network.bssid,
158862306a36Sopenharmony_ci				  is_beacon(beacon->header.frame_ctl) ?
158962306a36Sopenharmony_ci				  "BEACON" : "PROBE RESPONSE");
159062306a36Sopenharmony_ci#endif
159162306a36Sopenharmony_ci		memcpy(target, &network, sizeof(*target));
159262306a36Sopenharmony_ci		list_add_tail(&target->list, &ieee->network_list);
159362306a36Sopenharmony_ci	} else {
159462306a36Sopenharmony_ci		LIBIPW_DEBUG_SCAN("Updating '%*pE' (%pM) via %s.\n",
159562306a36Sopenharmony_ci				  target->ssid_len, target->ssid,
159662306a36Sopenharmony_ci				  target->bssid,
159762306a36Sopenharmony_ci				  is_beacon(beacon->header.frame_ctl) ?
159862306a36Sopenharmony_ci				  "BEACON" : "PROBE RESPONSE");
159962306a36Sopenharmony_ci		update_network(target, &network);
160062306a36Sopenharmony_ci	}
160162306a36Sopenharmony_ci
160262306a36Sopenharmony_ci	spin_unlock_irqrestore(&ieee->lock, flags);
160362306a36Sopenharmony_ci
160462306a36Sopenharmony_ci	if (is_beacon(beacon->header.frame_ctl)) {
160562306a36Sopenharmony_ci		if (ieee->handle_beacon != NULL)
160662306a36Sopenharmony_ci			ieee->handle_beacon(dev, beacon, target);
160762306a36Sopenharmony_ci	} else {
160862306a36Sopenharmony_ci		if (ieee->handle_probe_response != NULL)
160962306a36Sopenharmony_ci			ieee->handle_probe_response(dev, beacon, target);
161062306a36Sopenharmony_ci	}
161162306a36Sopenharmony_ci}
161262306a36Sopenharmony_ci
161362306a36Sopenharmony_civoid libipw_rx_mgt(struct libipw_device *ieee,
161462306a36Sopenharmony_ci		      struct libipw_hdr_4addr *header,
161562306a36Sopenharmony_ci		      struct libipw_rx_stats *stats)
161662306a36Sopenharmony_ci{
161762306a36Sopenharmony_ci	switch (WLAN_FC_GET_STYPE(le16_to_cpu(header->frame_ctl))) {
161862306a36Sopenharmony_ci	case IEEE80211_STYPE_ASSOC_RESP:
161962306a36Sopenharmony_ci		LIBIPW_DEBUG_MGMT("received ASSOCIATION RESPONSE (%d)\n",
162062306a36Sopenharmony_ci				     WLAN_FC_GET_STYPE(le16_to_cpu
162162306a36Sopenharmony_ci						       (header->frame_ctl)));
162262306a36Sopenharmony_ci		libipw_handle_assoc_resp(ieee,
162362306a36Sopenharmony_ci					    (struct libipw_assoc_response *)
162462306a36Sopenharmony_ci					    header, stats);
162562306a36Sopenharmony_ci		break;
162662306a36Sopenharmony_ci
162762306a36Sopenharmony_ci	case IEEE80211_STYPE_REASSOC_RESP:
162862306a36Sopenharmony_ci		LIBIPW_DEBUG_MGMT("received REASSOCIATION RESPONSE (%d)\n",
162962306a36Sopenharmony_ci				     WLAN_FC_GET_STYPE(le16_to_cpu
163062306a36Sopenharmony_ci						       (header->frame_ctl)));
163162306a36Sopenharmony_ci		break;
163262306a36Sopenharmony_ci
163362306a36Sopenharmony_ci	case IEEE80211_STYPE_PROBE_REQ:
163462306a36Sopenharmony_ci		LIBIPW_DEBUG_MGMT("received auth (%d)\n",
163562306a36Sopenharmony_ci				     WLAN_FC_GET_STYPE(le16_to_cpu
163662306a36Sopenharmony_ci						       (header->frame_ctl)));
163762306a36Sopenharmony_ci
163862306a36Sopenharmony_ci		if (ieee->handle_probe_request != NULL)
163962306a36Sopenharmony_ci			ieee->handle_probe_request(ieee->dev,
164062306a36Sopenharmony_ci						   (struct
164162306a36Sopenharmony_ci						    libipw_probe_request *)
164262306a36Sopenharmony_ci						   header, stats);
164362306a36Sopenharmony_ci		break;
164462306a36Sopenharmony_ci
164562306a36Sopenharmony_ci	case IEEE80211_STYPE_PROBE_RESP:
164662306a36Sopenharmony_ci		LIBIPW_DEBUG_MGMT("received PROBE RESPONSE (%d)\n",
164762306a36Sopenharmony_ci				     WLAN_FC_GET_STYPE(le16_to_cpu
164862306a36Sopenharmony_ci						       (header->frame_ctl)));
164962306a36Sopenharmony_ci		LIBIPW_DEBUG_SCAN("Probe response\n");
165062306a36Sopenharmony_ci		libipw_process_probe_response(ieee,
165162306a36Sopenharmony_ci						 (struct
165262306a36Sopenharmony_ci						  libipw_probe_response *)
165362306a36Sopenharmony_ci						 header, stats);
165462306a36Sopenharmony_ci		break;
165562306a36Sopenharmony_ci
165662306a36Sopenharmony_ci	case IEEE80211_STYPE_BEACON:
165762306a36Sopenharmony_ci		LIBIPW_DEBUG_MGMT("received BEACON (%d)\n",
165862306a36Sopenharmony_ci				     WLAN_FC_GET_STYPE(le16_to_cpu
165962306a36Sopenharmony_ci						       (header->frame_ctl)));
166062306a36Sopenharmony_ci		LIBIPW_DEBUG_SCAN("Beacon\n");
166162306a36Sopenharmony_ci		libipw_process_probe_response(ieee,
166262306a36Sopenharmony_ci						 (struct
166362306a36Sopenharmony_ci						  libipw_probe_response *)
166462306a36Sopenharmony_ci						 header, stats);
166562306a36Sopenharmony_ci		break;
166662306a36Sopenharmony_ci	case IEEE80211_STYPE_AUTH:
166762306a36Sopenharmony_ci
166862306a36Sopenharmony_ci		LIBIPW_DEBUG_MGMT("received auth (%d)\n",
166962306a36Sopenharmony_ci				     WLAN_FC_GET_STYPE(le16_to_cpu
167062306a36Sopenharmony_ci						       (header->frame_ctl)));
167162306a36Sopenharmony_ci
167262306a36Sopenharmony_ci		if (ieee->handle_auth != NULL)
167362306a36Sopenharmony_ci			ieee->handle_auth(ieee->dev,
167462306a36Sopenharmony_ci					  (struct libipw_auth *)header);
167562306a36Sopenharmony_ci		break;
167662306a36Sopenharmony_ci
167762306a36Sopenharmony_ci	case IEEE80211_STYPE_DISASSOC:
167862306a36Sopenharmony_ci		if (ieee->handle_disassoc != NULL)
167962306a36Sopenharmony_ci			ieee->handle_disassoc(ieee->dev,
168062306a36Sopenharmony_ci					      (struct libipw_disassoc *)
168162306a36Sopenharmony_ci					      header);
168262306a36Sopenharmony_ci		break;
168362306a36Sopenharmony_ci
168462306a36Sopenharmony_ci	case IEEE80211_STYPE_ACTION:
168562306a36Sopenharmony_ci		LIBIPW_DEBUG_MGMT("ACTION\n");
168662306a36Sopenharmony_ci		if (ieee->handle_action)
168762306a36Sopenharmony_ci			ieee->handle_action(ieee->dev,
168862306a36Sopenharmony_ci					    (struct libipw_action *)
168962306a36Sopenharmony_ci					    header, stats);
169062306a36Sopenharmony_ci		break;
169162306a36Sopenharmony_ci
169262306a36Sopenharmony_ci	case IEEE80211_STYPE_REASSOC_REQ:
169362306a36Sopenharmony_ci		LIBIPW_DEBUG_MGMT("received reassoc (%d)\n",
169462306a36Sopenharmony_ci				     WLAN_FC_GET_STYPE(le16_to_cpu
169562306a36Sopenharmony_ci						       (header->frame_ctl)));
169662306a36Sopenharmony_ci
169762306a36Sopenharmony_ci		LIBIPW_DEBUG_MGMT("%s: LIBIPW_REASSOC_REQ received\n",
169862306a36Sopenharmony_ci				     ieee->dev->name);
169962306a36Sopenharmony_ci		if (ieee->handle_reassoc_request != NULL)
170062306a36Sopenharmony_ci			ieee->handle_reassoc_request(ieee->dev,
170162306a36Sopenharmony_ci						    (struct libipw_reassoc_request *)
170262306a36Sopenharmony_ci						     header);
170362306a36Sopenharmony_ci		break;
170462306a36Sopenharmony_ci
170562306a36Sopenharmony_ci	case IEEE80211_STYPE_ASSOC_REQ:
170662306a36Sopenharmony_ci		LIBIPW_DEBUG_MGMT("received assoc (%d)\n",
170762306a36Sopenharmony_ci				     WLAN_FC_GET_STYPE(le16_to_cpu
170862306a36Sopenharmony_ci						       (header->frame_ctl)));
170962306a36Sopenharmony_ci
171062306a36Sopenharmony_ci		LIBIPW_DEBUG_MGMT("%s: LIBIPW_ASSOC_REQ received\n",
171162306a36Sopenharmony_ci				     ieee->dev->name);
171262306a36Sopenharmony_ci		if (ieee->handle_assoc_request != NULL)
171362306a36Sopenharmony_ci			ieee->handle_assoc_request(ieee->dev);
171462306a36Sopenharmony_ci		break;
171562306a36Sopenharmony_ci
171662306a36Sopenharmony_ci	case IEEE80211_STYPE_DEAUTH:
171762306a36Sopenharmony_ci		LIBIPW_DEBUG_MGMT("DEAUTH\n");
171862306a36Sopenharmony_ci		if (ieee->handle_deauth != NULL)
171962306a36Sopenharmony_ci			ieee->handle_deauth(ieee->dev,
172062306a36Sopenharmony_ci					    (struct libipw_deauth *)
172162306a36Sopenharmony_ci					    header);
172262306a36Sopenharmony_ci		break;
172362306a36Sopenharmony_ci	default:
172462306a36Sopenharmony_ci		LIBIPW_DEBUG_MGMT("received UNKNOWN (%d)\n",
172562306a36Sopenharmony_ci				     WLAN_FC_GET_STYPE(le16_to_cpu
172662306a36Sopenharmony_ci						       (header->frame_ctl)));
172762306a36Sopenharmony_ci		LIBIPW_DEBUG_MGMT("%s: Unknown management packet: %d\n",
172862306a36Sopenharmony_ci				     ieee->dev->name,
172962306a36Sopenharmony_ci				     WLAN_FC_GET_STYPE(le16_to_cpu
173062306a36Sopenharmony_ci						       (header->frame_ctl)));
173162306a36Sopenharmony_ci		break;
173262306a36Sopenharmony_ci	}
173362306a36Sopenharmony_ci}
173462306a36Sopenharmony_ci
173562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(libipw_rx_any);
173662306a36Sopenharmony_ciEXPORT_SYMBOL(libipw_rx_mgt);
173762306a36Sopenharmony_ciEXPORT_SYMBOL(libipw_rx);
1738