162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Intersil Prism2 driver with Host AP (software access point) support
462306a36Sopenharmony_ci * Copyright (c) 2001-2002, SSH Communications Security Corp and Jouni Malinen
562306a36Sopenharmony_ci * <j@w1.fi>
662306a36Sopenharmony_ci * Copyright (c) 2002-2005, Jouni Malinen <j@w1.fi>
762306a36Sopenharmony_ci *
862306a36Sopenharmony_ci * This file is to be included into hostap.c when S/W AP functionality is
962306a36Sopenharmony_ci * compiled.
1062306a36Sopenharmony_ci *
1162306a36Sopenharmony_ci * AP:  FIX:
1262306a36Sopenharmony_ci * - if unicast Class 2 (assoc,reassoc,disassoc) frame received from
1362306a36Sopenharmony_ci *   unauthenticated STA, send deauth. frame (8802.11: 5.5)
1462306a36Sopenharmony_ci * - if unicast Class 3 (data with to/from DS,deauth,pspoll) frame received
1562306a36Sopenharmony_ci *   from authenticated, but unassoc STA, send disassoc frame (8802.11: 5.5)
1662306a36Sopenharmony_ci * - if unicast Class 3 received from unauthenticated STA, send deauth. frame
1762306a36Sopenharmony_ci *   (8802.11: 5.5)
1862306a36Sopenharmony_ci */
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_ci#include <linux/proc_fs.h>
2162306a36Sopenharmony_ci#include <linux/seq_file.h>
2262306a36Sopenharmony_ci#include <linux/delay.h>
2362306a36Sopenharmony_ci#include <linux/random.h>
2462306a36Sopenharmony_ci#include <linux/if_arp.h>
2562306a36Sopenharmony_ci#include <linux/slab.h>
2662306a36Sopenharmony_ci#include <linux/export.h>
2762306a36Sopenharmony_ci#include <linux/moduleparam.h>
2862306a36Sopenharmony_ci#include <linux/etherdevice.h>
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_ci#include "hostap_wlan.h"
3162306a36Sopenharmony_ci#include "hostap.h"
3262306a36Sopenharmony_ci#include "hostap_ap.h"
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_cistatic int other_ap_policy[MAX_PARM_DEVICES] = { AP_OTHER_AP_SKIP_ALL,
3562306a36Sopenharmony_ci						 DEF_INTS };
3662306a36Sopenharmony_cimodule_param_array(other_ap_policy, int, NULL, 0444);
3762306a36Sopenharmony_ciMODULE_PARM_DESC(other_ap_policy, "Other AP beacon monitoring policy (0-3)");
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_cistatic int ap_max_inactivity[MAX_PARM_DEVICES] = { AP_MAX_INACTIVITY_SEC,
4062306a36Sopenharmony_ci						   DEF_INTS };
4162306a36Sopenharmony_cimodule_param_array(ap_max_inactivity, int, NULL, 0444);
4262306a36Sopenharmony_ciMODULE_PARM_DESC(ap_max_inactivity, "AP timeout (in seconds) for station "
4362306a36Sopenharmony_ci		 "inactivity");
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_cistatic int ap_bridge_packets[MAX_PARM_DEVICES] = { 1, DEF_INTS };
4662306a36Sopenharmony_cimodule_param_array(ap_bridge_packets, int, NULL, 0444);
4762306a36Sopenharmony_ciMODULE_PARM_DESC(ap_bridge_packets, "Bridge packets directly between "
4862306a36Sopenharmony_ci		 "stations");
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_cistatic int autom_ap_wds[MAX_PARM_DEVICES] = { 0, DEF_INTS };
5162306a36Sopenharmony_cimodule_param_array(autom_ap_wds, int, NULL, 0444);
5262306a36Sopenharmony_ciMODULE_PARM_DESC(autom_ap_wds, "Add WDS connections to other APs "
5362306a36Sopenharmony_ci		 "automatically");
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_cistatic struct sta_info* ap_get_sta(struct ap_data *ap, u8 *sta);
5762306a36Sopenharmony_cistatic void hostap_event_expired_sta(struct net_device *dev,
5862306a36Sopenharmony_ci				     struct sta_info *sta);
5962306a36Sopenharmony_cistatic void handle_add_proc_queue(struct work_struct *work);
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_ci#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT
6262306a36Sopenharmony_cistatic void handle_wds_oper_queue(struct work_struct *work);
6362306a36Sopenharmony_cistatic void prism2_send_mgmt(struct net_device *dev,
6462306a36Sopenharmony_ci			     u16 type_subtype, char *body,
6562306a36Sopenharmony_ci			     int body_len, u8 *addr, u16 tx_cb_idx);
6662306a36Sopenharmony_ci#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_ci#if !defined(PRISM2_NO_PROCFS_DEBUG) && defined(CONFIG_PROC_FS)
7062306a36Sopenharmony_cistatic int ap_debug_proc_show(struct seq_file *m, void *v)
7162306a36Sopenharmony_ci{
7262306a36Sopenharmony_ci	struct ap_data *ap = pde_data(file_inode(m->file));
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_ci	seq_printf(m, "BridgedUnicastFrames=%u\n", ap->bridged_unicast);
7562306a36Sopenharmony_ci	seq_printf(m, "BridgedMulticastFrames=%u\n", ap->bridged_multicast);
7662306a36Sopenharmony_ci	seq_printf(m, "max_inactivity=%u\n", ap->max_inactivity / HZ);
7762306a36Sopenharmony_ci	seq_printf(m, "bridge_packets=%u\n", ap->bridge_packets);
7862306a36Sopenharmony_ci	seq_printf(m, "nullfunc_ack=%u\n", ap->nullfunc_ack);
7962306a36Sopenharmony_ci	seq_printf(m, "autom_ap_wds=%u\n", ap->autom_ap_wds);
8062306a36Sopenharmony_ci	seq_printf(m, "auth_algs=%u\n", ap->local->auth_algs);
8162306a36Sopenharmony_ci	seq_printf(m, "tx_drop_nonassoc=%u\n", ap->tx_drop_nonassoc);
8262306a36Sopenharmony_ci	return 0;
8362306a36Sopenharmony_ci}
8462306a36Sopenharmony_ci#endif
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_cistatic void ap_sta_hash_add(struct ap_data *ap, struct sta_info *sta)
8762306a36Sopenharmony_ci{
8862306a36Sopenharmony_ci	sta->hnext = ap->sta_hash[STA_HASH(sta->addr)];
8962306a36Sopenharmony_ci	ap->sta_hash[STA_HASH(sta->addr)] = sta;
9062306a36Sopenharmony_ci}
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_cistatic void ap_sta_hash_del(struct ap_data *ap, struct sta_info *sta)
9362306a36Sopenharmony_ci{
9462306a36Sopenharmony_ci	struct sta_info *s;
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_ci	s = ap->sta_hash[STA_HASH(sta->addr)];
9762306a36Sopenharmony_ci	if (s == NULL) return;
9862306a36Sopenharmony_ci	if (ether_addr_equal(s->addr, sta->addr)) {
9962306a36Sopenharmony_ci		ap->sta_hash[STA_HASH(sta->addr)] = s->hnext;
10062306a36Sopenharmony_ci		return;
10162306a36Sopenharmony_ci	}
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_ci	while (s->hnext != NULL && !ether_addr_equal(s->hnext->addr, sta->addr))
10462306a36Sopenharmony_ci		s = s->hnext;
10562306a36Sopenharmony_ci	if (s->hnext != NULL)
10662306a36Sopenharmony_ci		s->hnext = s->hnext->hnext;
10762306a36Sopenharmony_ci	else
10862306a36Sopenharmony_ci		printk("AP: could not remove STA %pM from hash table\n",
10962306a36Sopenharmony_ci		       sta->addr);
11062306a36Sopenharmony_ci}
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_cistatic void ap_free_sta(struct ap_data *ap, struct sta_info *sta)
11362306a36Sopenharmony_ci{
11462306a36Sopenharmony_ci	if (sta->ap && sta->local)
11562306a36Sopenharmony_ci		hostap_event_expired_sta(sta->local->dev, sta);
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_ci	if (ap->proc != NULL) {
11862306a36Sopenharmony_ci		char name[20];
11962306a36Sopenharmony_ci		sprintf(name, "%pM", sta->addr);
12062306a36Sopenharmony_ci		remove_proc_entry(name, ap->proc);
12162306a36Sopenharmony_ci	}
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_ci	if (sta->crypt) {
12462306a36Sopenharmony_ci		sta->crypt->ops->deinit(sta->crypt->priv);
12562306a36Sopenharmony_ci		kfree(sta->crypt);
12662306a36Sopenharmony_ci		sta->crypt = NULL;
12762306a36Sopenharmony_ci	}
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_ci	skb_queue_purge(&sta->tx_buf);
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_ci	ap->num_sta--;
13262306a36Sopenharmony_ci#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT
13362306a36Sopenharmony_ci	if (sta->aid > 0)
13462306a36Sopenharmony_ci		ap->sta_aid[sta->aid - 1] = NULL;
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_ci	if (!sta->ap)
13762306a36Sopenharmony_ci		kfree(sta->u.sta.challenge);
13862306a36Sopenharmony_ci	timer_shutdown_sync(&sta->timer);
13962306a36Sopenharmony_ci#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */
14062306a36Sopenharmony_ci
14162306a36Sopenharmony_ci	kfree(sta);
14262306a36Sopenharmony_ci}
14362306a36Sopenharmony_ci
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_cistatic void hostap_set_tim(local_info_t *local, int aid, int set)
14662306a36Sopenharmony_ci{
14762306a36Sopenharmony_ci	if (local->func->set_tim)
14862306a36Sopenharmony_ci		local->func->set_tim(local->dev, aid, set);
14962306a36Sopenharmony_ci}
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_ci
15262306a36Sopenharmony_cistatic void hostap_event_new_sta(struct net_device *dev, struct sta_info *sta)
15362306a36Sopenharmony_ci{
15462306a36Sopenharmony_ci	union iwreq_data wrqu;
15562306a36Sopenharmony_ci	memset(&wrqu, 0, sizeof(wrqu));
15662306a36Sopenharmony_ci	memcpy(wrqu.addr.sa_data, sta->addr, ETH_ALEN);
15762306a36Sopenharmony_ci	wrqu.addr.sa_family = ARPHRD_ETHER;
15862306a36Sopenharmony_ci	wireless_send_event(dev, IWEVREGISTERED, &wrqu, NULL);
15962306a36Sopenharmony_ci}
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_ci
16262306a36Sopenharmony_cistatic void hostap_event_expired_sta(struct net_device *dev,
16362306a36Sopenharmony_ci				     struct sta_info *sta)
16462306a36Sopenharmony_ci{
16562306a36Sopenharmony_ci	union iwreq_data wrqu;
16662306a36Sopenharmony_ci	memset(&wrqu, 0, sizeof(wrqu));
16762306a36Sopenharmony_ci	memcpy(wrqu.addr.sa_data, sta->addr, ETH_ALEN);
16862306a36Sopenharmony_ci	wrqu.addr.sa_family = ARPHRD_ETHER;
16962306a36Sopenharmony_ci	wireless_send_event(dev, IWEVEXPIRED, &wrqu, NULL);
17062306a36Sopenharmony_ci}
17162306a36Sopenharmony_ci
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_ci#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_cistatic void ap_handle_timer(struct timer_list *t)
17662306a36Sopenharmony_ci{
17762306a36Sopenharmony_ci	struct sta_info *sta = from_timer(sta, t, timer);
17862306a36Sopenharmony_ci	local_info_t *local;
17962306a36Sopenharmony_ci	struct ap_data *ap;
18062306a36Sopenharmony_ci	unsigned long next_time = 0;
18162306a36Sopenharmony_ci	int was_assoc;
18262306a36Sopenharmony_ci
18362306a36Sopenharmony_ci	if (sta == NULL || sta->local == NULL || sta->local->ap == NULL) {
18462306a36Sopenharmony_ci		PDEBUG(DEBUG_AP, "ap_handle_timer() called with NULL data\n");
18562306a36Sopenharmony_ci		return;
18662306a36Sopenharmony_ci	}
18762306a36Sopenharmony_ci
18862306a36Sopenharmony_ci	local = sta->local;
18962306a36Sopenharmony_ci	ap = local->ap;
19062306a36Sopenharmony_ci	was_assoc = sta->flags & WLAN_STA_ASSOC;
19162306a36Sopenharmony_ci
19262306a36Sopenharmony_ci	if (atomic_read(&sta->users) != 0)
19362306a36Sopenharmony_ci		next_time = jiffies + HZ;
19462306a36Sopenharmony_ci	else if ((sta->flags & WLAN_STA_PERM) && !(sta->flags & WLAN_STA_AUTH))
19562306a36Sopenharmony_ci		next_time = jiffies + ap->max_inactivity;
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_ci	if (time_before(jiffies, sta->last_rx + ap->max_inactivity)) {
19862306a36Sopenharmony_ci		/* station activity detected; reset timeout state */
19962306a36Sopenharmony_ci		sta->timeout_next = STA_NULLFUNC;
20062306a36Sopenharmony_ci		next_time = sta->last_rx + ap->max_inactivity;
20162306a36Sopenharmony_ci	} else if (sta->timeout_next == STA_DISASSOC &&
20262306a36Sopenharmony_ci		   !(sta->flags & WLAN_STA_PENDING_POLL)) {
20362306a36Sopenharmony_ci		/* STA ACKed data nullfunc frame poll */
20462306a36Sopenharmony_ci		sta->timeout_next = STA_NULLFUNC;
20562306a36Sopenharmony_ci		next_time = jiffies + ap->max_inactivity;
20662306a36Sopenharmony_ci	}
20762306a36Sopenharmony_ci
20862306a36Sopenharmony_ci	if (next_time) {
20962306a36Sopenharmony_ci		sta->timer.expires = next_time;
21062306a36Sopenharmony_ci		add_timer(&sta->timer);
21162306a36Sopenharmony_ci		return;
21262306a36Sopenharmony_ci	}
21362306a36Sopenharmony_ci
21462306a36Sopenharmony_ci	if (sta->ap)
21562306a36Sopenharmony_ci		sta->timeout_next = STA_DEAUTH;
21662306a36Sopenharmony_ci
21762306a36Sopenharmony_ci	if (sta->timeout_next == STA_DEAUTH && !(sta->flags & WLAN_STA_PERM)) {
21862306a36Sopenharmony_ci		spin_lock(&ap->sta_table_lock);
21962306a36Sopenharmony_ci		ap_sta_hash_del(ap, sta);
22062306a36Sopenharmony_ci		list_del(&sta->list);
22162306a36Sopenharmony_ci		spin_unlock(&ap->sta_table_lock);
22262306a36Sopenharmony_ci		sta->flags &= ~(WLAN_STA_AUTH | WLAN_STA_ASSOC);
22362306a36Sopenharmony_ci	} else if (sta->timeout_next == STA_DISASSOC)
22462306a36Sopenharmony_ci		sta->flags &= ~WLAN_STA_ASSOC;
22562306a36Sopenharmony_ci
22662306a36Sopenharmony_ci	if (was_assoc && !(sta->flags & WLAN_STA_ASSOC) && !sta->ap)
22762306a36Sopenharmony_ci		hostap_event_expired_sta(local->dev, sta);
22862306a36Sopenharmony_ci
22962306a36Sopenharmony_ci	if (sta->timeout_next == STA_DEAUTH && sta->aid > 0 &&
23062306a36Sopenharmony_ci	    !skb_queue_empty(&sta->tx_buf)) {
23162306a36Sopenharmony_ci		hostap_set_tim(local, sta->aid, 0);
23262306a36Sopenharmony_ci		sta->flags &= ~WLAN_STA_TIM;
23362306a36Sopenharmony_ci	}
23462306a36Sopenharmony_ci
23562306a36Sopenharmony_ci	if (sta->ap) {
23662306a36Sopenharmony_ci		if (ap->autom_ap_wds) {
23762306a36Sopenharmony_ci			PDEBUG(DEBUG_AP, "%s: removing automatic WDS "
23862306a36Sopenharmony_ci			       "connection to AP %pM\n",
23962306a36Sopenharmony_ci			       local->dev->name, sta->addr);
24062306a36Sopenharmony_ci			hostap_wds_link_oper(local, sta->addr, WDS_DEL);
24162306a36Sopenharmony_ci		}
24262306a36Sopenharmony_ci	} else if (sta->timeout_next == STA_NULLFUNC) {
24362306a36Sopenharmony_ci		/* send data frame to poll STA and check whether this frame
24462306a36Sopenharmony_ci		 * is ACKed */
24562306a36Sopenharmony_ci		/* FIX: IEEE80211_STYPE_NULLFUNC would be more appropriate, but
24662306a36Sopenharmony_ci		 * it is apparently not retried so TX Exc events are not
24762306a36Sopenharmony_ci		 * received for it */
24862306a36Sopenharmony_ci		sta->flags |= WLAN_STA_PENDING_POLL;
24962306a36Sopenharmony_ci		prism2_send_mgmt(local->dev, IEEE80211_FTYPE_DATA |
25062306a36Sopenharmony_ci				 IEEE80211_STYPE_DATA, NULL, 0,
25162306a36Sopenharmony_ci				 sta->addr, ap->tx_callback_poll);
25262306a36Sopenharmony_ci	} else {
25362306a36Sopenharmony_ci		int deauth = sta->timeout_next == STA_DEAUTH;
25462306a36Sopenharmony_ci		__le16 resp;
25562306a36Sopenharmony_ci		PDEBUG(DEBUG_AP, "%s: sending %s info to STA %pM"
25662306a36Sopenharmony_ci		       "(last=%lu, jiffies=%lu)\n",
25762306a36Sopenharmony_ci		       local->dev->name,
25862306a36Sopenharmony_ci		       deauth ? "deauthentication" : "disassociation",
25962306a36Sopenharmony_ci		       sta->addr, sta->last_rx, jiffies);
26062306a36Sopenharmony_ci
26162306a36Sopenharmony_ci		resp = cpu_to_le16(deauth ? WLAN_REASON_PREV_AUTH_NOT_VALID :
26262306a36Sopenharmony_ci				   WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY);
26362306a36Sopenharmony_ci		prism2_send_mgmt(local->dev, IEEE80211_FTYPE_MGMT |
26462306a36Sopenharmony_ci				 (deauth ? IEEE80211_STYPE_DEAUTH :
26562306a36Sopenharmony_ci				  IEEE80211_STYPE_DISASSOC),
26662306a36Sopenharmony_ci				 (char *) &resp, 2, sta->addr, 0);
26762306a36Sopenharmony_ci	}
26862306a36Sopenharmony_ci
26962306a36Sopenharmony_ci	if (sta->timeout_next == STA_DEAUTH) {
27062306a36Sopenharmony_ci		if (sta->flags & WLAN_STA_PERM) {
27162306a36Sopenharmony_ci			PDEBUG(DEBUG_AP, "%s: STA %pM"
27262306a36Sopenharmony_ci			       " would have been removed, "
27362306a36Sopenharmony_ci			       "but it has 'perm' flag\n",
27462306a36Sopenharmony_ci			       local->dev->name, sta->addr);
27562306a36Sopenharmony_ci		} else
27662306a36Sopenharmony_ci			ap_free_sta(ap, sta);
27762306a36Sopenharmony_ci		return;
27862306a36Sopenharmony_ci	}
27962306a36Sopenharmony_ci
28062306a36Sopenharmony_ci	if (sta->timeout_next == STA_NULLFUNC) {
28162306a36Sopenharmony_ci		sta->timeout_next = STA_DISASSOC;
28262306a36Sopenharmony_ci		sta->timer.expires = jiffies + AP_DISASSOC_DELAY;
28362306a36Sopenharmony_ci	} else {
28462306a36Sopenharmony_ci		sta->timeout_next = STA_DEAUTH;
28562306a36Sopenharmony_ci		sta->timer.expires = jiffies + AP_DEAUTH_DELAY;
28662306a36Sopenharmony_ci	}
28762306a36Sopenharmony_ci
28862306a36Sopenharmony_ci	add_timer(&sta->timer);
28962306a36Sopenharmony_ci}
29062306a36Sopenharmony_ci
29162306a36Sopenharmony_ci
29262306a36Sopenharmony_civoid hostap_deauth_all_stas(struct net_device *dev, struct ap_data *ap,
29362306a36Sopenharmony_ci			    int resend)
29462306a36Sopenharmony_ci{
29562306a36Sopenharmony_ci	u8 addr[ETH_ALEN];
29662306a36Sopenharmony_ci	__le16 resp;
29762306a36Sopenharmony_ci	int i;
29862306a36Sopenharmony_ci
29962306a36Sopenharmony_ci	PDEBUG(DEBUG_AP, "%s: Deauthenticate all stations\n", dev->name);
30062306a36Sopenharmony_ci	eth_broadcast_addr(addr);
30162306a36Sopenharmony_ci
30262306a36Sopenharmony_ci	resp = cpu_to_le16(WLAN_REASON_PREV_AUTH_NOT_VALID);
30362306a36Sopenharmony_ci
30462306a36Sopenharmony_ci	/* deauth message sent; try to resend it few times; the message is
30562306a36Sopenharmony_ci	 * broadcast, so it may be delayed until next DTIM; there is not much
30662306a36Sopenharmony_ci	 * else we can do at this point since the driver is going to be shut
30762306a36Sopenharmony_ci	 * down */
30862306a36Sopenharmony_ci	for (i = 0; i < 5; i++) {
30962306a36Sopenharmony_ci		prism2_send_mgmt(dev, IEEE80211_FTYPE_MGMT |
31062306a36Sopenharmony_ci				 IEEE80211_STYPE_DEAUTH,
31162306a36Sopenharmony_ci				 (char *) &resp, 2, addr, 0);
31262306a36Sopenharmony_ci
31362306a36Sopenharmony_ci		if (!resend || ap->num_sta <= 0)
31462306a36Sopenharmony_ci			return;
31562306a36Sopenharmony_ci
31662306a36Sopenharmony_ci		mdelay(50);
31762306a36Sopenharmony_ci	}
31862306a36Sopenharmony_ci}
31962306a36Sopenharmony_ci
32062306a36Sopenharmony_ci
32162306a36Sopenharmony_cistatic int ap_control_proc_show(struct seq_file *m, void *v)
32262306a36Sopenharmony_ci{
32362306a36Sopenharmony_ci	struct ap_data *ap = pde_data(file_inode(m->file));
32462306a36Sopenharmony_ci	char *policy_txt;
32562306a36Sopenharmony_ci	struct mac_entry *entry;
32662306a36Sopenharmony_ci
32762306a36Sopenharmony_ci	if (v == SEQ_START_TOKEN) {
32862306a36Sopenharmony_ci		switch (ap->mac_restrictions.policy) {
32962306a36Sopenharmony_ci		case MAC_POLICY_OPEN:
33062306a36Sopenharmony_ci			policy_txt = "open";
33162306a36Sopenharmony_ci			break;
33262306a36Sopenharmony_ci		case MAC_POLICY_ALLOW:
33362306a36Sopenharmony_ci			policy_txt = "allow";
33462306a36Sopenharmony_ci			break;
33562306a36Sopenharmony_ci		case MAC_POLICY_DENY:
33662306a36Sopenharmony_ci			policy_txt = "deny";
33762306a36Sopenharmony_ci			break;
33862306a36Sopenharmony_ci		default:
33962306a36Sopenharmony_ci			policy_txt = "unknown";
34062306a36Sopenharmony_ci			break;
34162306a36Sopenharmony_ci		}
34262306a36Sopenharmony_ci		seq_printf(m, "MAC policy: %s\n", policy_txt);
34362306a36Sopenharmony_ci		seq_printf(m, "MAC entries: %u\n", ap->mac_restrictions.entries);
34462306a36Sopenharmony_ci		seq_puts(m, "MAC list:\n");
34562306a36Sopenharmony_ci		return 0;
34662306a36Sopenharmony_ci	}
34762306a36Sopenharmony_ci
34862306a36Sopenharmony_ci	entry = v;
34962306a36Sopenharmony_ci	seq_printf(m, "%pM\n", entry->addr);
35062306a36Sopenharmony_ci	return 0;
35162306a36Sopenharmony_ci}
35262306a36Sopenharmony_ci
35362306a36Sopenharmony_cistatic void *ap_control_proc_start(struct seq_file *m, loff_t *_pos)
35462306a36Sopenharmony_ci{
35562306a36Sopenharmony_ci	struct ap_data *ap = pde_data(file_inode(m->file));
35662306a36Sopenharmony_ci	spin_lock_bh(&ap->mac_restrictions.lock);
35762306a36Sopenharmony_ci	return seq_list_start_head(&ap->mac_restrictions.mac_list, *_pos);
35862306a36Sopenharmony_ci}
35962306a36Sopenharmony_ci
36062306a36Sopenharmony_cistatic void *ap_control_proc_next(struct seq_file *m, void *v, loff_t *_pos)
36162306a36Sopenharmony_ci{
36262306a36Sopenharmony_ci	struct ap_data *ap = pde_data(file_inode(m->file));
36362306a36Sopenharmony_ci	return seq_list_next(v, &ap->mac_restrictions.mac_list, _pos);
36462306a36Sopenharmony_ci}
36562306a36Sopenharmony_ci
36662306a36Sopenharmony_cistatic void ap_control_proc_stop(struct seq_file *m, void *v)
36762306a36Sopenharmony_ci{
36862306a36Sopenharmony_ci	struct ap_data *ap = pde_data(file_inode(m->file));
36962306a36Sopenharmony_ci	spin_unlock_bh(&ap->mac_restrictions.lock);
37062306a36Sopenharmony_ci}
37162306a36Sopenharmony_ci
37262306a36Sopenharmony_cistatic const struct seq_operations ap_control_proc_seqops = {
37362306a36Sopenharmony_ci	.start	= ap_control_proc_start,
37462306a36Sopenharmony_ci	.next	= ap_control_proc_next,
37562306a36Sopenharmony_ci	.stop	= ap_control_proc_stop,
37662306a36Sopenharmony_ci	.show	= ap_control_proc_show,
37762306a36Sopenharmony_ci};
37862306a36Sopenharmony_ci
37962306a36Sopenharmony_ciint ap_control_add_mac(struct mac_restrictions *mac_restrictions, u8 *mac)
38062306a36Sopenharmony_ci{
38162306a36Sopenharmony_ci	struct mac_entry *entry;
38262306a36Sopenharmony_ci
38362306a36Sopenharmony_ci	entry = kmalloc(sizeof(struct mac_entry), GFP_KERNEL);
38462306a36Sopenharmony_ci	if (entry == NULL)
38562306a36Sopenharmony_ci		return -ENOMEM;
38662306a36Sopenharmony_ci
38762306a36Sopenharmony_ci	memcpy(entry->addr, mac, ETH_ALEN);
38862306a36Sopenharmony_ci
38962306a36Sopenharmony_ci	spin_lock_bh(&mac_restrictions->lock);
39062306a36Sopenharmony_ci	list_add_tail(&entry->list, &mac_restrictions->mac_list);
39162306a36Sopenharmony_ci	mac_restrictions->entries++;
39262306a36Sopenharmony_ci	spin_unlock_bh(&mac_restrictions->lock);
39362306a36Sopenharmony_ci
39462306a36Sopenharmony_ci	return 0;
39562306a36Sopenharmony_ci}
39662306a36Sopenharmony_ci
39762306a36Sopenharmony_ci
39862306a36Sopenharmony_ciint ap_control_del_mac(struct mac_restrictions *mac_restrictions, u8 *mac)
39962306a36Sopenharmony_ci{
40062306a36Sopenharmony_ci	struct list_head *ptr;
40162306a36Sopenharmony_ci	struct mac_entry *entry;
40262306a36Sopenharmony_ci
40362306a36Sopenharmony_ci	spin_lock_bh(&mac_restrictions->lock);
40462306a36Sopenharmony_ci	for (ptr = mac_restrictions->mac_list.next;
40562306a36Sopenharmony_ci	     ptr != &mac_restrictions->mac_list; ptr = ptr->next) {
40662306a36Sopenharmony_ci		entry = list_entry(ptr, struct mac_entry, list);
40762306a36Sopenharmony_ci
40862306a36Sopenharmony_ci		if (ether_addr_equal(entry->addr, mac)) {
40962306a36Sopenharmony_ci			list_del(ptr);
41062306a36Sopenharmony_ci			kfree(entry);
41162306a36Sopenharmony_ci			mac_restrictions->entries--;
41262306a36Sopenharmony_ci			spin_unlock_bh(&mac_restrictions->lock);
41362306a36Sopenharmony_ci			return 0;
41462306a36Sopenharmony_ci		}
41562306a36Sopenharmony_ci	}
41662306a36Sopenharmony_ci	spin_unlock_bh(&mac_restrictions->lock);
41762306a36Sopenharmony_ci	return -1;
41862306a36Sopenharmony_ci}
41962306a36Sopenharmony_ci
42062306a36Sopenharmony_ci
42162306a36Sopenharmony_cistatic int ap_control_mac_deny(struct mac_restrictions *mac_restrictions,
42262306a36Sopenharmony_ci			       u8 *mac)
42362306a36Sopenharmony_ci{
42462306a36Sopenharmony_ci	struct mac_entry *entry;
42562306a36Sopenharmony_ci	int found = 0;
42662306a36Sopenharmony_ci
42762306a36Sopenharmony_ci	if (mac_restrictions->policy == MAC_POLICY_OPEN)
42862306a36Sopenharmony_ci		return 0;
42962306a36Sopenharmony_ci
43062306a36Sopenharmony_ci	spin_lock_bh(&mac_restrictions->lock);
43162306a36Sopenharmony_ci	list_for_each_entry(entry, &mac_restrictions->mac_list, list) {
43262306a36Sopenharmony_ci		if (ether_addr_equal(entry->addr, mac)) {
43362306a36Sopenharmony_ci			found = 1;
43462306a36Sopenharmony_ci			break;
43562306a36Sopenharmony_ci		}
43662306a36Sopenharmony_ci	}
43762306a36Sopenharmony_ci	spin_unlock_bh(&mac_restrictions->lock);
43862306a36Sopenharmony_ci
43962306a36Sopenharmony_ci	if (mac_restrictions->policy == MAC_POLICY_ALLOW)
44062306a36Sopenharmony_ci		return !found;
44162306a36Sopenharmony_ci	else
44262306a36Sopenharmony_ci		return found;
44362306a36Sopenharmony_ci}
44462306a36Sopenharmony_ci
44562306a36Sopenharmony_ci
44662306a36Sopenharmony_civoid ap_control_flush_macs(struct mac_restrictions *mac_restrictions)
44762306a36Sopenharmony_ci{
44862306a36Sopenharmony_ci	struct list_head *ptr, *n;
44962306a36Sopenharmony_ci	struct mac_entry *entry;
45062306a36Sopenharmony_ci
45162306a36Sopenharmony_ci	if (mac_restrictions->entries == 0)
45262306a36Sopenharmony_ci		return;
45362306a36Sopenharmony_ci
45462306a36Sopenharmony_ci	spin_lock_bh(&mac_restrictions->lock);
45562306a36Sopenharmony_ci	for (ptr = mac_restrictions->mac_list.next, n = ptr->next;
45662306a36Sopenharmony_ci	     ptr != &mac_restrictions->mac_list;
45762306a36Sopenharmony_ci	     ptr = n, n = ptr->next) {
45862306a36Sopenharmony_ci		entry = list_entry(ptr, struct mac_entry, list);
45962306a36Sopenharmony_ci		list_del(ptr);
46062306a36Sopenharmony_ci		kfree(entry);
46162306a36Sopenharmony_ci	}
46262306a36Sopenharmony_ci	mac_restrictions->entries = 0;
46362306a36Sopenharmony_ci	spin_unlock_bh(&mac_restrictions->lock);
46462306a36Sopenharmony_ci}
46562306a36Sopenharmony_ci
46662306a36Sopenharmony_ci
46762306a36Sopenharmony_ciint ap_control_kick_mac(struct ap_data *ap, struct net_device *dev, u8 *mac)
46862306a36Sopenharmony_ci{
46962306a36Sopenharmony_ci	struct sta_info *sta;
47062306a36Sopenharmony_ci	__le16 resp;
47162306a36Sopenharmony_ci
47262306a36Sopenharmony_ci	spin_lock_bh(&ap->sta_table_lock);
47362306a36Sopenharmony_ci	sta = ap_get_sta(ap, mac);
47462306a36Sopenharmony_ci	if (sta) {
47562306a36Sopenharmony_ci		ap_sta_hash_del(ap, sta);
47662306a36Sopenharmony_ci		list_del(&sta->list);
47762306a36Sopenharmony_ci	}
47862306a36Sopenharmony_ci	spin_unlock_bh(&ap->sta_table_lock);
47962306a36Sopenharmony_ci
48062306a36Sopenharmony_ci	if (!sta)
48162306a36Sopenharmony_ci		return -EINVAL;
48262306a36Sopenharmony_ci
48362306a36Sopenharmony_ci	resp = cpu_to_le16(WLAN_REASON_PREV_AUTH_NOT_VALID);
48462306a36Sopenharmony_ci	prism2_send_mgmt(dev, IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_DEAUTH,
48562306a36Sopenharmony_ci			 (char *) &resp, 2, sta->addr, 0);
48662306a36Sopenharmony_ci
48762306a36Sopenharmony_ci	if ((sta->flags & WLAN_STA_ASSOC) && !sta->ap)
48862306a36Sopenharmony_ci		hostap_event_expired_sta(dev, sta);
48962306a36Sopenharmony_ci
49062306a36Sopenharmony_ci	ap_free_sta(ap, sta);
49162306a36Sopenharmony_ci
49262306a36Sopenharmony_ci	return 0;
49362306a36Sopenharmony_ci}
49462306a36Sopenharmony_ci
49562306a36Sopenharmony_ci#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */
49662306a36Sopenharmony_ci
49762306a36Sopenharmony_ci
49862306a36Sopenharmony_civoid ap_control_kickall(struct ap_data *ap)
49962306a36Sopenharmony_ci{
50062306a36Sopenharmony_ci	struct list_head *ptr, *n;
50162306a36Sopenharmony_ci	struct sta_info *sta;
50262306a36Sopenharmony_ci
50362306a36Sopenharmony_ci	spin_lock_bh(&ap->sta_table_lock);
50462306a36Sopenharmony_ci	for (ptr = ap->sta_list.next, n = ptr->next; ptr != &ap->sta_list;
50562306a36Sopenharmony_ci	     ptr = n, n = ptr->next) {
50662306a36Sopenharmony_ci		sta = list_entry(ptr, struct sta_info, list);
50762306a36Sopenharmony_ci		ap_sta_hash_del(ap, sta);
50862306a36Sopenharmony_ci		list_del(&sta->list);
50962306a36Sopenharmony_ci		if ((sta->flags & WLAN_STA_ASSOC) && !sta->ap && sta->local)
51062306a36Sopenharmony_ci			hostap_event_expired_sta(sta->local->dev, sta);
51162306a36Sopenharmony_ci		ap_free_sta(ap, sta);
51262306a36Sopenharmony_ci	}
51362306a36Sopenharmony_ci	spin_unlock_bh(&ap->sta_table_lock);
51462306a36Sopenharmony_ci}
51562306a36Sopenharmony_ci
51662306a36Sopenharmony_ci
51762306a36Sopenharmony_ci#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT
51862306a36Sopenharmony_ci
51962306a36Sopenharmony_cistatic int prism2_ap_proc_show(struct seq_file *m, void *v)
52062306a36Sopenharmony_ci{
52162306a36Sopenharmony_ci	struct sta_info *sta = v;
52262306a36Sopenharmony_ci	int i;
52362306a36Sopenharmony_ci
52462306a36Sopenharmony_ci	if (v == SEQ_START_TOKEN) {
52562306a36Sopenharmony_ci		seq_printf(m, "# BSSID CHAN SIGNAL NOISE RATE SSID FLAGS\n");
52662306a36Sopenharmony_ci		return 0;
52762306a36Sopenharmony_ci	}
52862306a36Sopenharmony_ci
52962306a36Sopenharmony_ci	if (!sta->ap)
53062306a36Sopenharmony_ci		return 0;
53162306a36Sopenharmony_ci
53262306a36Sopenharmony_ci	seq_printf(m, "%pM %d %d %d %d '",
53362306a36Sopenharmony_ci		   sta->addr,
53462306a36Sopenharmony_ci		   sta->u.ap.channel, sta->last_rx_signal,
53562306a36Sopenharmony_ci		   sta->last_rx_silence, sta->last_rx_rate);
53662306a36Sopenharmony_ci
53762306a36Sopenharmony_ci	for (i = 0; i < sta->u.ap.ssid_len; i++) {
53862306a36Sopenharmony_ci		if (sta->u.ap.ssid[i] >= 32 && sta->u.ap.ssid[i] < 127)
53962306a36Sopenharmony_ci			seq_putc(m, sta->u.ap.ssid[i]);
54062306a36Sopenharmony_ci		else
54162306a36Sopenharmony_ci			seq_printf(m, "<%02x>", sta->u.ap.ssid[i]);
54262306a36Sopenharmony_ci	}
54362306a36Sopenharmony_ci
54462306a36Sopenharmony_ci	seq_putc(m, '\'');
54562306a36Sopenharmony_ci	if (sta->capability & WLAN_CAPABILITY_ESS)
54662306a36Sopenharmony_ci		seq_puts(m, " [ESS]");
54762306a36Sopenharmony_ci	if (sta->capability & WLAN_CAPABILITY_IBSS)
54862306a36Sopenharmony_ci		seq_puts(m, " [IBSS]");
54962306a36Sopenharmony_ci	if (sta->capability & WLAN_CAPABILITY_PRIVACY)
55062306a36Sopenharmony_ci		seq_puts(m, " [WEP]");
55162306a36Sopenharmony_ci	seq_putc(m, '\n');
55262306a36Sopenharmony_ci	return 0;
55362306a36Sopenharmony_ci}
55462306a36Sopenharmony_ci
55562306a36Sopenharmony_cistatic void *prism2_ap_proc_start(struct seq_file *m, loff_t *_pos)
55662306a36Sopenharmony_ci{
55762306a36Sopenharmony_ci	struct ap_data *ap = pde_data(file_inode(m->file));
55862306a36Sopenharmony_ci	spin_lock_bh(&ap->sta_table_lock);
55962306a36Sopenharmony_ci	return seq_list_start_head(&ap->sta_list, *_pos);
56062306a36Sopenharmony_ci}
56162306a36Sopenharmony_ci
56262306a36Sopenharmony_cistatic void *prism2_ap_proc_next(struct seq_file *m, void *v, loff_t *_pos)
56362306a36Sopenharmony_ci{
56462306a36Sopenharmony_ci	struct ap_data *ap = pde_data(file_inode(m->file));
56562306a36Sopenharmony_ci	return seq_list_next(v, &ap->sta_list, _pos);
56662306a36Sopenharmony_ci}
56762306a36Sopenharmony_ci
56862306a36Sopenharmony_cistatic void prism2_ap_proc_stop(struct seq_file *m, void *v)
56962306a36Sopenharmony_ci{
57062306a36Sopenharmony_ci	struct ap_data *ap = pde_data(file_inode(m->file));
57162306a36Sopenharmony_ci	spin_unlock_bh(&ap->sta_table_lock);
57262306a36Sopenharmony_ci}
57362306a36Sopenharmony_ci
57462306a36Sopenharmony_cistatic const struct seq_operations prism2_ap_proc_seqops = {
57562306a36Sopenharmony_ci	.start	= prism2_ap_proc_start,
57662306a36Sopenharmony_ci	.next	= prism2_ap_proc_next,
57762306a36Sopenharmony_ci	.stop	= prism2_ap_proc_stop,
57862306a36Sopenharmony_ci	.show	= prism2_ap_proc_show,
57962306a36Sopenharmony_ci};
58062306a36Sopenharmony_ci#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */
58162306a36Sopenharmony_ci
58262306a36Sopenharmony_ci
58362306a36Sopenharmony_civoid hostap_check_sta_fw_version(struct ap_data *ap, int sta_fw_ver)
58462306a36Sopenharmony_ci{
58562306a36Sopenharmony_ci	if (!ap)
58662306a36Sopenharmony_ci		return;
58762306a36Sopenharmony_ci
58862306a36Sopenharmony_ci	if (sta_fw_ver == PRISM2_FW_VER(0,8,0)) {
58962306a36Sopenharmony_ci		PDEBUG(DEBUG_AP, "Using data::nullfunc ACK workaround - "
59062306a36Sopenharmony_ci		       "firmware upgrade recommended\n");
59162306a36Sopenharmony_ci		ap->nullfunc_ack = 1;
59262306a36Sopenharmony_ci	} else
59362306a36Sopenharmony_ci		ap->nullfunc_ack = 0;
59462306a36Sopenharmony_ci
59562306a36Sopenharmony_ci	if (sta_fw_ver == PRISM2_FW_VER(1,4,2)) {
59662306a36Sopenharmony_ci		printk(KERN_WARNING "%s: Warning: secondary station firmware "
59762306a36Sopenharmony_ci		       "version 1.4.2 does not seem to work in Host AP mode\n",
59862306a36Sopenharmony_ci		       ap->local->dev->name);
59962306a36Sopenharmony_ci	}
60062306a36Sopenharmony_ci}
60162306a36Sopenharmony_ci
60262306a36Sopenharmony_ci
60362306a36Sopenharmony_ci/* Called only as a tasklet (software IRQ) */
60462306a36Sopenharmony_cistatic void hostap_ap_tx_cb(struct sk_buff *skb, int ok, void *data)
60562306a36Sopenharmony_ci{
60662306a36Sopenharmony_ci	struct ap_data *ap = data;
60762306a36Sopenharmony_ci	struct ieee80211_hdr *hdr;
60862306a36Sopenharmony_ci
60962306a36Sopenharmony_ci	if (!ap->local->hostapd || !ap->local->apdev) {
61062306a36Sopenharmony_ci		dev_kfree_skb(skb);
61162306a36Sopenharmony_ci		return;
61262306a36Sopenharmony_ci	}
61362306a36Sopenharmony_ci
61462306a36Sopenharmony_ci	/* Pass the TX callback frame to the hostapd; use 802.11 header version
61562306a36Sopenharmony_ci	 * 1 to indicate failure (no ACK) and 2 success (frame ACKed) */
61662306a36Sopenharmony_ci
61762306a36Sopenharmony_ci	hdr = (struct ieee80211_hdr *) skb->data;
61862306a36Sopenharmony_ci	hdr->frame_control &= cpu_to_le16(~IEEE80211_FCTL_VERS);
61962306a36Sopenharmony_ci	hdr->frame_control |= cpu_to_le16(ok ? BIT(1) : BIT(0));
62062306a36Sopenharmony_ci
62162306a36Sopenharmony_ci	skb->dev = ap->local->apdev;
62262306a36Sopenharmony_ci	skb_pull(skb, hostap_80211_get_hdrlen(hdr->frame_control));
62362306a36Sopenharmony_ci	skb->pkt_type = PACKET_OTHERHOST;
62462306a36Sopenharmony_ci	skb->protocol = cpu_to_be16(ETH_P_802_2);
62562306a36Sopenharmony_ci	memset(skb->cb, 0, sizeof(skb->cb));
62662306a36Sopenharmony_ci	netif_rx(skb);
62762306a36Sopenharmony_ci}
62862306a36Sopenharmony_ci
62962306a36Sopenharmony_ci
63062306a36Sopenharmony_ci#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT
63162306a36Sopenharmony_ci/* Called only as a tasklet (software IRQ) */
63262306a36Sopenharmony_cistatic void hostap_ap_tx_cb_auth(struct sk_buff *skb, int ok, void *data)
63362306a36Sopenharmony_ci{
63462306a36Sopenharmony_ci	struct ap_data *ap = data;
63562306a36Sopenharmony_ci	struct net_device *dev = ap->local->dev;
63662306a36Sopenharmony_ci	struct ieee80211_hdr *hdr;
63762306a36Sopenharmony_ci	u16 auth_alg, auth_transaction, status;
63862306a36Sopenharmony_ci	__le16 *pos;
63962306a36Sopenharmony_ci	struct sta_info *sta = NULL;
64062306a36Sopenharmony_ci	char *txt = NULL;
64162306a36Sopenharmony_ci
64262306a36Sopenharmony_ci	if (ap->local->hostapd) {
64362306a36Sopenharmony_ci		dev_kfree_skb(skb);
64462306a36Sopenharmony_ci		return;
64562306a36Sopenharmony_ci	}
64662306a36Sopenharmony_ci
64762306a36Sopenharmony_ci	hdr = (struct ieee80211_hdr *) skb->data;
64862306a36Sopenharmony_ci	if (!ieee80211_is_auth(hdr->frame_control) ||
64962306a36Sopenharmony_ci	    skb->len < IEEE80211_MGMT_HDR_LEN + 6) {
65062306a36Sopenharmony_ci		printk(KERN_DEBUG "%s: hostap_ap_tx_cb_auth received invalid "
65162306a36Sopenharmony_ci		       "frame\n", dev->name);
65262306a36Sopenharmony_ci		dev_kfree_skb(skb);
65362306a36Sopenharmony_ci		return;
65462306a36Sopenharmony_ci	}
65562306a36Sopenharmony_ci
65662306a36Sopenharmony_ci	pos = (__le16 *) (skb->data + IEEE80211_MGMT_HDR_LEN);
65762306a36Sopenharmony_ci	auth_alg = le16_to_cpu(*pos++);
65862306a36Sopenharmony_ci	auth_transaction = le16_to_cpu(*pos++);
65962306a36Sopenharmony_ci	status = le16_to_cpu(*pos++);
66062306a36Sopenharmony_ci
66162306a36Sopenharmony_ci	if (!ok) {
66262306a36Sopenharmony_ci		txt = "frame was not ACKed";
66362306a36Sopenharmony_ci		goto done;
66462306a36Sopenharmony_ci	}
66562306a36Sopenharmony_ci
66662306a36Sopenharmony_ci	spin_lock(&ap->sta_table_lock);
66762306a36Sopenharmony_ci	sta = ap_get_sta(ap, hdr->addr1);
66862306a36Sopenharmony_ci	if (sta)
66962306a36Sopenharmony_ci		atomic_inc(&sta->users);
67062306a36Sopenharmony_ci	spin_unlock(&ap->sta_table_lock);
67162306a36Sopenharmony_ci
67262306a36Sopenharmony_ci	if (!sta) {
67362306a36Sopenharmony_ci		txt = "STA not found";
67462306a36Sopenharmony_ci		goto done;
67562306a36Sopenharmony_ci	}
67662306a36Sopenharmony_ci
67762306a36Sopenharmony_ci	if (status == WLAN_STATUS_SUCCESS &&
67862306a36Sopenharmony_ci	    ((auth_alg == WLAN_AUTH_OPEN && auth_transaction == 2) ||
67962306a36Sopenharmony_ci	     (auth_alg == WLAN_AUTH_SHARED_KEY && auth_transaction == 4))) {
68062306a36Sopenharmony_ci		txt = "STA authenticated";
68162306a36Sopenharmony_ci		sta->flags |= WLAN_STA_AUTH;
68262306a36Sopenharmony_ci		sta->last_auth = jiffies;
68362306a36Sopenharmony_ci	} else if (status != WLAN_STATUS_SUCCESS)
68462306a36Sopenharmony_ci		txt = "authentication failed";
68562306a36Sopenharmony_ci
68662306a36Sopenharmony_ci done:
68762306a36Sopenharmony_ci	if (sta)
68862306a36Sopenharmony_ci		atomic_dec(&sta->users);
68962306a36Sopenharmony_ci	if (txt) {
69062306a36Sopenharmony_ci		PDEBUG(DEBUG_AP, "%s: %pM auth_cb - alg=%d "
69162306a36Sopenharmony_ci		       "trans#=%d status=%d - %s\n",
69262306a36Sopenharmony_ci		       dev->name, hdr->addr1,
69362306a36Sopenharmony_ci		       auth_alg, auth_transaction, status, txt);
69462306a36Sopenharmony_ci	}
69562306a36Sopenharmony_ci	dev_kfree_skb(skb);
69662306a36Sopenharmony_ci}
69762306a36Sopenharmony_ci
69862306a36Sopenharmony_ci
69962306a36Sopenharmony_ci/* Called only as a tasklet (software IRQ) */
70062306a36Sopenharmony_cistatic void hostap_ap_tx_cb_assoc(struct sk_buff *skb, int ok, void *data)
70162306a36Sopenharmony_ci{
70262306a36Sopenharmony_ci	struct ap_data *ap = data;
70362306a36Sopenharmony_ci	struct net_device *dev = ap->local->dev;
70462306a36Sopenharmony_ci	struct ieee80211_hdr *hdr;
70562306a36Sopenharmony_ci	u16 status;
70662306a36Sopenharmony_ci	__le16 *pos;
70762306a36Sopenharmony_ci	struct sta_info *sta = NULL;
70862306a36Sopenharmony_ci	char *txt = NULL;
70962306a36Sopenharmony_ci
71062306a36Sopenharmony_ci	if (ap->local->hostapd) {
71162306a36Sopenharmony_ci		dev_kfree_skb(skb);
71262306a36Sopenharmony_ci		return;
71362306a36Sopenharmony_ci	}
71462306a36Sopenharmony_ci
71562306a36Sopenharmony_ci	hdr = (struct ieee80211_hdr *) skb->data;
71662306a36Sopenharmony_ci	if ((!ieee80211_is_assoc_resp(hdr->frame_control) &&
71762306a36Sopenharmony_ci	     !ieee80211_is_reassoc_resp(hdr->frame_control)) ||
71862306a36Sopenharmony_ci	    skb->len < IEEE80211_MGMT_HDR_LEN + 4) {
71962306a36Sopenharmony_ci		printk(KERN_DEBUG "%s: hostap_ap_tx_cb_assoc received invalid "
72062306a36Sopenharmony_ci		       "frame\n", dev->name);
72162306a36Sopenharmony_ci		dev_kfree_skb(skb);
72262306a36Sopenharmony_ci		return;
72362306a36Sopenharmony_ci	}
72462306a36Sopenharmony_ci
72562306a36Sopenharmony_ci	if (!ok) {
72662306a36Sopenharmony_ci		txt = "frame was not ACKed";
72762306a36Sopenharmony_ci		goto done;
72862306a36Sopenharmony_ci	}
72962306a36Sopenharmony_ci
73062306a36Sopenharmony_ci	spin_lock(&ap->sta_table_lock);
73162306a36Sopenharmony_ci	sta = ap_get_sta(ap, hdr->addr1);
73262306a36Sopenharmony_ci	if (sta)
73362306a36Sopenharmony_ci		atomic_inc(&sta->users);
73462306a36Sopenharmony_ci	spin_unlock(&ap->sta_table_lock);
73562306a36Sopenharmony_ci
73662306a36Sopenharmony_ci	if (!sta) {
73762306a36Sopenharmony_ci		txt = "STA not found";
73862306a36Sopenharmony_ci		goto done;
73962306a36Sopenharmony_ci	}
74062306a36Sopenharmony_ci
74162306a36Sopenharmony_ci	pos = (__le16 *) (skb->data + IEEE80211_MGMT_HDR_LEN);
74262306a36Sopenharmony_ci	pos++;
74362306a36Sopenharmony_ci	status = le16_to_cpu(*pos++);
74462306a36Sopenharmony_ci	if (status == WLAN_STATUS_SUCCESS) {
74562306a36Sopenharmony_ci		if (!(sta->flags & WLAN_STA_ASSOC))
74662306a36Sopenharmony_ci			hostap_event_new_sta(dev, sta);
74762306a36Sopenharmony_ci		txt = "STA associated";
74862306a36Sopenharmony_ci		sta->flags |= WLAN_STA_ASSOC;
74962306a36Sopenharmony_ci		sta->last_assoc = jiffies;
75062306a36Sopenharmony_ci	} else
75162306a36Sopenharmony_ci		txt = "association failed";
75262306a36Sopenharmony_ci
75362306a36Sopenharmony_ci done:
75462306a36Sopenharmony_ci	if (sta)
75562306a36Sopenharmony_ci		atomic_dec(&sta->users);
75662306a36Sopenharmony_ci	if (txt) {
75762306a36Sopenharmony_ci		PDEBUG(DEBUG_AP, "%s: %pM assoc_cb - %s\n",
75862306a36Sopenharmony_ci		       dev->name, hdr->addr1, txt);
75962306a36Sopenharmony_ci	}
76062306a36Sopenharmony_ci	dev_kfree_skb(skb);
76162306a36Sopenharmony_ci}
76262306a36Sopenharmony_ci
76362306a36Sopenharmony_ci/* Called only as a tasklet (software IRQ); TX callback for poll frames used
76462306a36Sopenharmony_ci * in verifying whether the STA is still present. */
76562306a36Sopenharmony_cistatic void hostap_ap_tx_cb_poll(struct sk_buff *skb, int ok, void *data)
76662306a36Sopenharmony_ci{
76762306a36Sopenharmony_ci	struct ap_data *ap = data;
76862306a36Sopenharmony_ci	struct ieee80211_hdr *hdr;
76962306a36Sopenharmony_ci	struct sta_info *sta;
77062306a36Sopenharmony_ci
77162306a36Sopenharmony_ci	if (skb->len < 24)
77262306a36Sopenharmony_ci		goto fail;
77362306a36Sopenharmony_ci	hdr = (struct ieee80211_hdr *) skb->data;
77462306a36Sopenharmony_ci	if (ok) {
77562306a36Sopenharmony_ci		spin_lock(&ap->sta_table_lock);
77662306a36Sopenharmony_ci		sta = ap_get_sta(ap, hdr->addr1);
77762306a36Sopenharmony_ci		if (sta)
77862306a36Sopenharmony_ci			sta->flags &= ~WLAN_STA_PENDING_POLL;
77962306a36Sopenharmony_ci		spin_unlock(&ap->sta_table_lock);
78062306a36Sopenharmony_ci	} else {
78162306a36Sopenharmony_ci		PDEBUG(DEBUG_AP,
78262306a36Sopenharmony_ci		       "%s: STA %pM did not ACK activity poll frame\n",
78362306a36Sopenharmony_ci		       ap->local->dev->name, hdr->addr1);
78462306a36Sopenharmony_ci	}
78562306a36Sopenharmony_ci
78662306a36Sopenharmony_ci fail:
78762306a36Sopenharmony_ci	dev_kfree_skb(skb);
78862306a36Sopenharmony_ci}
78962306a36Sopenharmony_ci#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */
79062306a36Sopenharmony_ci
79162306a36Sopenharmony_ci
79262306a36Sopenharmony_civoid hostap_init_data(local_info_t *local)
79362306a36Sopenharmony_ci{
79462306a36Sopenharmony_ci	struct ap_data *ap = local->ap;
79562306a36Sopenharmony_ci
79662306a36Sopenharmony_ci	if (ap == NULL) {
79762306a36Sopenharmony_ci		printk(KERN_WARNING "hostap_init_data: ap == NULL\n");
79862306a36Sopenharmony_ci		return;
79962306a36Sopenharmony_ci	}
80062306a36Sopenharmony_ci	memset(ap, 0, sizeof(struct ap_data));
80162306a36Sopenharmony_ci	ap->local = local;
80262306a36Sopenharmony_ci
80362306a36Sopenharmony_ci	ap->ap_policy = GET_INT_PARM(other_ap_policy, local->card_idx);
80462306a36Sopenharmony_ci	ap->bridge_packets = GET_INT_PARM(ap_bridge_packets, local->card_idx);
80562306a36Sopenharmony_ci	ap->max_inactivity =
80662306a36Sopenharmony_ci		GET_INT_PARM(ap_max_inactivity, local->card_idx) * HZ;
80762306a36Sopenharmony_ci	ap->autom_ap_wds = GET_INT_PARM(autom_ap_wds, local->card_idx);
80862306a36Sopenharmony_ci
80962306a36Sopenharmony_ci	spin_lock_init(&ap->sta_table_lock);
81062306a36Sopenharmony_ci	INIT_LIST_HEAD(&ap->sta_list);
81162306a36Sopenharmony_ci
81262306a36Sopenharmony_ci	/* Initialize task queue structure for AP management */
81362306a36Sopenharmony_ci	INIT_WORK(&local->ap->add_sta_proc_queue, handle_add_proc_queue);
81462306a36Sopenharmony_ci
81562306a36Sopenharmony_ci	ap->tx_callback_idx =
81662306a36Sopenharmony_ci		hostap_tx_callback_register(local, hostap_ap_tx_cb, ap);
81762306a36Sopenharmony_ci	if (ap->tx_callback_idx == 0)
81862306a36Sopenharmony_ci		printk(KERN_WARNING "%s: failed to register TX callback for "
81962306a36Sopenharmony_ci		       "AP\n", local->dev->name);
82062306a36Sopenharmony_ci#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT
82162306a36Sopenharmony_ci	INIT_WORK(&local->ap->wds_oper_queue, handle_wds_oper_queue);
82262306a36Sopenharmony_ci
82362306a36Sopenharmony_ci	ap->tx_callback_auth =
82462306a36Sopenharmony_ci		hostap_tx_callback_register(local, hostap_ap_tx_cb_auth, ap);
82562306a36Sopenharmony_ci	ap->tx_callback_assoc =
82662306a36Sopenharmony_ci		hostap_tx_callback_register(local, hostap_ap_tx_cb_assoc, ap);
82762306a36Sopenharmony_ci	ap->tx_callback_poll =
82862306a36Sopenharmony_ci		hostap_tx_callback_register(local, hostap_ap_tx_cb_poll, ap);
82962306a36Sopenharmony_ci	if (ap->tx_callback_auth == 0 || ap->tx_callback_assoc == 0 ||
83062306a36Sopenharmony_ci		ap->tx_callback_poll == 0)
83162306a36Sopenharmony_ci		printk(KERN_WARNING "%s: failed to register TX callback for "
83262306a36Sopenharmony_ci		       "AP\n", local->dev->name);
83362306a36Sopenharmony_ci
83462306a36Sopenharmony_ci	spin_lock_init(&ap->mac_restrictions.lock);
83562306a36Sopenharmony_ci	INIT_LIST_HEAD(&ap->mac_restrictions.mac_list);
83662306a36Sopenharmony_ci#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */
83762306a36Sopenharmony_ci
83862306a36Sopenharmony_ci	ap->initialized = 1;
83962306a36Sopenharmony_ci}
84062306a36Sopenharmony_ci
84162306a36Sopenharmony_ci
84262306a36Sopenharmony_civoid hostap_init_ap_proc(local_info_t *local)
84362306a36Sopenharmony_ci{
84462306a36Sopenharmony_ci	struct ap_data *ap = local->ap;
84562306a36Sopenharmony_ci
84662306a36Sopenharmony_ci	ap->proc = local->proc;
84762306a36Sopenharmony_ci	if (ap->proc == NULL)
84862306a36Sopenharmony_ci		return;
84962306a36Sopenharmony_ci
85062306a36Sopenharmony_ci#ifndef PRISM2_NO_PROCFS_DEBUG
85162306a36Sopenharmony_ci	proc_create_single_data("ap_debug", 0, ap->proc, ap_debug_proc_show, ap);
85262306a36Sopenharmony_ci#endif /* PRISM2_NO_PROCFS_DEBUG */
85362306a36Sopenharmony_ci
85462306a36Sopenharmony_ci#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT
85562306a36Sopenharmony_ci	proc_create_seq_data("ap_control", 0, ap->proc, &ap_control_proc_seqops,
85662306a36Sopenharmony_ci			ap);
85762306a36Sopenharmony_ci	proc_create_seq_data("ap", 0, ap->proc, &prism2_ap_proc_seqops, ap);
85862306a36Sopenharmony_ci#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */
85962306a36Sopenharmony_ci
86062306a36Sopenharmony_ci}
86162306a36Sopenharmony_ci
86262306a36Sopenharmony_ci
86362306a36Sopenharmony_civoid hostap_free_data(struct ap_data *ap)
86462306a36Sopenharmony_ci{
86562306a36Sopenharmony_ci	struct sta_info *n, *sta;
86662306a36Sopenharmony_ci
86762306a36Sopenharmony_ci	if (ap == NULL || !ap->initialized) {
86862306a36Sopenharmony_ci		printk(KERN_DEBUG "hostap_free_data: ap has not yet been "
86962306a36Sopenharmony_ci		       "initialized - skip resource freeing\n");
87062306a36Sopenharmony_ci		return;
87162306a36Sopenharmony_ci	}
87262306a36Sopenharmony_ci
87362306a36Sopenharmony_ci	flush_work(&ap->add_sta_proc_queue);
87462306a36Sopenharmony_ci
87562306a36Sopenharmony_ci#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT
87662306a36Sopenharmony_ci	flush_work(&ap->wds_oper_queue);
87762306a36Sopenharmony_ci	if (ap->crypt)
87862306a36Sopenharmony_ci		ap->crypt->deinit(ap->crypt_priv);
87962306a36Sopenharmony_ci	ap->crypt = ap->crypt_priv = NULL;
88062306a36Sopenharmony_ci#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */
88162306a36Sopenharmony_ci
88262306a36Sopenharmony_ci	list_for_each_entry_safe(sta, n, &ap->sta_list, list) {
88362306a36Sopenharmony_ci		ap_sta_hash_del(ap, sta);
88462306a36Sopenharmony_ci		list_del(&sta->list);
88562306a36Sopenharmony_ci		if ((sta->flags & WLAN_STA_ASSOC) && !sta->ap && sta->local)
88662306a36Sopenharmony_ci			hostap_event_expired_sta(sta->local->dev, sta);
88762306a36Sopenharmony_ci		ap_free_sta(ap, sta);
88862306a36Sopenharmony_ci	}
88962306a36Sopenharmony_ci
89062306a36Sopenharmony_ci#ifndef PRISM2_NO_PROCFS_DEBUG
89162306a36Sopenharmony_ci	if (ap->proc != NULL) {
89262306a36Sopenharmony_ci		remove_proc_entry("ap_debug", ap->proc);
89362306a36Sopenharmony_ci	}
89462306a36Sopenharmony_ci#endif /* PRISM2_NO_PROCFS_DEBUG */
89562306a36Sopenharmony_ci
89662306a36Sopenharmony_ci#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT
89762306a36Sopenharmony_ci	if (ap->proc != NULL) {
89862306a36Sopenharmony_ci	  remove_proc_entry("ap", ap->proc);
89962306a36Sopenharmony_ci		remove_proc_entry("ap_control", ap->proc);
90062306a36Sopenharmony_ci	}
90162306a36Sopenharmony_ci	ap_control_flush_macs(&ap->mac_restrictions);
90262306a36Sopenharmony_ci#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */
90362306a36Sopenharmony_ci
90462306a36Sopenharmony_ci	ap->initialized = 0;
90562306a36Sopenharmony_ci}
90662306a36Sopenharmony_ci
90762306a36Sopenharmony_ci
90862306a36Sopenharmony_ci/* caller should have mutex for AP STA list handling */
90962306a36Sopenharmony_cistatic struct sta_info* ap_get_sta(struct ap_data *ap, u8 *sta)
91062306a36Sopenharmony_ci{
91162306a36Sopenharmony_ci	struct sta_info *s;
91262306a36Sopenharmony_ci
91362306a36Sopenharmony_ci	s = ap->sta_hash[STA_HASH(sta)];
91462306a36Sopenharmony_ci	while (s != NULL && !ether_addr_equal(s->addr, sta))
91562306a36Sopenharmony_ci		s = s->hnext;
91662306a36Sopenharmony_ci	return s;
91762306a36Sopenharmony_ci}
91862306a36Sopenharmony_ci
91962306a36Sopenharmony_ci
92062306a36Sopenharmony_ci#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT
92162306a36Sopenharmony_ci
92262306a36Sopenharmony_ci/* Called from timer handler and from scheduled AP queue handlers */
92362306a36Sopenharmony_cistatic void prism2_send_mgmt(struct net_device *dev,
92462306a36Sopenharmony_ci			     u16 type_subtype, char *body,
92562306a36Sopenharmony_ci			     int body_len, u8 *addr, u16 tx_cb_idx)
92662306a36Sopenharmony_ci{
92762306a36Sopenharmony_ci	struct hostap_interface *iface;
92862306a36Sopenharmony_ci	local_info_t *local;
92962306a36Sopenharmony_ci	struct ieee80211_hdr *hdr;
93062306a36Sopenharmony_ci	u16 fc;
93162306a36Sopenharmony_ci	struct sk_buff *skb;
93262306a36Sopenharmony_ci	struct hostap_skb_tx_data *meta;
93362306a36Sopenharmony_ci	int hdrlen;
93462306a36Sopenharmony_ci
93562306a36Sopenharmony_ci	iface = netdev_priv(dev);
93662306a36Sopenharmony_ci	local = iface->local;
93762306a36Sopenharmony_ci	dev = local->dev; /* always use master radio device */
93862306a36Sopenharmony_ci	iface = netdev_priv(dev);
93962306a36Sopenharmony_ci
94062306a36Sopenharmony_ci	if (!(dev->flags & IFF_UP)) {
94162306a36Sopenharmony_ci		PDEBUG(DEBUG_AP, "%s: prism2_send_mgmt - device is not UP - "
94262306a36Sopenharmony_ci		       "cannot send frame\n", dev->name);
94362306a36Sopenharmony_ci		return;
94462306a36Sopenharmony_ci	}
94562306a36Sopenharmony_ci
94662306a36Sopenharmony_ci	skb = dev_alloc_skb(sizeof(*hdr) + body_len);
94762306a36Sopenharmony_ci	if (skb == NULL) {
94862306a36Sopenharmony_ci		PDEBUG(DEBUG_AP, "%s: prism2_send_mgmt failed to allocate "
94962306a36Sopenharmony_ci		       "skb\n", dev->name);
95062306a36Sopenharmony_ci		return;
95162306a36Sopenharmony_ci	}
95262306a36Sopenharmony_ci
95362306a36Sopenharmony_ci	fc = type_subtype;
95462306a36Sopenharmony_ci	hdrlen = hostap_80211_get_hdrlen(cpu_to_le16(type_subtype));
95562306a36Sopenharmony_ci	hdr = skb_put_zero(skb, hdrlen);
95662306a36Sopenharmony_ci	if (body)
95762306a36Sopenharmony_ci		skb_put_data(skb, body, body_len);
95862306a36Sopenharmony_ci
95962306a36Sopenharmony_ci	/* FIX: ctrl::ack sending used special HFA384X_TX_CTRL_802_11
96062306a36Sopenharmony_ci	 * tx_control instead of using local->tx_control */
96162306a36Sopenharmony_ci
96262306a36Sopenharmony_ci
96362306a36Sopenharmony_ci	memcpy(hdr->addr1, addr, ETH_ALEN); /* DA / RA */
96462306a36Sopenharmony_ci	if (ieee80211_is_data(hdr->frame_control)) {
96562306a36Sopenharmony_ci		fc |= IEEE80211_FCTL_FROMDS;
96662306a36Sopenharmony_ci		memcpy(hdr->addr2, dev->dev_addr, ETH_ALEN); /* BSSID */
96762306a36Sopenharmony_ci		memcpy(hdr->addr3, dev->dev_addr, ETH_ALEN); /* SA */
96862306a36Sopenharmony_ci	} else if (ieee80211_is_ctl(hdr->frame_control)) {
96962306a36Sopenharmony_ci		/* control:ACK does not have addr2 or addr3 */
97062306a36Sopenharmony_ci		eth_zero_addr(hdr->addr2);
97162306a36Sopenharmony_ci		eth_zero_addr(hdr->addr3);
97262306a36Sopenharmony_ci	} else {
97362306a36Sopenharmony_ci		memcpy(hdr->addr2, dev->dev_addr, ETH_ALEN); /* SA */
97462306a36Sopenharmony_ci		memcpy(hdr->addr3, dev->dev_addr, ETH_ALEN); /* BSSID */
97562306a36Sopenharmony_ci	}
97662306a36Sopenharmony_ci
97762306a36Sopenharmony_ci	hdr->frame_control = cpu_to_le16(fc);
97862306a36Sopenharmony_ci
97962306a36Sopenharmony_ci	meta = (struct hostap_skb_tx_data *) skb->cb;
98062306a36Sopenharmony_ci	memset(meta, 0, sizeof(*meta));
98162306a36Sopenharmony_ci	meta->magic = HOSTAP_SKB_TX_DATA_MAGIC;
98262306a36Sopenharmony_ci	meta->iface = iface;
98362306a36Sopenharmony_ci	meta->tx_cb_idx = tx_cb_idx;
98462306a36Sopenharmony_ci
98562306a36Sopenharmony_ci	skb->dev = dev;
98662306a36Sopenharmony_ci	skb_reset_mac_header(skb);
98762306a36Sopenharmony_ci	skb_reset_network_header(skb);
98862306a36Sopenharmony_ci	dev_queue_xmit(skb);
98962306a36Sopenharmony_ci}
99062306a36Sopenharmony_ci#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */
99162306a36Sopenharmony_ci
99262306a36Sopenharmony_ci#ifdef CONFIG_PROC_FS
99362306a36Sopenharmony_cistatic int prism2_sta_proc_show(struct seq_file *m, void *v)
99462306a36Sopenharmony_ci{
99562306a36Sopenharmony_ci	struct sta_info *sta = m->private;
99662306a36Sopenharmony_ci	int i;
99762306a36Sopenharmony_ci
99862306a36Sopenharmony_ci	/* FIX: possible race condition.. the STA data could have just expired,
99962306a36Sopenharmony_ci	 * but proc entry was still here so that the read could have started;
100062306a36Sopenharmony_ci	 * some locking should be done here.. */
100162306a36Sopenharmony_ci
100262306a36Sopenharmony_ci	seq_printf(m,
100362306a36Sopenharmony_ci		   "%s=%pM\nusers=%d\naid=%d\n"
100462306a36Sopenharmony_ci		   "flags=0x%04x%s%s%s%s%s%s%s\n"
100562306a36Sopenharmony_ci		   "capability=0x%02x\nlisten_interval=%d\nsupported_rates=",
100662306a36Sopenharmony_ci		   sta->ap ? "AP" : "STA",
100762306a36Sopenharmony_ci		   sta->addr, atomic_read(&sta->users), sta->aid,
100862306a36Sopenharmony_ci		   sta->flags,
100962306a36Sopenharmony_ci		   sta->flags & WLAN_STA_AUTH ? " AUTH" : "",
101062306a36Sopenharmony_ci		   sta->flags & WLAN_STA_ASSOC ? " ASSOC" : "",
101162306a36Sopenharmony_ci		   sta->flags & WLAN_STA_PS ? " PS" : "",
101262306a36Sopenharmony_ci		   sta->flags & WLAN_STA_TIM ? " TIM" : "",
101362306a36Sopenharmony_ci		   sta->flags & WLAN_STA_PERM ? " PERM" : "",
101462306a36Sopenharmony_ci		   sta->flags & WLAN_STA_AUTHORIZED ? " AUTHORIZED" : "",
101562306a36Sopenharmony_ci		   sta->flags & WLAN_STA_PENDING_POLL ? " POLL" : "",
101662306a36Sopenharmony_ci		   sta->capability, sta->listen_interval);
101762306a36Sopenharmony_ci	/* supported_rates: 500 kbit/s units with msb ignored */
101862306a36Sopenharmony_ci	for (i = 0; i < sizeof(sta->supported_rates); i++)
101962306a36Sopenharmony_ci		if (sta->supported_rates[i] != 0)
102062306a36Sopenharmony_ci			seq_printf(m, "%d%sMbps ",
102162306a36Sopenharmony_ci				   (sta->supported_rates[i] & 0x7f) / 2,
102262306a36Sopenharmony_ci				   sta->supported_rates[i] & 1 ? ".5" : "");
102362306a36Sopenharmony_ci	seq_printf(m,
102462306a36Sopenharmony_ci		   "\njiffies=%lu\nlast_auth=%lu\nlast_assoc=%lu\n"
102562306a36Sopenharmony_ci		   "last_rx=%lu\nlast_tx=%lu\nrx_packets=%lu\n"
102662306a36Sopenharmony_ci		   "tx_packets=%lu\n"
102762306a36Sopenharmony_ci		   "rx_bytes=%lu\ntx_bytes=%lu\nbuffer_count=%d\n"
102862306a36Sopenharmony_ci		   "last_rx: silence=%d dBm signal=%d dBm rate=%d%s Mbps\n"
102962306a36Sopenharmony_ci		   "tx_rate=%d\ntx[1M]=%d\ntx[2M]=%d\ntx[5.5M]=%d\n"
103062306a36Sopenharmony_ci		   "tx[11M]=%d\n"
103162306a36Sopenharmony_ci		   "rx[1M]=%d\nrx[2M]=%d\nrx[5.5M]=%d\nrx[11M]=%d\n",
103262306a36Sopenharmony_ci		   jiffies, sta->last_auth, sta->last_assoc, sta->last_rx,
103362306a36Sopenharmony_ci		   sta->last_tx,
103462306a36Sopenharmony_ci		   sta->rx_packets, sta->tx_packets, sta->rx_bytes,
103562306a36Sopenharmony_ci		   sta->tx_bytes, skb_queue_len(&sta->tx_buf),
103662306a36Sopenharmony_ci		   sta->last_rx_silence,
103762306a36Sopenharmony_ci		   sta->last_rx_signal, sta->last_rx_rate / 10,
103862306a36Sopenharmony_ci		   sta->last_rx_rate % 10 ? ".5" : "",
103962306a36Sopenharmony_ci		   sta->tx_rate, sta->tx_count[0], sta->tx_count[1],
104062306a36Sopenharmony_ci		   sta->tx_count[2], sta->tx_count[3],  sta->rx_count[0],
104162306a36Sopenharmony_ci		   sta->rx_count[1], sta->rx_count[2], sta->rx_count[3]);
104262306a36Sopenharmony_ci	if (sta->crypt && sta->crypt->ops && sta->crypt->ops->print_stats)
104362306a36Sopenharmony_ci		sta->crypt->ops->print_stats(m, sta->crypt->priv);
104462306a36Sopenharmony_ci#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT
104562306a36Sopenharmony_ci	if (sta->ap) {
104662306a36Sopenharmony_ci		if (sta->u.ap.channel >= 0)
104762306a36Sopenharmony_ci			seq_printf(m, "channel=%d\n", sta->u.ap.channel);
104862306a36Sopenharmony_ci		seq_puts(m, "ssid=");
104962306a36Sopenharmony_ci		for (i = 0; i < sta->u.ap.ssid_len; i++) {
105062306a36Sopenharmony_ci			if (sta->u.ap.ssid[i] >= 32 && sta->u.ap.ssid[i] < 127)
105162306a36Sopenharmony_ci				seq_putc(m, sta->u.ap.ssid[i]);
105262306a36Sopenharmony_ci			else
105362306a36Sopenharmony_ci				seq_printf(m, "<%02x>", sta->u.ap.ssid[i]);
105462306a36Sopenharmony_ci		}
105562306a36Sopenharmony_ci		seq_putc(m, '\n');
105662306a36Sopenharmony_ci	}
105762306a36Sopenharmony_ci#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */
105862306a36Sopenharmony_ci
105962306a36Sopenharmony_ci	return 0;
106062306a36Sopenharmony_ci}
106162306a36Sopenharmony_ci#endif
106262306a36Sopenharmony_ci
106362306a36Sopenharmony_cistatic void handle_add_proc_queue(struct work_struct *work)
106462306a36Sopenharmony_ci{
106562306a36Sopenharmony_ci	struct ap_data *ap = container_of(work, struct ap_data,
106662306a36Sopenharmony_ci					  add_sta_proc_queue);
106762306a36Sopenharmony_ci	struct sta_info *sta;
106862306a36Sopenharmony_ci	char name[20];
106962306a36Sopenharmony_ci	struct add_sta_proc_data *entry, *prev;
107062306a36Sopenharmony_ci
107162306a36Sopenharmony_ci	entry = ap->add_sta_proc_entries;
107262306a36Sopenharmony_ci	ap->add_sta_proc_entries = NULL;
107362306a36Sopenharmony_ci
107462306a36Sopenharmony_ci	while (entry) {
107562306a36Sopenharmony_ci		spin_lock_bh(&ap->sta_table_lock);
107662306a36Sopenharmony_ci		sta = ap_get_sta(ap, entry->addr);
107762306a36Sopenharmony_ci		if (sta)
107862306a36Sopenharmony_ci			atomic_inc(&sta->users);
107962306a36Sopenharmony_ci		spin_unlock_bh(&ap->sta_table_lock);
108062306a36Sopenharmony_ci
108162306a36Sopenharmony_ci		if (sta) {
108262306a36Sopenharmony_ci			sprintf(name, "%pM", sta->addr);
108362306a36Sopenharmony_ci			sta->proc = proc_create_single_data(
108462306a36Sopenharmony_ci				name, 0, ap->proc,
108562306a36Sopenharmony_ci				prism2_sta_proc_show, sta);
108662306a36Sopenharmony_ci
108762306a36Sopenharmony_ci			atomic_dec(&sta->users);
108862306a36Sopenharmony_ci		}
108962306a36Sopenharmony_ci
109062306a36Sopenharmony_ci		prev = entry;
109162306a36Sopenharmony_ci		entry = entry->next;
109262306a36Sopenharmony_ci		kfree(prev);
109362306a36Sopenharmony_ci	}
109462306a36Sopenharmony_ci}
109562306a36Sopenharmony_ci
109662306a36Sopenharmony_ci
109762306a36Sopenharmony_cistatic struct sta_info * ap_add_sta(struct ap_data *ap, u8 *addr)
109862306a36Sopenharmony_ci{
109962306a36Sopenharmony_ci	struct sta_info *sta;
110062306a36Sopenharmony_ci
110162306a36Sopenharmony_ci	sta = kzalloc(sizeof(struct sta_info), GFP_ATOMIC);
110262306a36Sopenharmony_ci	if (sta == NULL) {
110362306a36Sopenharmony_ci		PDEBUG(DEBUG_AP, "AP: kmalloc failed\n");
110462306a36Sopenharmony_ci		return NULL;
110562306a36Sopenharmony_ci	}
110662306a36Sopenharmony_ci
110762306a36Sopenharmony_ci	/* initialize STA info data */
110862306a36Sopenharmony_ci	sta->local = ap->local;
110962306a36Sopenharmony_ci	skb_queue_head_init(&sta->tx_buf);
111062306a36Sopenharmony_ci	memcpy(sta->addr, addr, ETH_ALEN);
111162306a36Sopenharmony_ci
111262306a36Sopenharmony_ci	atomic_inc(&sta->users);
111362306a36Sopenharmony_ci	spin_lock_bh(&ap->sta_table_lock);
111462306a36Sopenharmony_ci	list_add(&sta->list, &ap->sta_list);
111562306a36Sopenharmony_ci	ap->num_sta++;
111662306a36Sopenharmony_ci	ap_sta_hash_add(ap, sta);
111762306a36Sopenharmony_ci	spin_unlock_bh(&ap->sta_table_lock);
111862306a36Sopenharmony_ci
111962306a36Sopenharmony_ci	if (ap->proc) {
112062306a36Sopenharmony_ci		struct add_sta_proc_data *entry;
112162306a36Sopenharmony_ci		/* schedule a non-interrupt context process to add a procfs
112262306a36Sopenharmony_ci		 * entry for the STA since procfs code use GFP_KERNEL */
112362306a36Sopenharmony_ci		entry = kmalloc(sizeof(*entry), GFP_ATOMIC);
112462306a36Sopenharmony_ci		if (entry) {
112562306a36Sopenharmony_ci			memcpy(entry->addr, sta->addr, ETH_ALEN);
112662306a36Sopenharmony_ci			entry->next = ap->add_sta_proc_entries;
112762306a36Sopenharmony_ci			ap->add_sta_proc_entries = entry;
112862306a36Sopenharmony_ci			schedule_work(&ap->add_sta_proc_queue);
112962306a36Sopenharmony_ci		} else
113062306a36Sopenharmony_ci			printk(KERN_DEBUG "Failed to add STA proc data\n");
113162306a36Sopenharmony_ci	}
113262306a36Sopenharmony_ci
113362306a36Sopenharmony_ci#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT
113462306a36Sopenharmony_ci	timer_setup(&sta->timer, ap_handle_timer, 0);
113562306a36Sopenharmony_ci	sta->timer.expires = jiffies + ap->max_inactivity;
113662306a36Sopenharmony_ci	if (!ap->local->hostapd)
113762306a36Sopenharmony_ci		add_timer(&sta->timer);
113862306a36Sopenharmony_ci#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */
113962306a36Sopenharmony_ci
114062306a36Sopenharmony_ci	return sta;
114162306a36Sopenharmony_ci}
114262306a36Sopenharmony_ci
114362306a36Sopenharmony_ci
114462306a36Sopenharmony_cistatic int ap_tx_rate_ok(int rateidx, struct sta_info *sta,
114562306a36Sopenharmony_ci			 local_info_t *local)
114662306a36Sopenharmony_ci{
114762306a36Sopenharmony_ci	if (rateidx > sta->tx_max_rate ||
114862306a36Sopenharmony_ci	    !(sta->tx_supp_rates & (1 << rateidx)))
114962306a36Sopenharmony_ci		return 0;
115062306a36Sopenharmony_ci
115162306a36Sopenharmony_ci	if (local->tx_rate_control != 0 &&
115262306a36Sopenharmony_ci	    !(local->tx_rate_control & (1 << rateidx)))
115362306a36Sopenharmony_ci		return 0;
115462306a36Sopenharmony_ci
115562306a36Sopenharmony_ci	return 1;
115662306a36Sopenharmony_ci}
115762306a36Sopenharmony_ci
115862306a36Sopenharmony_ci
115962306a36Sopenharmony_cistatic void prism2_check_tx_rates(struct sta_info *sta)
116062306a36Sopenharmony_ci{
116162306a36Sopenharmony_ci	int i;
116262306a36Sopenharmony_ci
116362306a36Sopenharmony_ci	sta->tx_supp_rates = 0;
116462306a36Sopenharmony_ci	for (i = 0; i < sizeof(sta->supported_rates); i++) {
116562306a36Sopenharmony_ci		if ((sta->supported_rates[i] & 0x7f) == 2)
116662306a36Sopenharmony_ci			sta->tx_supp_rates |= WLAN_RATE_1M;
116762306a36Sopenharmony_ci		if ((sta->supported_rates[i] & 0x7f) == 4)
116862306a36Sopenharmony_ci			sta->tx_supp_rates |= WLAN_RATE_2M;
116962306a36Sopenharmony_ci		if ((sta->supported_rates[i] & 0x7f) == 11)
117062306a36Sopenharmony_ci			sta->tx_supp_rates |= WLAN_RATE_5M5;
117162306a36Sopenharmony_ci		if ((sta->supported_rates[i] & 0x7f) == 22)
117262306a36Sopenharmony_ci			sta->tx_supp_rates |= WLAN_RATE_11M;
117362306a36Sopenharmony_ci	}
117462306a36Sopenharmony_ci	sta->tx_max_rate = sta->tx_rate = sta->tx_rate_idx = 0;
117562306a36Sopenharmony_ci	if (sta->tx_supp_rates & WLAN_RATE_1M) {
117662306a36Sopenharmony_ci		sta->tx_max_rate = 0;
117762306a36Sopenharmony_ci		if (ap_tx_rate_ok(0, sta, sta->local)) {
117862306a36Sopenharmony_ci			sta->tx_rate = 10;
117962306a36Sopenharmony_ci			sta->tx_rate_idx = 0;
118062306a36Sopenharmony_ci		}
118162306a36Sopenharmony_ci	}
118262306a36Sopenharmony_ci	if (sta->tx_supp_rates & WLAN_RATE_2M) {
118362306a36Sopenharmony_ci		sta->tx_max_rate = 1;
118462306a36Sopenharmony_ci		if (ap_tx_rate_ok(1, sta, sta->local)) {
118562306a36Sopenharmony_ci			sta->tx_rate = 20;
118662306a36Sopenharmony_ci			sta->tx_rate_idx = 1;
118762306a36Sopenharmony_ci		}
118862306a36Sopenharmony_ci	}
118962306a36Sopenharmony_ci	if (sta->tx_supp_rates & WLAN_RATE_5M5) {
119062306a36Sopenharmony_ci		sta->tx_max_rate = 2;
119162306a36Sopenharmony_ci		if (ap_tx_rate_ok(2, sta, sta->local)) {
119262306a36Sopenharmony_ci			sta->tx_rate = 55;
119362306a36Sopenharmony_ci			sta->tx_rate_idx = 2;
119462306a36Sopenharmony_ci		}
119562306a36Sopenharmony_ci	}
119662306a36Sopenharmony_ci	if (sta->tx_supp_rates & WLAN_RATE_11M) {
119762306a36Sopenharmony_ci		sta->tx_max_rate = 3;
119862306a36Sopenharmony_ci		if (ap_tx_rate_ok(3, sta, sta->local)) {
119962306a36Sopenharmony_ci			sta->tx_rate = 110;
120062306a36Sopenharmony_ci			sta->tx_rate_idx = 3;
120162306a36Sopenharmony_ci		}
120262306a36Sopenharmony_ci	}
120362306a36Sopenharmony_ci}
120462306a36Sopenharmony_ci
120562306a36Sopenharmony_ci
120662306a36Sopenharmony_ci#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT
120762306a36Sopenharmony_ci
120862306a36Sopenharmony_cistatic void ap_crypt_init(struct ap_data *ap)
120962306a36Sopenharmony_ci{
121062306a36Sopenharmony_ci	ap->crypt = lib80211_get_crypto_ops("WEP");
121162306a36Sopenharmony_ci
121262306a36Sopenharmony_ci	if (ap->crypt) {
121362306a36Sopenharmony_ci		if (ap->crypt->init) {
121462306a36Sopenharmony_ci			ap->crypt_priv = ap->crypt->init(0);
121562306a36Sopenharmony_ci			if (ap->crypt_priv == NULL)
121662306a36Sopenharmony_ci				ap->crypt = NULL;
121762306a36Sopenharmony_ci			else {
121862306a36Sopenharmony_ci				u8 key[WEP_KEY_LEN];
121962306a36Sopenharmony_ci				get_random_bytes(key, WEP_KEY_LEN);
122062306a36Sopenharmony_ci				ap->crypt->set_key(key, WEP_KEY_LEN, NULL,
122162306a36Sopenharmony_ci						   ap->crypt_priv);
122262306a36Sopenharmony_ci			}
122362306a36Sopenharmony_ci		}
122462306a36Sopenharmony_ci	}
122562306a36Sopenharmony_ci
122662306a36Sopenharmony_ci	if (ap->crypt == NULL) {
122762306a36Sopenharmony_ci		printk(KERN_WARNING "AP could not initialize WEP: load module "
122862306a36Sopenharmony_ci		       "lib80211_crypt_wep.ko\n");
122962306a36Sopenharmony_ci	}
123062306a36Sopenharmony_ci}
123162306a36Sopenharmony_ci
123262306a36Sopenharmony_ci
123362306a36Sopenharmony_ci/* Generate challenge data for shared key authentication. IEEE 802.11 specifies
123462306a36Sopenharmony_ci * that WEP algorithm is used for generating challenge. This should be unique,
123562306a36Sopenharmony_ci * but otherwise there is not really need for randomness etc. Initialize WEP
123662306a36Sopenharmony_ci * with pseudo random key and then use increasing IV to get unique challenge
123762306a36Sopenharmony_ci * streams.
123862306a36Sopenharmony_ci *
123962306a36Sopenharmony_ci * Called only as a scheduled task for pending AP frames.
124062306a36Sopenharmony_ci */
124162306a36Sopenharmony_cistatic char * ap_auth_make_challenge(struct ap_data *ap)
124262306a36Sopenharmony_ci{
124362306a36Sopenharmony_ci	char *tmpbuf;
124462306a36Sopenharmony_ci	struct sk_buff *skb;
124562306a36Sopenharmony_ci
124662306a36Sopenharmony_ci	if (ap->crypt == NULL) {
124762306a36Sopenharmony_ci		ap_crypt_init(ap);
124862306a36Sopenharmony_ci		if (ap->crypt == NULL)
124962306a36Sopenharmony_ci			return NULL;
125062306a36Sopenharmony_ci	}
125162306a36Sopenharmony_ci
125262306a36Sopenharmony_ci	tmpbuf = kmalloc(WLAN_AUTH_CHALLENGE_LEN, GFP_ATOMIC);
125362306a36Sopenharmony_ci	if (tmpbuf == NULL) {
125462306a36Sopenharmony_ci		PDEBUG(DEBUG_AP, "AP: kmalloc failed for challenge\n");
125562306a36Sopenharmony_ci		return NULL;
125662306a36Sopenharmony_ci	}
125762306a36Sopenharmony_ci
125862306a36Sopenharmony_ci	skb = dev_alloc_skb(WLAN_AUTH_CHALLENGE_LEN +
125962306a36Sopenharmony_ci			    ap->crypt->extra_mpdu_prefix_len +
126062306a36Sopenharmony_ci			    ap->crypt->extra_mpdu_postfix_len);
126162306a36Sopenharmony_ci	if (skb == NULL) {
126262306a36Sopenharmony_ci		kfree(tmpbuf);
126362306a36Sopenharmony_ci		return NULL;
126462306a36Sopenharmony_ci	}
126562306a36Sopenharmony_ci
126662306a36Sopenharmony_ci	skb_reserve(skb, ap->crypt->extra_mpdu_prefix_len);
126762306a36Sopenharmony_ci	skb_put_zero(skb, WLAN_AUTH_CHALLENGE_LEN);
126862306a36Sopenharmony_ci	if (ap->crypt->encrypt_mpdu(skb, 0, ap->crypt_priv)) {
126962306a36Sopenharmony_ci		dev_kfree_skb(skb);
127062306a36Sopenharmony_ci		kfree(tmpbuf);
127162306a36Sopenharmony_ci		return NULL;
127262306a36Sopenharmony_ci	}
127362306a36Sopenharmony_ci
127462306a36Sopenharmony_ci	skb_copy_from_linear_data_offset(skb, ap->crypt->extra_mpdu_prefix_len,
127562306a36Sopenharmony_ci					 tmpbuf, WLAN_AUTH_CHALLENGE_LEN);
127662306a36Sopenharmony_ci	dev_kfree_skb(skb);
127762306a36Sopenharmony_ci
127862306a36Sopenharmony_ci	return tmpbuf;
127962306a36Sopenharmony_ci}
128062306a36Sopenharmony_ci
128162306a36Sopenharmony_ci
128262306a36Sopenharmony_ci/* Called only as a scheduled task for pending AP frames. */
128362306a36Sopenharmony_cistatic void handle_authen(local_info_t *local, struct sk_buff *skb,
128462306a36Sopenharmony_ci			  struct hostap_80211_rx_status *rx_stats)
128562306a36Sopenharmony_ci{
128662306a36Sopenharmony_ci	struct net_device *dev = local->dev;
128762306a36Sopenharmony_ci	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
128862306a36Sopenharmony_ci	size_t hdrlen;
128962306a36Sopenharmony_ci	struct ap_data *ap = local->ap;
129062306a36Sopenharmony_ci	char body[8 + WLAN_AUTH_CHALLENGE_LEN], *challenge = NULL;
129162306a36Sopenharmony_ci	int len, olen;
129262306a36Sopenharmony_ci	u16 auth_alg, auth_transaction, status_code;
129362306a36Sopenharmony_ci	__le16 *pos;
129462306a36Sopenharmony_ci	u16 resp = WLAN_STATUS_SUCCESS;
129562306a36Sopenharmony_ci	struct sta_info *sta = NULL;
129662306a36Sopenharmony_ci	struct lib80211_crypt_data *crypt;
129762306a36Sopenharmony_ci	char *txt = "";
129862306a36Sopenharmony_ci
129962306a36Sopenharmony_ci	len = skb->len - IEEE80211_MGMT_HDR_LEN;
130062306a36Sopenharmony_ci
130162306a36Sopenharmony_ci	hdrlen = hostap_80211_get_hdrlen(hdr->frame_control);
130262306a36Sopenharmony_ci
130362306a36Sopenharmony_ci	if (len < 6) {
130462306a36Sopenharmony_ci		PDEBUG(DEBUG_AP, "%s: handle_authen - too short payload "
130562306a36Sopenharmony_ci		       "(len=%d) from %pM\n", dev->name, len, hdr->addr2);
130662306a36Sopenharmony_ci		return;
130762306a36Sopenharmony_ci	}
130862306a36Sopenharmony_ci
130962306a36Sopenharmony_ci	spin_lock_bh(&local->ap->sta_table_lock);
131062306a36Sopenharmony_ci	sta = ap_get_sta(local->ap, hdr->addr2);
131162306a36Sopenharmony_ci	if (sta)
131262306a36Sopenharmony_ci		atomic_inc(&sta->users);
131362306a36Sopenharmony_ci	spin_unlock_bh(&local->ap->sta_table_lock);
131462306a36Sopenharmony_ci
131562306a36Sopenharmony_ci	if (sta && sta->crypt)
131662306a36Sopenharmony_ci		crypt = sta->crypt;
131762306a36Sopenharmony_ci	else {
131862306a36Sopenharmony_ci		int idx = 0;
131962306a36Sopenharmony_ci		if (skb->len >= hdrlen + 3)
132062306a36Sopenharmony_ci			idx = skb->data[hdrlen + 3] >> 6;
132162306a36Sopenharmony_ci		crypt = local->crypt_info.crypt[idx];
132262306a36Sopenharmony_ci	}
132362306a36Sopenharmony_ci
132462306a36Sopenharmony_ci	pos = (__le16 *) (skb->data + IEEE80211_MGMT_HDR_LEN);
132562306a36Sopenharmony_ci	auth_alg = __le16_to_cpu(*pos);
132662306a36Sopenharmony_ci	pos++;
132762306a36Sopenharmony_ci	auth_transaction = __le16_to_cpu(*pos);
132862306a36Sopenharmony_ci	pos++;
132962306a36Sopenharmony_ci	status_code = __le16_to_cpu(*pos);
133062306a36Sopenharmony_ci	pos++;
133162306a36Sopenharmony_ci
133262306a36Sopenharmony_ci	if (ether_addr_equal(dev->dev_addr, hdr->addr2) ||
133362306a36Sopenharmony_ci	    ap_control_mac_deny(&ap->mac_restrictions, hdr->addr2)) {
133462306a36Sopenharmony_ci		txt = "authentication denied";
133562306a36Sopenharmony_ci		resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
133662306a36Sopenharmony_ci		goto fail;
133762306a36Sopenharmony_ci	}
133862306a36Sopenharmony_ci
133962306a36Sopenharmony_ci	if (((local->auth_algs & PRISM2_AUTH_OPEN) &&
134062306a36Sopenharmony_ci	     auth_alg == WLAN_AUTH_OPEN) ||
134162306a36Sopenharmony_ci	    ((local->auth_algs & PRISM2_AUTH_SHARED_KEY) &&
134262306a36Sopenharmony_ci	     crypt && auth_alg == WLAN_AUTH_SHARED_KEY)) {
134362306a36Sopenharmony_ci	} else {
134462306a36Sopenharmony_ci		txt = "unsupported algorithm";
134562306a36Sopenharmony_ci		resp = WLAN_STATUS_NOT_SUPPORTED_AUTH_ALG;
134662306a36Sopenharmony_ci		goto fail;
134762306a36Sopenharmony_ci	}
134862306a36Sopenharmony_ci
134962306a36Sopenharmony_ci	if (len >= 8) {
135062306a36Sopenharmony_ci		u8 *u = (u8 *) pos;
135162306a36Sopenharmony_ci		if (*u == WLAN_EID_CHALLENGE) {
135262306a36Sopenharmony_ci			if (*(u + 1) != WLAN_AUTH_CHALLENGE_LEN) {
135362306a36Sopenharmony_ci				txt = "invalid challenge len";
135462306a36Sopenharmony_ci				resp = WLAN_STATUS_CHALLENGE_FAIL;
135562306a36Sopenharmony_ci				goto fail;
135662306a36Sopenharmony_ci			}
135762306a36Sopenharmony_ci			if (len - 8 < WLAN_AUTH_CHALLENGE_LEN) {
135862306a36Sopenharmony_ci				txt = "challenge underflow";
135962306a36Sopenharmony_ci				resp = WLAN_STATUS_CHALLENGE_FAIL;
136062306a36Sopenharmony_ci				goto fail;
136162306a36Sopenharmony_ci			}
136262306a36Sopenharmony_ci			challenge = (char *) (u + 2);
136362306a36Sopenharmony_ci		}
136462306a36Sopenharmony_ci	}
136562306a36Sopenharmony_ci
136662306a36Sopenharmony_ci	if (sta && sta->ap) {
136762306a36Sopenharmony_ci		if (time_after(jiffies, sta->u.ap.last_beacon +
136862306a36Sopenharmony_ci			       (10 * sta->listen_interval * HZ) / 1024)) {
136962306a36Sopenharmony_ci			PDEBUG(DEBUG_AP, "%s: no beacons received for a while,"
137062306a36Sopenharmony_ci			       " assuming AP %pM is now STA\n",
137162306a36Sopenharmony_ci			       dev->name, sta->addr);
137262306a36Sopenharmony_ci			sta->ap = 0;
137362306a36Sopenharmony_ci			sta->flags = 0;
137462306a36Sopenharmony_ci			sta->u.sta.challenge = NULL;
137562306a36Sopenharmony_ci		} else {
137662306a36Sopenharmony_ci			txt = "AP trying to authenticate?";
137762306a36Sopenharmony_ci			resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
137862306a36Sopenharmony_ci			goto fail;
137962306a36Sopenharmony_ci		}
138062306a36Sopenharmony_ci	}
138162306a36Sopenharmony_ci
138262306a36Sopenharmony_ci	if ((auth_alg == WLAN_AUTH_OPEN && auth_transaction == 1) ||
138362306a36Sopenharmony_ci	    (auth_alg == WLAN_AUTH_SHARED_KEY &&
138462306a36Sopenharmony_ci	     (auth_transaction == 1 ||
138562306a36Sopenharmony_ci	      (auth_transaction == 3 && sta != NULL &&
138662306a36Sopenharmony_ci	       sta->u.sta.challenge != NULL)))) {
138762306a36Sopenharmony_ci	} else {
138862306a36Sopenharmony_ci		txt = "unknown authentication transaction number";
138962306a36Sopenharmony_ci		resp = WLAN_STATUS_UNKNOWN_AUTH_TRANSACTION;
139062306a36Sopenharmony_ci		goto fail;
139162306a36Sopenharmony_ci	}
139262306a36Sopenharmony_ci
139362306a36Sopenharmony_ci	if (sta == NULL) {
139462306a36Sopenharmony_ci		txt = "new STA";
139562306a36Sopenharmony_ci
139662306a36Sopenharmony_ci		if (local->ap->num_sta >= MAX_STA_COUNT) {
139762306a36Sopenharmony_ci			/* FIX: might try to remove some old STAs first? */
139862306a36Sopenharmony_ci			txt = "no more room for new STAs";
139962306a36Sopenharmony_ci			resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
140062306a36Sopenharmony_ci			goto fail;
140162306a36Sopenharmony_ci		}
140262306a36Sopenharmony_ci
140362306a36Sopenharmony_ci		sta = ap_add_sta(local->ap, hdr->addr2);
140462306a36Sopenharmony_ci		if (sta == NULL) {
140562306a36Sopenharmony_ci			txt = "ap_add_sta failed";
140662306a36Sopenharmony_ci			resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
140762306a36Sopenharmony_ci			goto fail;
140862306a36Sopenharmony_ci		}
140962306a36Sopenharmony_ci	}
141062306a36Sopenharmony_ci
141162306a36Sopenharmony_ci	switch (auth_alg) {
141262306a36Sopenharmony_ci	case WLAN_AUTH_OPEN:
141362306a36Sopenharmony_ci		txt = "authOK";
141462306a36Sopenharmony_ci		/* IEEE 802.11 standard is not completely clear about
141562306a36Sopenharmony_ci		 * whether STA is considered authenticated after
141662306a36Sopenharmony_ci		 * authentication OK frame has been send or after it
141762306a36Sopenharmony_ci		 * has been ACKed. In order to reduce interoperability
141862306a36Sopenharmony_ci		 * issues, mark the STA authenticated before ACK. */
141962306a36Sopenharmony_ci		sta->flags |= WLAN_STA_AUTH;
142062306a36Sopenharmony_ci		break;
142162306a36Sopenharmony_ci
142262306a36Sopenharmony_ci	case WLAN_AUTH_SHARED_KEY:
142362306a36Sopenharmony_ci		if (auth_transaction == 1) {
142462306a36Sopenharmony_ci			if (sta->u.sta.challenge == NULL) {
142562306a36Sopenharmony_ci				sta->u.sta.challenge =
142662306a36Sopenharmony_ci					ap_auth_make_challenge(local->ap);
142762306a36Sopenharmony_ci				if (sta->u.sta.challenge == NULL) {
142862306a36Sopenharmony_ci					resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
142962306a36Sopenharmony_ci					goto fail;
143062306a36Sopenharmony_ci				}
143162306a36Sopenharmony_ci			}
143262306a36Sopenharmony_ci		} else {
143362306a36Sopenharmony_ci			if (sta->u.sta.challenge == NULL ||
143462306a36Sopenharmony_ci			    challenge == NULL ||
143562306a36Sopenharmony_ci			    memcmp(sta->u.sta.challenge, challenge,
143662306a36Sopenharmony_ci				   WLAN_AUTH_CHALLENGE_LEN) != 0 ||
143762306a36Sopenharmony_ci			    !ieee80211_has_protected(hdr->frame_control)) {
143862306a36Sopenharmony_ci				txt = "challenge response incorrect";
143962306a36Sopenharmony_ci				resp = WLAN_STATUS_CHALLENGE_FAIL;
144062306a36Sopenharmony_ci				goto fail;
144162306a36Sopenharmony_ci			}
144262306a36Sopenharmony_ci
144362306a36Sopenharmony_ci			txt = "challenge OK - authOK";
144462306a36Sopenharmony_ci			/* IEEE 802.11 standard is not completely clear about
144562306a36Sopenharmony_ci			 * whether STA is considered authenticated after
144662306a36Sopenharmony_ci			 * authentication OK frame has been send or after it
144762306a36Sopenharmony_ci			 * has been ACKed. In order to reduce interoperability
144862306a36Sopenharmony_ci			 * issues, mark the STA authenticated before ACK. */
144962306a36Sopenharmony_ci			sta->flags |= WLAN_STA_AUTH;
145062306a36Sopenharmony_ci			kfree(sta->u.sta.challenge);
145162306a36Sopenharmony_ci			sta->u.sta.challenge = NULL;
145262306a36Sopenharmony_ci		}
145362306a36Sopenharmony_ci		break;
145462306a36Sopenharmony_ci	}
145562306a36Sopenharmony_ci
145662306a36Sopenharmony_ci fail:
145762306a36Sopenharmony_ci	pos = (__le16 *) body;
145862306a36Sopenharmony_ci	*pos = cpu_to_le16(auth_alg);
145962306a36Sopenharmony_ci	pos++;
146062306a36Sopenharmony_ci	*pos = cpu_to_le16(auth_transaction + 1);
146162306a36Sopenharmony_ci	pos++;
146262306a36Sopenharmony_ci	*pos = cpu_to_le16(resp); /* status_code */
146362306a36Sopenharmony_ci	pos++;
146462306a36Sopenharmony_ci	olen = 6;
146562306a36Sopenharmony_ci
146662306a36Sopenharmony_ci	if (resp == WLAN_STATUS_SUCCESS && sta != NULL &&
146762306a36Sopenharmony_ci	    sta->u.sta.challenge != NULL &&
146862306a36Sopenharmony_ci	    auth_alg == WLAN_AUTH_SHARED_KEY && auth_transaction == 1) {
146962306a36Sopenharmony_ci		u8 *tmp = (u8 *) pos;
147062306a36Sopenharmony_ci		*tmp++ = WLAN_EID_CHALLENGE;
147162306a36Sopenharmony_ci		*tmp++ = WLAN_AUTH_CHALLENGE_LEN;
147262306a36Sopenharmony_ci		pos++;
147362306a36Sopenharmony_ci		memcpy(pos, sta->u.sta.challenge, WLAN_AUTH_CHALLENGE_LEN);
147462306a36Sopenharmony_ci		olen += 2 + WLAN_AUTH_CHALLENGE_LEN;
147562306a36Sopenharmony_ci	}
147662306a36Sopenharmony_ci
147762306a36Sopenharmony_ci	prism2_send_mgmt(dev, IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_AUTH,
147862306a36Sopenharmony_ci			 body, olen, hdr->addr2, ap->tx_callback_auth);
147962306a36Sopenharmony_ci
148062306a36Sopenharmony_ci	if (sta) {
148162306a36Sopenharmony_ci		sta->last_rx = jiffies;
148262306a36Sopenharmony_ci		atomic_dec(&sta->users);
148362306a36Sopenharmony_ci	}
148462306a36Sopenharmony_ci
148562306a36Sopenharmony_ci	if (resp) {
148662306a36Sopenharmony_ci		PDEBUG(DEBUG_AP, "%s: %pM auth (alg=%d "
148762306a36Sopenharmony_ci		       "trans#=%d stat=%d len=%d fc=%04x) ==> %d (%s)\n",
148862306a36Sopenharmony_ci		       dev->name, hdr->addr2,
148962306a36Sopenharmony_ci		       auth_alg, auth_transaction, status_code, len,
149062306a36Sopenharmony_ci		       le16_to_cpu(hdr->frame_control), resp, txt);
149162306a36Sopenharmony_ci	}
149262306a36Sopenharmony_ci}
149362306a36Sopenharmony_ci
149462306a36Sopenharmony_ci
149562306a36Sopenharmony_ci/* Called only as a scheduled task for pending AP frames. */
149662306a36Sopenharmony_cistatic void handle_assoc(local_info_t *local, struct sk_buff *skb,
149762306a36Sopenharmony_ci			 struct hostap_80211_rx_status *rx_stats, int reassoc)
149862306a36Sopenharmony_ci{
149962306a36Sopenharmony_ci	struct net_device *dev = local->dev;
150062306a36Sopenharmony_ci	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
150162306a36Sopenharmony_ci	char body[12], *p, *lpos;
150262306a36Sopenharmony_ci	int len, left;
150362306a36Sopenharmony_ci	__le16 *pos;
150462306a36Sopenharmony_ci	u16 resp = WLAN_STATUS_SUCCESS;
150562306a36Sopenharmony_ci	struct sta_info *sta = NULL;
150662306a36Sopenharmony_ci	int send_deauth = 0;
150762306a36Sopenharmony_ci	char __always_unused *txt = "";
150862306a36Sopenharmony_ci	u8 prev_ap[ETH_ALEN];
150962306a36Sopenharmony_ci
151062306a36Sopenharmony_ci	left = len = skb->len - IEEE80211_MGMT_HDR_LEN;
151162306a36Sopenharmony_ci
151262306a36Sopenharmony_ci	if (len < (reassoc ? 10 : 4)) {
151362306a36Sopenharmony_ci		PDEBUG(DEBUG_AP, "%s: handle_assoc - too short payload "
151462306a36Sopenharmony_ci		       "(len=%d, reassoc=%d) from %pM\n",
151562306a36Sopenharmony_ci		       dev->name, len, reassoc, hdr->addr2);
151662306a36Sopenharmony_ci		return;
151762306a36Sopenharmony_ci	}
151862306a36Sopenharmony_ci
151962306a36Sopenharmony_ci	spin_lock_bh(&local->ap->sta_table_lock);
152062306a36Sopenharmony_ci	sta = ap_get_sta(local->ap, hdr->addr2);
152162306a36Sopenharmony_ci	if (sta == NULL || (sta->flags & WLAN_STA_AUTH) == 0) {
152262306a36Sopenharmony_ci		spin_unlock_bh(&local->ap->sta_table_lock);
152362306a36Sopenharmony_ci		txt = "trying to associate before authentication";
152462306a36Sopenharmony_ci		send_deauth = 1;
152562306a36Sopenharmony_ci		resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
152662306a36Sopenharmony_ci		sta = NULL; /* do not decrement sta->users */
152762306a36Sopenharmony_ci		goto fail;
152862306a36Sopenharmony_ci	}
152962306a36Sopenharmony_ci	atomic_inc(&sta->users);
153062306a36Sopenharmony_ci	spin_unlock_bh(&local->ap->sta_table_lock);
153162306a36Sopenharmony_ci
153262306a36Sopenharmony_ci	pos = (__le16 *) (skb->data + IEEE80211_MGMT_HDR_LEN);
153362306a36Sopenharmony_ci	sta->capability = __le16_to_cpu(*pos);
153462306a36Sopenharmony_ci	pos++; left -= 2;
153562306a36Sopenharmony_ci	sta->listen_interval = __le16_to_cpu(*pos);
153662306a36Sopenharmony_ci	pos++; left -= 2;
153762306a36Sopenharmony_ci
153862306a36Sopenharmony_ci	if (reassoc) {
153962306a36Sopenharmony_ci		memcpy(prev_ap, pos, ETH_ALEN);
154062306a36Sopenharmony_ci		pos++; pos++; pos++; left -= 6;
154162306a36Sopenharmony_ci	} else
154262306a36Sopenharmony_ci		eth_zero_addr(prev_ap);
154362306a36Sopenharmony_ci
154462306a36Sopenharmony_ci	if (left >= 2) {
154562306a36Sopenharmony_ci		unsigned int ileft;
154662306a36Sopenharmony_ci		unsigned char *u = (unsigned char *) pos;
154762306a36Sopenharmony_ci
154862306a36Sopenharmony_ci		if (*u == WLAN_EID_SSID) {
154962306a36Sopenharmony_ci			u++; left--;
155062306a36Sopenharmony_ci			ileft = *u;
155162306a36Sopenharmony_ci			u++; left--;
155262306a36Sopenharmony_ci
155362306a36Sopenharmony_ci			if (ileft > left || ileft > MAX_SSID_LEN) {
155462306a36Sopenharmony_ci				txt = "SSID overflow";
155562306a36Sopenharmony_ci				resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
155662306a36Sopenharmony_ci				goto fail;
155762306a36Sopenharmony_ci			}
155862306a36Sopenharmony_ci
155962306a36Sopenharmony_ci			if (ileft != strlen(local->essid) ||
156062306a36Sopenharmony_ci			    memcmp(local->essid, u, ileft) != 0) {
156162306a36Sopenharmony_ci				txt = "not our SSID";
156262306a36Sopenharmony_ci				resp = WLAN_STATUS_ASSOC_DENIED_UNSPEC;
156362306a36Sopenharmony_ci				goto fail;
156462306a36Sopenharmony_ci			}
156562306a36Sopenharmony_ci
156662306a36Sopenharmony_ci			u += ileft;
156762306a36Sopenharmony_ci			left -= ileft;
156862306a36Sopenharmony_ci		}
156962306a36Sopenharmony_ci
157062306a36Sopenharmony_ci		if (left >= 2 && *u == WLAN_EID_SUPP_RATES) {
157162306a36Sopenharmony_ci			u++; left--;
157262306a36Sopenharmony_ci			ileft = *u;
157362306a36Sopenharmony_ci			u++; left--;
157462306a36Sopenharmony_ci
157562306a36Sopenharmony_ci			if (ileft > left || ileft == 0 ||
157662306a36Sopenharmony_ci			    ileft > WLAN_SUPP_RATES_MAX) {
157762306a36Sopenharmony_ci				txt = "SUPP_RATES len error";
157862306a36Sopenharmony_ci				resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
157962306a36Sopenharmony_ci				goto fail;
158062306a36Sopenharmony_ci			}
158162306a36Sopenharmony_ci
158262306a36Sopenharmony_ci			memset(sta->supported_rates, 0,
158362306a36Sopenharmony_ci			       sizeof(sta->supported_rates));
158462306a36Sopenharmony_ci			memcpy(sta->supported_rates, u, ileft);
158562306a36Sopenharmony_ci			prism2_check_tx_rates(sta);
158662306a36Sopenharmony_ci
158762306a36Sopenharmony_ci			u += ileft;
158862306a36Sopenharmony_ci			left -= ileft;
158962306a36Sopenharmony_ci		}
159062306a36Sopenharmony_ci
159162306a36Sopenharmony_ci		if (left > 0) {
159262306a36Sopenharmony_ci			PDEBUG(DEBUG_AP, "%s: assoc from %pM"
159362306a36Sopenharmony_ci			       " with extra data (%d bytes) [",
159462306a36Sopenharmony_ci			       dev->name, hdr->addr2, left);
159562306a36Sopenharmony_ci			while (left > 0) {
159662306a36Sopenharmony_ci				PDEBUG2(DEBUG_AP, "<%02x>", *u);
159762306a36Sopenharmony_ci				u++; left--;
159862306a36Sopenharmony_ci			}
159962306a36Sopenharmony_ci			PDEBUG2(DEBUG_AP, "]\n");
160062306a36Sopenharmony_ci		}
160162306a36Sopenharmony_ci	} else {
160262306a36Sopenharmony_ci		txt = "frame underflow";
160362306a36Sopenharmony_ci		resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
160462306a36Sopenharmony_ci		goto fail;
160562306a36Sopenharmony_ci	}
160662306a36Sopenharmony_ci
160762306a36Sopenharmony_ci	/* get a unique AID */
160862306a36Sopenharmony_ci	if (sta->aid > 0)
160962306a36Sopenharmony_ci		txt = "OK, old AID";
161062306a36Sopenharmony_ci	else {
161162306a36Sopenharmony_ci		spin_lock_bh(&local->ap->sta_table_lock);
161262306a36Sopenharmony_ci		for (sta->aid = 1; sta->aid <= MAX_AID_TABLE_SIZE; sta->aid++)
161362306a36Sopenharmony_ci			if (local->ap->sta_aid[sta->aid - 1] == NULL)
161462306a36Sopenharmony_ci				break;
161562306a36Sopenharmony_ci		if (sta->aid > MAX_AID_TABLE_SIZE) {
161662306a36Sopenharmony_ci			sta->aid = 0;
161762306a36Sopenharmony_ci			spin_unlock_bh(&local->ap->sta_table_lock);
161862306a36Sopenharmony_ci			resp = WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA;
161962306a36Sopenharmony_ci			txt = "no room for more AIDs";
162062306a36Sopenharmony_ci		} else {
162162306a36Sopenharmony_ci			local->ap->sta_aid[sta->aid - 1] = sta;
162262306a36Sopenharmony_ci			spin_unlock_bh(&local->ap->sta_table_lock);
162362306a36Sopenharmony_ci			txt = "OK, new AID";
162462306a36Sopenharmony_ci		}
162562306a36Sopenharmony_ci	}
162662306a36Sopenharmony_ci
162762306a36Sopenharmony_ci fail:
162862306a36Sopenharmony_ci	pos = (__le16 *) body;
162962306a36Sopenharmony_ci
163062306a36Sopenharmony_ci	if (send_deauth) {
163162306a36Sopenharmony_ci		*pos = cpu_to_le16(WLAN_REASON_STA_REQ_ASSOC_WITHOUT_AUTH);
163262306a36Sopenharmony_ci		pos++;
163362306a36Sopenharmony_ci	} else {
163462306a36Sopenharmony_ci		/* FIX: CF-Pollable and CF-PollReq should be set to match the
163562306a36Sopenharmony_ci		 * values in beacons/probe responses */
163662306a36Sopenharmony_ci		/* FIX: how about privacy and WEP? */
163762306a36Sopenharmony_ci		/* capability */
163862306a36Sopenharmony_ci		*pos = cpu_to_le16(WLAN_CAPABILITY_ESS);
163962306a36Sopenharmony_ci		pos++;
164062306a36Sopenharmony_ci
164162306a36Sopenharmony_ci		/* status_code */
164262306a36Sopenharmony_ci		*pos = cpu_to_le16(resp);
164362306a36Sopenharmony_ci		pos++;
164462306a36Sopenharmony_ci
164562306a36Sopenharmony_ci		*pos = cpu_to_le16((sta && sta->aid > 0 ? sta->aid : 0) |
164662306a36Sopenharmony_ci				     BIT(14) | BIT(15)); /* AID */
164762306a36Sopenharmony_ci		pos++;
164862306a36Sopenharmony_ci
164962306a36Sopenharmony_ci		/* Supported rates (Information element) */
165062306a36Sopenharmony_ci		p = (char *) pos;
165162306a36Sopenharmony_ci		*p++ = WLAN_EID_SUPP_RATES;
165262306a36Sopenharmony_ci		lpos = p;
165362306a36Sopenharmony_ci		*p++ = 0; /* len */
165462306a36Sopenharmony_ci		if (local->tx_rate_control & WLAN_RATE_1M) {
165562306a36Sopenharmony_ci			*p++ = local->basic_rates & WLAN_RATE_1M ? 0x82 : 0x02;
165662306a36Sopenharmony_ci			(*lpos)++;
165762306a36Sopenharmony_ci		}
165862306a36Sopenharmony_ci		if (local->tx_rate_control & WLAN_RATE_2M) {
165962306a36Sopenharmony_ci			*p++ = local->basic_rates & WLAN_RATE_2M ? 0x84 : 0x04;
166062306a36Sopenharmony_ci			(*lpos)++;
166162306a36Sopenharmony_ci		}
166262306a36Sopenharmony_ci		if (local->tx_rate_control & WLAN_RATE_5M5) {
166362306a36Sopenharmony_ci			*p++ = local->basic_rates & WLAN_RATE_5M5 ?
166462306a36Sopenharmony_ci				0x8b : 0x0b;
166562306a36Sopenharmony_ci			(*lpos)++;
166662306a36Sopenharmony_ci		}
166762306a36Sopenharmony_ci		if (local->tx_rate_control & WLAN_RATE_11M) {
166862306a36Sopenharmony_ci			*p++ = local->basic_rates & WLAN_RATE_11M ?
166962306a36Sopenharmony_ci				0x96 : 0x16;
167062306a36Sopenharmony_ci			(*lpos)++;
167162306a36Sopenharmony_ci		}
167262306a36Sopenharmony_ci		pos = (__le16 *) p;
167362306a36Sopenharmony_ci	}
167462306a36Sopenharmony_ci
167562306a36Sopenharmony_ci	prism2_send_mgmt(dev, IEEE80211_FTYPE_MGMT |
167662306a36Sopenharmony_ci			 (send_deauth ? IEEE80211_STYPE_DEAUTH :
167762306a36Sopenharmony_ci			  (reassoc ? IEEE80211_STYPE_REASSOC_RESP :
167862306a36Sopenharmony_ci			   IEEE80211_STYPE_ASSOC_RESP)),
167962306a36Sopenharmony_ci			 body, (u8 *) pos - (u8 *) body,
168062306a36Sopenharmony_ci			 hdr->addr2,
168162306a36Sopenharmony_ci			 send_deauth ? 0 : local->ap->tx_callback_assoc);
168262306a36Sopenharmony_ci
168362306a36Sopenharmony_ci	if (sta) {
168462306a36Sopenharmony_ci		if (resp == WLAN_STATUS_SUCCESS) {
168562306a36Sopenharmony_ci			sta->last_rx = jiffies;
168662306a36Sopenharmony_ci			/* STA will be marked associated from TX callback, if
168762306a36Sopenharmony_ci			 * AssocResp is ACKed */
168862306a36Sopenharmony_ci		}
168962306a36Sopenharmony_ci		atomic_dec(&sta->users);
169062306a36Sopenharmony_ci	}
169162306a36Sopenharmony_ci
169262306a36Sopenharmony_ci#if 0
169362306a36Sopenharmony_ci	PDEBUG(DEBUG_AP, "%s: %pM %sassoc (len=%d "
169462306a36Sopenharmony_ci	       "prev_ap=%pM) => %d(%d) (%s)\n",
169562306a36Sopenharmony_ci	       dev->name,
169662306a36Sopenharmony_ci	       hdr->addr2,
169762306a36Sopenharmony_ci	       reassoc ? "re" : "", len,
169862306a36Sopenharmony_ci	       prev_ap,
169962306a36Sopenharmony_ci	       resp, send_deauth, txt);
170062306a36Sopenharmony_ci#endif
170162306a36Sopenharmony_ci}
170262306a36Sopenharmony_ci
170362306a36Sopenharmony_ci
170462306a36Sopenharmony_ci/* Called only as a scheduled task for pending AP frames. */
170562306a36Sopenharmony_cistatic void handle_deauth(local_info_t *local, struct sk_buff *skb,
170662306a36Sopenharmony_ci			  struct hostap_80211_rx_status *rx_stats)
170762306a36Sopenharmony_ci{
170862306a36Sopenharmony_ci	struct net_device *dev = local->dev;
170962306a36Sopenharmony_ci	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
171062306a36Sopenharmony_ci	char *body = (char *) (skb->data + IEEE80211_MGMT_HDR_LEN);
171162306a36Sopenharmony_ci	int len;
171262306a36Sopenharmony_ci	u16 reason_code;
171362306a36Sopenharmony_ci	__le16 *pos;
171462306a36Sopenharmony_ci	struct sta_info *sta = NULL;
171562306a36Sopenharmony_ci
171662306a36Sopenharmony_ci	len = skb->len - IEEE80211_MGMT_HDR_LEN;
171762306a36Sopenharmony_ci
171862306a36Sopenharmony_ci	if (len < 2) {
171962306a36Sopenharmony_ci		printk("handle_deauth - too short payload (len=%d)\n", len);
172062306a36Sopenharmony_ci		return;
172162306a36Sopenharmony_ci	}
172262306a36Sopenharmony_ci
172362306a36Sopenharmony_ci	pos = (__le16 *) body;
172462306a36Sopenharmony_ci	reason_code = le16_to_cpu(*pos);
172562306a36Sopenharmony_ci
172662306a36Sopenharmony_ci	PDEBUG(DEBUG_AP, "%s: deauthentication: %pM len=%d, "
172762306a36Sopenharmony_ci	       "reason_code=%d\n", dev->name, hdr->addr2,
172862306a36Sopenharmony_ci	       len, reason_code);
172962306a36Sopenharmony_ci
173062306a36Sopenharmony_ci	spin_lock_bh(&local->ap->sta_table_lock);
173162306a36Sopenharmony_ci	sta = ap_get_sta(local->ap, hdr->addr2);
173262306a36Sopenharmony_ci	if (sta != NULL) {
173362306a36Sopenharmony_ci		if ((sta->flags & WLAN_STA_ASSOC) && !sta->ap)
173462306a36Sopenharmony_ci			hostap_event_expired_sta(local->dev, sta);
173562306a36Sopenharmony_ci		sta->flags &= ~(WLAN_STA_AUTH | WLAN_STA_ASSOC);
173662306a36Sopenharmony_ci	}
173762306a36Sopenharmony_ci	spin_unlock_bh(&local->ap->sta_table_lock);
173862306a36Sopenharmony_ci	if (sta == NULL) {
173962306a36Sopenharmony_ci		printk("%s: deauthentication from %pM, "
174062306a36Sopenharmony_ci	       "reason_code=%d, but STA not authenticated\n", dev->name,
174162306a36Sopenharmony_ci		       hdr->addr2, reason_code);
174262306a36Sopenharmony_ci	}
174362306a36Sopenharmony_ci}
174462306a36Sopenharmony_ci
174562306a36Sopenharmony_ci
174662306a36Sopenharmony_ci/* Called only as a scheduled task for pending AP frames. */
174762306a36Sopenharmony_cistatic void handle_disassoc(local_info_t *local, struct sk_buff *skb,
174862306a36Sopenharmony_ci			    struct hostap_80211_rx_status *rx_stats)
174962306a36Sopenharmony_ci{
175062306a36Sopenharmony_ci	struct net_device *dev = local->dev;
175162306a36Sopenharmony_ci	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
175262306a36Sopenharmony_ci	char *body = skb->data + IEEE80211_MGMT_HDR_LEN;
175362306a36Sopenharmony_ci	int len;
175462306a36Sopenharmony_ci	u16 reason_code;
175562306a36Sopenharmony_ci	__le16 *pos;
175662306a36Sopenharmony_ci	struct sta_info *sta = NULL;
175762306a36Sopenharmony_ci
175862306a36Sopenharmony_ci	len = skb->len - IEEE80211_MGMT_HDR_LEN;
175962306a36Sopenharmony_ci
176062306a36Sopenharmony_ci	if (len < 2) {
176162306a36Sopenharmony_ci		printk("handle_disassoc - too short payload (len=%d)\n", len);
176262306a36Sopenharmony_ci		return;
176362306a36Sopenharmony_ci	}
176462306a36Sopenharmony_ci
176562306a36Sopenharmony_ci	pos = (__le16 *) body;
176662306a36Sopenharmony_ci	reason_code = le16_to_cpu(*pos);
176762306a36Sopenharmony_ci
176862306a36Sopenharmony_ci	PDEBUG(DEBUG_AP, "%s: disassociation: %pM len=%d, "
176962306a36Sopenharmony_ci	       "reason_code=%d\n", dev->name, hdr->addr2,
177062306a36Sopenharmony_ci	       len, reason_code);
177162306a36Sopenharmony_ci
177262306a36Sopenharmony_ci	spin_lock_bh(&local->ap->sta_table_lock);
177362306a36Sopenharmony_ci	sta = ap_get_sta(local->ap, hdr->addr2);
177462306a36Sopenharmony_ci	if (sta != NULL) {
177562306a36Sopenharmony_ci		if ((sta->flags & WLAN_STA_ASSOC) && !sta->ap)
177662306a36Sopenharmony_ci			hostap_event_expired_sta(local->dev, sta);
177762306a36Sopenharmony_ci		sta->flags &= ~WLAN_STA_ASSOC;
177862306a36Sopenharmony_ci	}
177962306a36Sopenharmony_ci	spin_unlock_bh(&local->ap->sta_table_lock);
178062306a36Sopenharmony_ci	if (sta == NULL) {
178162306a36Sopenharmony_ci		printk("%s: disassociation from %pM, "
178262306a36Sopenharmony_ci		       "reason_code=%d, but STA not authenticated\n",
178362306a36Sopenharmony_ci		       dev->name, hdr->addr2, reason_code);
178462306a36Sopenharmony_ci	}
178562306a36Sopenharmony_ci}
178662306a36Sopenharmony_ci
178762306a36Sopenharmony_ci
178862306a36Sopenharmony_ci/* Called only as a scheduled task for pending AP frames. */
178962306a36Sopenharmony_cistatic void ap_handle_data_nullfunc(local_info_t *local,
179062306a36Sopenharmony_ci				    struct ieee80211_hdr *hdr)
179162306a36Sopenharmony_ci{
179262306a36Sopenharmony_ci	struct net_device *dev = local->dev;
179362306a36Sopenharmony_ci
179462306a36Sopenharmony_ci	/* some STA f/w's seem to require control::ACK frame for
179562306a36Sopenharmony_ci	 * data::nullfunc, but at least Prism2 station f/w version 0.8.0 does
179662306a36Sopenharmony_ci	 * not send this..
179762306a36Sopenharmony_ci	 * send control::ACK for the data::nullfunc */
179862306a36Sopenharmony_ci
179962306a36Sopenharmony_ci	printk(KERN_DEBUG "Sending control::ACK for data::nullfunc\n");
180062306a36Sopenharmony_ci	prism2_send_mgmt(dev, IEEE80211_FTYPE_CTL | IEEE80211_STYPE_ACK,
180162306a36Sopenharmony_ci			 NULL, 0, hdr->addr2, 0);
180262306a36Sopenharmony_ci}
180362306a36Sopenharmony_ci
180462306a36Sopenharmony_ci
180562306a36Sopenharmony_ci/* Called only as a scheduled task for pending AP frames. */
180662306a36Sopenharmony_cistatic void ap_handle_dropped_data(local_info_t *local,
180762306a36Sopenharmony_ci				   struct ieee80211_hdr *hdr)
180862306a36Sopenharmony_ci{
180962306a36Sopenharmony_ci	struct net_device *dev = local->dev;
181062306a36Sopenharmony_ci	struct sta_info *sta;
181162306a36Sopenharmony_ci	__le16 reason;
181262306a36Sopenharmony_ci
181362306a36Sopenharmony_ci	spin_lock_bh(&local->ap->sta_table_lock);
181462306a36Sopenharmony_ci	sta = ap_get_sta(local->ap, hdr->addr2);
181562306a36Sopenharmony_ci	if (sta)
181662306a36Sopenharmony_ci		atomic_inc(&sta->users);
181762306a36Sopenharmony_ci	spin_unlock_bh(&local->ap->sta_table_lock);
181862306a36Sopenharmony_ci
181962306a36Sopenharmony_ci	if (sta != NULL && (sta->flags & WLAN_STA_ASSOC)) {
182062306a36Sopenharmony_ci		PDEBUG(DEBUG_AP, "ap_handle_dropped_data: STA is now okay?\n");
182162306a36Sopenharmony_ci		atomic_dec(&sta->users);
182262306a36Sopenharmony_ci		return;
182362306a36Sopenharmony_ci	}
182462306a36Sopenharmony_ci
182562306a36Sopenharmony_ci	reason = cpu_to_le16(WLAN_REASON_CLASS3_FRAME_FROM_NONASSOC_STA);
182662306a36Sopenharmony_ci	prism2_send_mgmt(dev, IEEE80211_FTYPE_MGMT |
182762306a36Sopenharmony_ci			 ((sta == NULL || !(sta->flags & WLAN_STA_ASSOC)) ?
182862306a36Sopenharmony_ci			  IEEE80211_STYPE_DEAUTH : IEEE80211_STYPE_DISASSOC),
182962306a36Sopenharmony_ci			 (char *) &reason, sizeof(reason), hdr->addr2, 0);
183062306a36Sopenharmony_ci
183162306a36Sopenharmony_ci	if (sta)
183262306a36Sopenharmony_ci		atomic_dec(&sta->users);
183362306a36Sopenharmony_ci}
183462306a36Sopenharmony_ci
183562306a36Sopenharmony_ci#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */
183662306a36Sopenharmony_ci
183762306a36Sopenharmony_ci
183862306a36Sopenharmony_ci/* Called only as a scheduled task for pending AP frames. */
183962306a36Sopenharmony_cistatic void pspoll_send_buffered(local_info_t *local, struct sta_info *sta,
184062306a36Sopenharmony_ci				 struct sk_buff *skb)
184162306a36Sopenharmony_ci{
184262306a36Sopenharmony_ci	struct hostap_skb_tx_data *meta;
184362306a36Sopenharmony_ci
184462306a36Sopenharmony_ci	if (!(sta->flags & WLAN_STA_PS)) {
184562306a36Sopenharmony_ci		/* Station has moved to non-PS mode, so send all buffered
184662306a36Sopenharmony_ci		 * frames using normal device queue. */
184762306a36Sopenharmony_ci		dev_queue_xmit(skb);
184862306a36Sopenharmony_ci		return;
184962306a36Sopenharmony_ci	}
185062306a36Sopenharmony_ci
185162306a36Sopenharmony_ci	/* add a flag for hostap_handle_sta_tx() to know that this skb should
185262306a36Sopenharmony_ci	 * be passed through even though STA is using PS */
185362306a36Sopenharmony_ci	meta = (struct hostap_skb_tx_data *) skb->cb;
185462306a36Sopenharmony_ci	meta->flags |= HOSTAP_TX_FLAGS_BUFFERED_FRAME;
185562306a36Sopenharmony_ci	if (!skb_queue_empty(&sta->tx_buf)) {
185662306a36Sopenharmony_ci		/* indicate to STA that more frames follow */
185762306a36Sopenharmony_ci		meta->flags |= HOSTAP_TX_FLAGS_ADD_MOREDATA;
185862306a36Sopenharmony_ci	}
185962306a36Sopenharmony_ci	dev_queue_xmit(skb);
186062306a36Sopenharmony_ci}
186162306a36Sopenharmony_ci
186262306a36Sopenharmony_ci
186362306a36Sopenharmony_ci/* Called only as a scheduled task for pending AP frames. */
186462306a36Sopenharmony_cistatic void handle_pspoll(local_info_t *local,
186562306a36Sopenharmony_ci			  struct ieee80211_hdr *hdr,
186662306a36Sopenharmony_ci			  struct hostap_80211_rx_status *rx_stats)
186762306a36Sopenharmony_ci{
186862306a36Sopenharmony_ci	struct net_device *dev = local->dev;
186962306a36Sopenharmony_ci	struct sta_info *sta;
187062306a36Sopenharmony_ci	u16 aid;
187162306a36Sopenharmony_ci	struct sk_buff *skb;
187262306a36Sopenharmony_ci
187362306a36Sopenharmony_ci	PDEBUG(DEBUG_PS2, "handle_pspoll: BSSID=%pM, TA=%pM PWRMGT=%d\n",
187462306a36Sopenharmony_ci	       hdr->addr1, hdr->addr2, !!ieee80211_has_pm(hdr->frame_control));
187562306a36Sopenharmony_ci
187662306a36Sopenharmony_ci	if (!ether_addr_equal(hdr->addr1, dev->dev_addr)) {
187762306a36Sopenharmony_ci		PDEBUG(DEBUG_AP,
187862306a36Sopenharmony_ci		       "handle_pspoll - addr1(BSSID)=%pM not own MAC\n",
187962306a36Sopenharmony_ci		       hdr->addr1);
188062306a36Sopenharmony_ci		return;
188162306a36Sopenharmony_ci	}
188262306a36Sopenharmony_ci
188362306a36Sopenharmony_ci	aid = le16_to_cpu(hdr->duration_id);
188462306a36Sopenharmony_ci	if ((aid & (BIT(15) | BIT(14))) != (BIT(15) | BIT(14))) {
188562306a36Sopenharmony_ci		PDEBUG(DEBUG_PS, "   PSPOLL and AID[15:14] not set\n");
188662306a36Sopenharmony_ci		return;
188762306a36Sopenharmony_ci	}
188862306a36Sopenharmony_ci	aid &= ~(BIT(15) | BIT(14));
188962306a36Sopenharmony_ci	if (aid == 0 || aid > MAX_AID_TABLE_SIZE) {
189062306a36Sopenharmony_ci		PDEBUG(DEBUG_PS, "   invalid aid=%d\n", aid);
189162306a36Sopenharmony_ci		return;
189262306a36Sopenharmony_ci	}
189362306a36Sopenharmony_ci	PDEBUG(DEBUG_PS2, "   aid=%d\n", aid);
189462306a36Sopenharmony_ci
189562306a36Sopenharmony_ci	spin_lock_bh(&local->ap->sta_table_lock);
189662306a36Sopenharmony_ci	sta = ap_get_sta(local->ap, hdr->addr2);
189762306a36Sopenharmony_ci	if (sta)
189862306a36Sopenharmony_ci		atomic_inc(&sta->users);
189962306a36Sopenharmony_ci	spin_unlock_bh(&local->ap->sta_table_lock);
190062306a36Sopenharmony_ci
190162306a36Sopenharmony_ci	if (sta == NULL) {
190262306a36Sopenharmony_ci		PDEBUG(DEBUG_PS, "   STA not found\n");
190362306a36Sopenharmony_ci		return;
190462306a36Sopenharmony_ci	}
190562306a36Sopenharmony_ci	if (sta->aid != aid) {
190662306a36Sopenharmony_ci		PDEBUG(DEBUG_PS, "   received aid=%i does not match with "
190762306a36Sopenharmony_ci		       "assoc.aid=%d\n", aid, sta->aid);
190862306a36Sopenharmony_ci		return;
190962306a36Sopenharmony_ci	}
191062306a36Sopenharmony_ci
191162306a36Sopenharmony_ci	/* FIX: todo:
191262306a36Sopenharmony_ci	 * - add timeout for buffering (clear aid in TIM vector if buffer timed
191362306a36Sopenharmony_ci	 *   out (expiry time must be longer than ListenInterval for
191462306a36Sopenharmony_ci	 *   the corresponding STA; "8802-11: 11.2.1.9 AP aging function"
191562306a36Sopenharmony_ci	 * - what to do, if buffered, pspolled, and sent frame is not ACKed by
191662306a36Sopenharmony_ci	 *   sta; store buffer for later use and leave TIM aid bit set? use
191762306a36Sopenharmony_ci	 *   TX event to check whether frame was ACKed?
191862306a36Sopenharmony_ci	 */
191962306a36Sopenharmony_ci
192062306a36Sopenharmony_ci	while ((skb = skb_dequeue(&sta->tx_buf)) != NULL) {
192162306a36Sopenharmony_ci		/* send buffered frame .. */
192262306a36Sopenharmony_ci		PDEBUG(DEBUG_PS2, "Sending buffered frame to STA after PS POLL"
192362306a36Sopenharmony_ci		       " (buffer_count=%d)\n", skb_queue_len(&sta->tx_buf));
192462306a36Sopenharmony_ci
192562306a36Sopenharmony_ci		pspoll_send_buffered(local, sta, skb);
192662306a36Sopenharmony_ci
192762306a36Sopenharmony_ci		if (sta->flags & WLAN_STA_PS) {
192862306a36Sopenharmony_ci			/* send only one buffered packet per PS Poll */
192962306a36Sopenharmony_ci			/* FIX: should ignore further PS Polls until the
193062306a36Sopenharmony_ci			 * buffered packet that was just sent is acknowledged
193162306a36Sopenharmony_ci			 * (Tx or TxExc event) */
193262306a36Sopenharmony_ci			break;
193362306a36Sopenharmony_ci		}
193462306a36Sopenharmony_ci	}
193562306a36Sopenharmony_ci
193662306a36Sopenharmony_ci	if (skb_queue_empty(&sta->tx_buf)) {
193762306a36Sopenharmony_ci		/* try to clear aid from TIM */
193862306a36Sopenharmony_ci		if (!(sta->flags & WLAN_STA_TIM))
193962306a36Sopenharmony_ci			PDEBUG(DEBUG_PS2,  "Re-unsetting TIM for aid %d\n",
194062306a36Sopenharmony_ci			       aid);
194162306a36Sopenharmony_ci		hostap_set_tim(local, aid, 0);
194262306a36Sopenharmony_ci		sta->flags &= ~WLAN_STA_TIM;
194362306a36Sopenharmony_ci	}
194462306a36Sopenharmony_ci
194562306a36Sopenharmony_ci	atomic_dec(&sta->users);
194662306a36Sopenharmony_ci}
194762306a36Sopenharmony_ci
194862306a36Sopenharmony_ci
194962306a36Sopenharmony_ci#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT
195062306a36Sopenharmony_ci
195162306a36Sopenharmony_cistatic void handle_wds_oper_queue(struct work_struct *work)
195262306a36Sopenharmony_ci{
195362306a36Sopenharmony_ci	struct ap_data *ap = container_of(work, struct ap_data,
195462306a36Sopenharmony_ci					  wds_oper_queue);
195562306a36Sopenharmony_ci	local_info_t *local = ap->local;
195662306a36Sopenharmony_ci	struct wds_oper_data *entry, *prev;
195762306a36Sopenharmony_ci
195862306a36Sopenharmony_ci	spin_lock_bh(&local->lock);
195962306a36Sopenharmony_ci	entry = local->ap->wds_oper_entries;
196062306a36Sopenharmony_ci	local->ap->wds_oper_entries = NULL;
196162306a36Sopenharmony_ci	spin_unlock_bh(&local->lock);
196262306a36Sopenharmony_ci
196362306a36Sopenharmony_ci	while (entry) {
196462306a36Sopenharmony_ci		PDEBUG(DEBUG_AP, "%s: %s automatic WDS connection "
196562306a36Sopenharmony_ci		       "to AP %pM\n",
196662306a36Sopenharmony_ci		       local->dev->name,
196762306a36Sopenharmony_ci		       entry->type == WDS_ADD ? "adding" : "removing",
196862306a36Sopenharmony_ci		       entry->addr);
196962306a36Sopenharmony_ci		if (entry->type == WDS_ADD)
197062306a36Sopenharmony_ci			prism2_wds_add(local, entry->addr, 0);
197162306a36Sopenharmony_ci		else if (entry->type == WDS_DEL)
197262306a36Sopenharmony_ci			prism2_wds_del(local, entry->addr, 0, 1);
197362306a36Sopenharmony_ci
197462306a36Sopenharmony_ci		prev = entry;
197562306a36Sopenharmony_ci		entry = entry->next;
197662306a36Sopenharmony_ci		kfree(prev);
197762306a36Sopenharmony_ci	}
197862306a36Sopenharmony_ci}
197962306a36Sopenharmony_ci
198062306a36Sopenharmony_ci
198162306a36Sopenharmony_ci/* Called only as a scheduled task for pending AP frames. */
198262306a36Sopenharmony_cistatic void handle_beacon(local_info_t *local, struct sk_buff *skb,
198362306a36Sopenharmony_ci			  struct hostap_80211_rx_status *rx_stats)
198462306a36Sopenharmony_ci{
198562306a36Sopenharmony_ci	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
198662306a36Sopenharmony_ci	char *body = skb->data + IEEE80211_MGMT_HDR_LEN;
198762306a36Sopenharmony_ci	int len, left;
198862306a36Sopenharmony_ci	u16 beacon_int, capability;
198962306a36Sopenharmony_ci	__le16 *pos;
199062306a36Sopenharmony_ci	char *ssid = NULL;
199162306a36Sopenharmony_ci	unsigned char *supp_rates = NULL;
199262306a36Sopenharmony_ci	int ssid_len = 0, supp_rates_len = 0;
199362306a36Sopenharmony_ci	struct sta_info *sta = NULL;
199462306a36Sopenharmony_ci	int new_sta = 0, channel = -1;
199562306a36Sopenharmony_ci
199662306a36Sopenharmony_ci	len = skb->len - IEEE80211_MGMT_HDR_LEN;
199762306a36Sopenharmony_ci
199862306a36Sopenharmony_ci	if (len < 8 + 2 + 2) {
199962306a36Sopenharmony_ci		printk(KERN_DEBUG "handle_beacon - too short payload "
200062306a36Sopenharmony_ci		       "(len=%d)\n", len);
200162306a36Sopenharmony_ci		return;
200262306a36Sopenharmony_ci	}
200362306a36Sopenharmony_ci
200462306a36Sopenharmony_ci	pos = (__le16 *) body;
200562306a36Sopenharmony_ci	left = len;
200662306a36Sopenharmony_ci
200762306a36Sopenharmony_ci	/* Timestamp (8 octets) */
200862306a36Sopenharmony_ci	pos += 4; left -= 8;
200962306a36Sopenharmony_ci	/* Beacon interval (2 octets) */
201062306a36Sopenharmony_ci	beacon_int = le16_to_cpu(*pos);
201162306a36Sopenharmony_ci	pos++; left -= 2;
201262306a36Sopenharmony_ci	/* Capability information (2 octets) */
201362306a36Sopenharmony_ci	capability = le16_to_cpu(*pos);
201462306a36Sopenharmony_ci	pos++; left -= 2;
201562306a36Sopenharmony_ci
201662306a36Sopenharmony_ci	if (local->ap->ap_policy != AP_OTHER_AP_EVEN_IBSS &&
201762306a36Sopenharmony_ci	    capability & WLAN_CAPABILITY_IBSS)
201862306a36Sopenharmony_ci		return;
201962306a36Sopenharmony_ci
202062306a36Sopenharmony_ci	if (left >= 2) {
202162306a36Sopenharmony_ci		unsigned int ileft;
202262306a36Sopenharmony_ci		unsigned char *u = (unsigned char *) pos;
202362306a36Sopenharmony_ci
202462306a36Sopenharmony_ci		if (*u == WLAN_EID_SSID) {
202562306a36Sopenharmony_ci			u++; left--;
202662306a36Sopenharmony_ci			ileft = *u;
202762306a36Sopenharmony_ci			u++; left--;
202862306a36Sopenharmony_ci
202962306a36Sopenharmony_ci			if (ileft > left || ileft > MAX_SSID_LEN) {
203062306a36Sopenharmony_ci				PDEBUG(DEBUG_AP, "SSID: overflow\n");
203162306a36Sopenharmony_ci				return;
203262306a36Sopenharmony_ci			}
203362306a36Sopenharmony_ci
203462306a36Sopenharmony_ci			if (local->ap->ap_policy == AP_OTHER_AP_SAME_SSID &&
203562306a36Sopenharmony_ci			    (ileft != strlen(local->essid) ||
203662306a36Sopenharmony_ci			     memcmp(local->essid, u, ileft) != 0)) {
203762306a36Sopenharmony_ci				/* not our SSID */
203862306a36Sopenharmony_ci				return;
203962306a36Sopenharmony_ci			}
204062306a36Sopenharmony_ci
204162306a36Sopenharmony_ci			ssid = u;
204262306a36Sopenharmony_ci			ssid_len = ileft;
204362306a36Sopenharmony_ci
204462306a36Sopenharmony_ci			u += ileft;
204562306a36Sopenharmony_ci			left -= ileft;
204662306a36Sopenharmony_ci		}
204762306a36Sopenharmony_ci
204862306a36Sopenharmony_ci		if (*u == WLAN_EID_SUPP_RATES) {
204962306a36Sopenharmony_ci			u++; left--;
205062306a36Sopenharmony_ci			ileft = *u;
205162306a36Sopenharmony_ci			u++; left--;
205262306a36Sopenharmony_ci
205362306a36Sopenharmony_ci			if (ileft > left || ileft == 0 || ileft > 8) {
205462306a36Sopenharmony_ci				PDEBUG(DEBUG_AP, " - SUPP_RATES len error\n");
205562306a36Sopenharmony_ci				return;
205662306a36Sopenharmony_ci			}
205762306a36Sopenharmony_ci
205862306a36Sopenharmony_ci			supp_rates = u;
205962306a36Sopenharmony_ci			supp_rates_len = ileft;
206062306a36Sopenharmony_ci
206162306a36Sopenharmony_ci			u += ileft;
206262306a36Sopenharmony_ci			left -= ileft;
206362306a36Sopenharmony_ci		}
206462306a36Sopenharmony_ci
206562306a36Sopenharmony_ci		if (*u == WLAN_EID_DS_PARAMS) {
206662306a36Sopenharmony_ci			u++; left--;
206762306a36Sopenharmony_ci			ileft = *u;
206862306a36Sopenharmony_ci			u++; left--;
206962306a36Sopenharmony_ci
207062306a36Sopenharmony_ci			if (ileft > left || ileft != 1) {
207162306a36Sopenharmony_ci				PDEBUG(DEBUG_AP, " - DS_PARAMS len error\n");
207262306a36Sopenharmony_ci				return;
207362306a36Sopenharmony_ci			}
207462306a36Sopenharmony_ci
207562306a36Sopenharmony_ci			channel = *u;
207662306a36Sopenharmony_ci
207762306a36Sopenharmony_ci			u += ileft;
207862306a36Sopenharmony_ci			left -= ileft;
207962306a36Sopenharmony_ci		}
208062306a36Sopenharmony_ci	}
208162306a36Sopenharmony_ci
208262306a36Sopenharmony_ci	spin_lock_bh(&local->ap->sta_table_lock);
208362306a36Sopenharmony_ci	sta = ap_get_sta(local->ap, hdr->addr2);
208462306a36Sopenharmony_ci	if (sta != NULL)
208562306a36Sopenharmony_ci		atomic_inc(&sta->users);
208662306a36Sopenharmony_ci	spin_unlock_bh(&local->ap->sta_table_lock);
208762306a36Sopenharmony_ci
208862306a36Sopenharmony_ci	if (sta == NULL) {
208962306a36Sopenharmony_ci		/* add new AP */
209062306a36Sopenharmony_ci		new_sta = 1;
209162306a36Sopenharmony_ci		sta = ap_add_sta(local->ap, hdr->addr2);
209262306a36Sopenharmony_ci		if (sta == NULL) {
209362306a36Sopenharmony_ci			printk(KERN_INFO "prism2: kmalloc failed for AP "
209462306a36Sopenharmony_ci			       "data structure\n");
209562306a36Sopenharmony_ci			return;
209662306a36Sopenharmony_ci		}
209762306a36Sopenharmony_ci		hostap_event_new_sta(local->dev, sta);
209862306a36Sopenharmony_ci
209962306a36Sopenharmony_ci		/* mark APs authentication and associated for pseudo ad-hoc
210062306a36Sopenharmony_ci		 * style communication */
210162306a36Sopenharmony_ci		sta->flags = WLAN_STA_AUTH | WLAN_STA_ASSOC;
210262306a36Sopenharmony_ci
210362306a36Sopenharmony_ci		if (local->ap->autom_ap_wds) {
210462306a36Sopenharmony_ci			hostap_wds_link_oper(local, sta->addr, WDS_ADD);
210562306a36Sopenharmony_ci		}
210662306a36Sopenharmony_ci	}
210762306a36Sopenharmony_ci
210862306a36Sopenharmony_ci	sta->ap = 1;
210962306a36Sopenharmony_ci	if (ssid) {
211062306a36Sopenharmony_ci		sta->u.ap.ssid_len = ssid_len;
211162306a36Sopenharmony_ci		memcpy(sta->u.ap.ssid, ssid, ssid_len);
211262306a36Sopenharmony_ci		sta->u.ap.ssid[ssid_len] = '\0';
211362306a36Sopenharmony_ci	} else {
211462306a36Sopenharmony_ci		sta->u.ap.ssid_len = 0;
211562306a36Sopenharmony_ci		sta->u.ap.ssid[0] = '\0';
211662306a36Sopenharmony_ci	}
211762306a36Sopenharmony_ci	sta->u.ap.channel = channel;
211862306a36Sopenharmony_ci	sta->rx_packets++;
211962306a36Sopenharmony_ci	sta->rx_bytes += len;
212062306a36Sopenharmony_ci	sta->u.ap.last_beacon = sta->last_rx = jiffies;
212162306a36Sopenharmony_ci	sta->capability = capability;
212262306a36Sopenharmony_ci	sta->listen_interval = beacon_int;
212362306a36Sopenharmony_ci
212462306a36Sopenharmony_ci	atomic_dec(&sta->users);
212562306a36Sopenharmony_ci
212662306a36Sopenharmony_ci	if (new_sta) {
212762306a36Sopenharmony_ci		memset(sta->supported_rates, 0, sizeof(sta->supported_rates));
212862306a36Sopenharmony_ci		memcpy(sta->supported_rates, supp_rates, supp_rates_len);
212962306a36Sopenharmony_ci		prism2_check_tx_rates(sta);
213062306a36Sopenharmony_ci	}
213162306a36Sopenharmony_ci}
213262306a36Sopenharmony_ci
213362306a36Sopenharmony_ci#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */
213462306a36Sopenharmony_ci
213562306a36Sopenharmony_ci
213662306a36Sopenharmony_ci/* Called only as a tasklet. */
213762306a36Sopenharmony_cistatic void handle_ap_item(local_info_t *local, struct sk_buff *skb,
213862306a36Sopenharmony_ci			   struct hostap_80211_rx_status *rx_stats)
213962306a36Sopenharmony_ci{
214062306a36Sopenharmony_ci#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT
214162306a36Sopenharmony_ci	struct net_device *dev = local->dev;
214262306a36Sopenharmony_ci#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */
214362306a36Sopenharmony_ci	u16 fc, type, stype;
214462306a36Sopenharmony_ci	struct ieee80211_hdr *hdr;
214562306a36Sopenharmony_ci
214662306a36Sopenharmony_ci	/* FIX: should give skb->len to handler functions and check that the
214762306a36Sopenharmony_ci	 * buffer is long enough */
214862306a36Sopenharmony_ci	hdr = (struct ieee80211_hdr *) skb->data;
214962306a36Sopenharmony_ci	fc = le16_to_cpu(hdr->frame_control);
215062306a36Sopenharmony_ci	type = fc & IEEE80211_FCTL_FTYPE;
215162306a36Sopenharmony_ci	stype = fc & IEEE80211_FCTL_STYPE;
215262306a36Sopenharmony_ci
215362306a36Sopenharmony_ci#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT
215462306a36Sopenharmony_ci	if (!local->hostapd && type == IEEE80211_FTYPE_DATA) {
215562306a36Sopenharmony_ci		PDEBUG(DEBUG_AP, "handle_ap_item - data frame\n");
215662306a36Sopenharmony_ci
215762306a36Sopenharmony_ci		if (!(fc & IEEE80211_FCTL_TODS) ||
215862306a36Sopenharmony_ci		    (fc & IEEE80211_FCTL_FROMDS)) {
215962306a36Sopenharmony_ci			if (stype == IEEE80211_STYPE_NULLFUNC) {
216062306a36Sopenharmony_ci				/* no ToDS nullfunc seems to be used to check
216162306a36Sopenharmony_ci				 * AP association; so send reject message to
216262306a36Sopenharmony_ci				 * speed up re-association */
216362306a36Sopenharmony_ci				ap_handle_dropped_data(local, hdr);
216462306a36Sopenharmony_ci				goto done;
216562306a36Sopenharmony_ci			}
216662306a36Sopenharmony_ci			PDEBUG(DEBUG_AP, "   not ToDS frame (fc=0x%04x)\n",
216762306a36Sopenharmony_ci			       fc);
216862306a36Sopenharmony_ci			goto done;
216962306a36Sopenharmony_ci		}
217062306a36Sopenharmony_ci
217162306a36Sopenharmony_ci		if (!ether_addr_equal(hdr->addr1, dev->dev_addr)) {
217262306a36Sopenharmony_ci			PDEBUG(DEBUG_AP, "handle_ap_item - addr1(BSSID)=%pM"
217362306a36Sopenharmony_ci			       " not own MAC\n", hdr->addr1);
217462306a36Sopenharmony_ci			goto done;
217562306a36Sopenharmony_ci		}
217662306a36Sopenharmony_ci
217762306a36Sopenharmony_ci		if (local->ap->nullfunc_ack &&
217862306a36Sopenharmony_ci		    stype == IEEE80211_STYPE_NULLFUNC)
217962306a36Sopenharmony_ci			ap_handle_data_nullfunc(local, hdr);
218062306a36Sopenharmony_ci		else
218162306a36Sopenharmony_ci			ap_handle_dropped_data(local, hdr);
218262306a36Sopenharmony_ci		goto done;
218362306a36Sopenharmony_ci	}
218462306a36Sopenharmony_ci
218562306a36Sopenharmony_ci	if (type == IEEE80211_FTYPE_MGMT && stype == IEEE80211_STYPE_BEACON) {
218662306a36Sopenharmony_ci		handle_beacon(local, skb, rx_stats);
218762306a36Sopenharmony_ci		goto done;
218862306a36Sopenharmony_ci	}
218962306a36Sopenharmony_ci#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */
219062306a36Sopenharmony_ci
219162306a36Sopenharmony_ci	if (type == IEEE80211_FTYPE_CTL && stype == IEEE80211_STYPE_PSPOLL) {
219262306a36Sopenharmony_ci		handle_pspoll(local, hdr, rx_stats);
219362306a36Sopenharmony_ci		goto done;
219462306a36Sopenharmony_ci	}
219562306a36Sopenharmony_ci
219662306a36Sopenharmony_ci	if (local->hostapd) {
219762306a36Sopenharmony_ci		PDEBUG(DEBUG_AP, "Unknown frame in AP queue: type=0x%02x "
219862306a36Sopenharmony_ci		       "subtype=0x%02x\n", type, stype);
219962306a36Sopenharmony_ci		goto done;
220062306a36Sopenharmony_ci	}
220162306a36Sopenharmony_ci
220262306a36Sopenharmony_ci#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT
220362306a36Sopenharmony_ci	if (type != IEEE80211_FTYPE_MGMT) {
220462306a36Sopenharmony_ci		PDEBUG(DEBUG_AP, "handle_ap_item - not a management frame?\n");
220562306a36Sopenharmony_ci		goto done;
220662306a36Sopenharmony_ci	}
220762306a36Sopenharmony_ci
220862306a36Sopenharmony_ci	if (!ether_addr_equal(hdr->addr1, dev->dev_addr)) {
220962306a36Sopenharmony_ci		PDEBUG(DEBUG_AP, "handle_ap_item - addr1(DA)=%pM"
221062306a36Sopenharmony_ci		       " not own MAC\n", hdr->addr1);
221162306a36Sopenharmony_ci		goto done;
221262306a36Sopenharmony_ci	}
221362306a36Sopenharmony_ci
221462306a36Sopenharmony_ci	if (!ether_addr_equal(hdr->addr3, dev->dev_addr)) {
221562306a36Sopenharmony_ci		PDEBUG(DEBUG_AP, "handle_ap_item - addr3(BSSID)=%pM"
221662306a36Sopenharmony_ci		       " not own MAC\n", hdr->addr3);
221762306a36Sopenharmony_ci		goto done;
221862306a36Sopenharmony_ci	}
221962306a36Sopenharmony_ci
222062306a36Sopenharmony_ci	switch (stype) {
222162306a36Sopenharmony_ci	case IEEE80211_STYPE_ASSOC_REQ:
222262306a36Sopenharmony_ci		handle_assoc(local, skb, rx_stats, 0);
222362306a36Sopenharmony_ci		break;
222462306a36Sopenharmony_ci	case IEEE80211_STYPE_ASSOC_RESP:
222562306a36Sopenharmony_ci		PDEBUG(DEBUG_AP, "==> ASSOC RESP (ignored)\n");
222662306a36Sopenharmony_ci		break;
222762306a36Sopenharmony_ci	case IEEE80211_STYPE_REASSOC_REQ:
222862306a36Sopenharmony_ci		handle_assoc(local, skb, rx_stats, 1);
222962306a36Sopenharmony_ci		break;
223062306a36Sopenharmony_ci	case IEEE80211_STYPE_REASSOC_RESP:
223162306a36Sopenharmony_ci		PDEBUG(DEBUG_AP, "==> REASSOC RESP (ignored)\n");
223262306a36Sopenharmony_ci		break;
223362306a36Sopenharmony_ci	case IEEE80211_STYPE_ATIM:
223462306a36Sopenharmony_ci		PDEBUG(DEBUG_AP, "==> ATIM (ignored)\n");
223562306a36Sopenharmony_ci		break;
223662306a36Sopenharmony_ci	case IEEE80211_STYPE_DISASSOC:
223762306a36Sopenharmony_ci		handle_disassoc(local, skb, rx_stats);
223862306a36Sopenharmony_ci		break;
223962306a36Sopenharmony_ci	case IEEE80211_STYPE_AUTH:
224062306a36Sopenharmony_ci		handle_authen(local, skb, rx_stats);
224162306a36Sopenharmony_ci		break;
224262306a36Sopenharmony_ci	case IEEE80211_STYPE_DEAUTH:
224362306a36Sopenharmony_ci		handle_deauth(local, skb, rx_stats);
224462306a36Sopenharmony_ci		break;
224562306a36Sopenharmony_ci	default:
224662306a36Sopenharmony_ci		PDEBUG(DEBUG_AP, "Unknown mgmt frame subtype 0x%02x\n",
224762306a36Sopenharmony_ci		       stype >> 4);
224862306a36Sopenharmony_ci		break;
224962306a36Sopenharmony_ci	}
225062306a36Sopenharmony_ci#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */
225162306a36Sopenharmony_ci
225262306a36Sopenharmony_ci done:
225362306a36Sopenharmony_ci	dev_kfree_skb(skb);
225462306a36Sopenharmony_ci}
225562306a36Sopenharmony_ci
225662306a36Sopenharmony_ci
225762306a36Sopenharmony_ci/* Called only as a tasklet (software IRQ) */
225862306a36Sopenharmony_civoid hostap_rx(struct net_device *dev, struct sk_buff *skb,
225962306a36Sopenharmony_ci	       struct hostap_80211_rx_status *rx_stats)
226062306a36Sopenharmony_ci{
226162306a36Sopenharmony_ci	struct hostap_interface *iface;
226262306a36Sopenharmony_ci	local_info_t *local;
226362306a36Sopenharmony_ci	struct ieee80211_hdr *hdr;
226462306a36Sopenharmony_ci
226562306a36Sopenharmony_ci	iface = netdev_priv(dev);
226662306a36Sopenharmony_ci	local = iface->local;
226762306a36Sopenharmony_ci
226862306a36Sopenharmony_ci	if (skb->len < 16)
226962306a36Sopenharmony_ci		goto drop;
227062306a36Sopenharmony_ci
227162306a36Sopenharmony_ci	dev->stats.rx_packets++;
227262306a36Sopenharmony_ci
227362306a36Sopenharmony_ci	hdr = (struct ieee80211_hdr *) skb->data;
227462306a36Sopenharmony_ci
227562306a36Sopenharmony_ci	if (local->ap->ap_policy == AP_OTHER_AP_SKIP_ALL &&
227662306a36Sopenharmony_ci	    ieee80211_is_beacon(hdr->frame_control))
227762306a36Sopenharmony_ci		goto drop;
227862306a36Sopenharmony_ci
227962306a36Sopenharmony_ci	skb->protocol = cpu_to_be16(ETH_P_HOSTAP);
228062306a36Sopenharmony_ci	handle_ap_item(local, skb, rx_stats);
228162306a36Sopenharmony_ci	return;
228262306a36Sopenharmony_ci
228362306a36Sopenharmony_ci drop:
228462306a36Sopenharmony_ci	dev_kfree_skb(skb);
228562306a36Sopenharmony_ci}
228662306a36Sopenharmony_ci
228762306a36Sopenharmony_ci
228862306a36Sopenharmony_ci/* Called only as a tasklet (software IRQ) */
228962306a36Sopenharmony_cistatic void schedule_packet_send(local_info_t *local, struct sta_info *sta)
229062306a36Sopenharmony_ci{
229162306a36Sopenharmony_ci	struct sk_buff *skb;
229262306a36Sopenharmony_ci	struct ieee80211_hdr *hdr;
229362306a36Sopenharmony_ci	struct hostap_80211_rx_status rx_stats;
229462306a36Sopenharmony_ci
229562306a36Sopenharmony_ci	if (skb_queue_empty(&sta->tx_buf))
229662306a36Sopenharmony_ci		return;
229762306a36Sopenharmony_ci
229862306a36Sopenharmony_ci	skb = dev_alloc_skb(16);
229962306a36Sopenharmony_ci	if (skb == NULL) {
230062306a36Sopenharmony_ci		printk(KERN_DEBUG "%s: schedule_packet_send: skb alloc "
230162306a36Sopenharmony_ci		       "failed\n", local->dev->name);
230262306a36Sopenharmony_ci		return;
230362306a36Sopenharmony_ci	}
230462306a36Sopenharmony_ci
230562306a36Sopenharmony_ci	hdr = skb_put(skb, 16);
230662306a36Sopenharmony_ci
230762306a36Sopenharmony_ci	/* Generate a fake pspoll frame to start packet delivery */
230862306a36Sopenharmony_ci	hdr->frame_control = cpu_to_le16(
230962306a36Sopenharmony_ci		IEEE80211_FTYPE_CTL | IEEE80211_STYPE_PSPOLL);
231062306a36Sopenharmony_ci	memcpy(hdr->addr1, local->dev->dev_addr, ETH_ALEN);
231162306a36Sopenharmony_ci	memcpy(hdr->addr2, sta->addr, ETH_ALEN);
231262306a36Sopenharmony_ci	hdr->duration_id = cpu_to_le16(sta->aid | BIT(15) | BIT(14));
231362306a36Sopenharmony_ci
231462306a36Sopenharmony_ci	PDEBUG(DEBUG_PS2,
231562306a36Sopenharmony_ci	       "%s: Scheduling buffered packet delivery for STA %pM\n",
231662306a36Sopenharmony_ci	       local->dev->name, sta->addr);
231762306a36Sopenharmony_ci
231862306a36Sopenharmony_ci	skb->dev = local->dev;
231962306a36Sopenharmony_ci
232062306a36Sopenharmony_ci	memset(&rx_stats, 0, sizeof(rx_stats));
232162306a36Sopenharmony_ci	hostap_rx(local->dev, skb, &rx_stats);
232262306a36Sopenharmony_ci}
232362306a36Sopenharmony_ci
232462306a36Sopenharmony_ci
232562306a36Sopenharmony_ciint prism2_ap_get_sta_qual(local_info_t *local, struct sockaddr addr[],
232662306a36Sopenharmony_ci			   struct iw_quality qual[], int buf_size,
232762306a36Sopenharmony_ci			   int aplist)
232862306a36Sopenharmony_ci{
232962306a36Sopenharmony_ci	struct ap_data *ap = local->ap;
233062306a36Sopenharmony_ci	struct list_head *ptr;
233162306a36Sopenharmony_ci	int count = 0;
233262306a36Sopenharmony_ci
233362306a36Sopenharmony_ci	spin_lock_bh(&ap->sta_table_lock);
233462306a36Sopenharmony_ci
233562306a36Sopenharmony_ci	for (ptr = ap->sta_list.next; ptr != NULL && ptr != &ap->sta_list;
233662306a36Sopenharmony_ci	     ptr = ptr->next) {
233762306a36Sopenharmony_ci		struct sta_info *sta = (struct sta_info *) ptr;
233862306a36Sopenharmony_ci
233962306a36Sopenharmony_ci		if (aplist && !sta->ap)
234062306a36Sopenharmony_ci			continue;
234162306a36Sopenharmony_ci		addr[count].sa_family = ARPHRD_ETHER;
234262306a36Sopenharmony_ci		memcpy(addr[count].sa_data, sta->addr, ETH_ALEN);
234362306a36Sopenharmony_ci		if (sta->last_rx_silence == 0)
234462306a36Sopenharmony_ci			qual[count].qual = sta->last_rx_signal < 27 ?
234562306a36Sopenharmony_ci				0 : (sta->last_rx_signal - 27) * 92 / 127;
234662306a36Sopenharmony_ci		else
234762306a36Sopenharmony_ci			qual[count].qual = sta->last_rx_signal -
234862306a36Sopenharmony_ci				sta->last_rx_silence - 35;
234962306a36Sopenharmony_ci		qual[count].level = HFA384X_LEVEL_TO_dBm(sta->last_rx_signal);
235062306a36Sopenharmony_ci		qual[count].noise = HFA384X_LEVEL_TO_dBm(sta->last_rx_silence);
235162306a36Sopenharmony_ci		qual[count].updated = sta->last_rx_updated;
235262306a36Sopenharmony_ci
235362306a36Sopenharmony_ci		sta->last_rx_updated = IW_QUAL_DBM;
235462306a36Sopenharmony_ci
235562306a36Sopenharmony_ci		count++;
235662306a36Sopenharmony_ci		if (count >= buf_size)
235762306a36Sopenharmony_ci			break;
235862306a36Sopenharmony_ci	}
235962306a36Sopenharmony_ci	spin_unlock_bh(&ap->sta_table_lock);
236062306a36Sopenharmony_ci
236162306a36Sopenharmony_ci	return count;
236262306a36Sopenharmony_ci}
236362306a36Sopenharmony_ci
236462306a36Sopenharmony_ci
236562306a36Sopenharmony_ci/* Translate our list of Access Points & Stations to a card independent
236662306a36Sopenharmony_ci * format that the Wireless Tools will understand - Jean II */
236762306a36Sopenharmony_ciint prism2_ap_translate_scan(struct net_device *dev,
236862306a36Sopenharmony_ci			     struct iw_request_info *info, char *buffer)
236962306a36Sopenharmony_ci{
237062306a36Sopenharmony_ci	struct hostap_interface *iface;
237162306a36Sopenharmony_ci	local_info_t *local;
237262306a36Sopenharmony_ci	struct ap_data *ap;
237362306a36Sopenharmony_ci	struct list_head *ptr;
237462306a36Sopenharmony_ci	struct iw_event iwe;
237562306a36Sopenharmony_ci	char *current_ev = buffer;
237662306a36Sopenharmony_ci	char *end_buf = buffer + IW_SCAN_MAX_DATA;
237762306a36Sopenharmony_ci#if !defined(PRISM2_NO_KERNEL_IEEE80211_MGMT)
237862306a36Sopenharmony_ci	char buf[64];
237962306a36Sopenharmony_ci#endif
238062306a36Sopenharmony_ci
238162306a36Sopenharmony_ci	iface = netdev_priv(dev);
238262306a36Sopenharmony_ci	local = iface->local;
238362306a36Sopenharmony_ci	ap = local->ap;
238462306a36Sopenharmony_ci
238562306a36Sopenharmony_ci	spin_lock_bh(&ap->sta_table_lock);
238662306a36Sopenharmony_ci
238762306a36Sopenharmony_ci	for (ptr = ap->sta_list.next; ptr != NULL && ptr != &ap->sta_list;
238862306a36Sopenharmony_ci	     ptr = ptr->next) {
238962306a36Sopenharmony_ci		struct sta_info *sta = (struct sta_info *) ptr;
239062306a36Sopenharmony_ci
239162306a36Sopenharmony_ci		/* First entry *MUST* be the AP MAC address */
239262306a36Sopenharmony_ci		memset(&iwe, 0, sizeof(iwe));
239362306a36Sopenharmony_ci		iwe.cmd = SIOCGIWAP;
239462306a36Sopenharmony_ci		iwe.u.ap_addr.sa_family = ARPHRD_ETHER;
239562306a36Sopenharmony_ci		memcpy(iwe.u.ap_addr.sa_data, sta->addr, ETH_ALEN);
239662306a36Sopenharmony_ci		iwe.len = IW_EV_ADDR_LEN;
239762306a36Sopenharmony_ci		current_ev = iwe_stream_add_event(info, current_ev, end_buf,
239862306a36Sopenharmony_ci						  &iwe, IW_EV_ADDR_LEN);
239962306a36Sopenharmony_ci
240062306a36Sopenharmony_ci		/* Use the mode to indicate if it's a station or
240162306a36Sopenharmony_ci		 * an Access Point */
240262306a36Sopenharmony_ci		memset(&iwe, 0, sizeof(iwe));
240362306a36Sopenharmony_ci		iwe.cmd = SIOCGIWMODE;
240462306a36Sopenharmony_ci		if (sta->ap)
240562306a36Sopenharmony_ci			iwe.u.mode = IW_MODE_MASTER;
240662306a36Sopenharmony_ci		else
240762306a36Sopenharmony_ci			iwe.u.mode = IW_MODE_INFRA;
240862306a36Sopenharmony_ci		iwe.len = IW_EV_UINT_LEN;
240962306a36Sopenharmony_ci		current_ev = iwe_stream_add_event(info, current_ev, end_buf,
241062306a36Sopenharmony_ci						  &iwe, IW_EV_UINT_LEN);
241162306a36Sopenharmony_ci
241262306a36Sopenharmony_ci		/* Some quality */
241362306a36Sopenharmony_ci		memset(&iwe, 0, sizeof(iwe));
241462306a36Sopenharmony_ci		iwe.cmd = IWEVQUAL;
241562306a36Sopenharmony_ci		if (sta->last_rx_silence == 0)
241662306a36Sopenharmony_ci			iwe.u.qual.qual = sta->last_rx_signal < 27 ?
241762306a36Sopenharmony_ci				0 : (sta->last_rx_signal - 27) * 92 / 127;
241862306a36Sopenharmony_ci		else
241962306a36Sopenharmony_ci			iwe.u.qual.qual = sta->last_rx_signal -
242062306a36Sopenharmony_ci				sta->last_rx_silence - 35;
242162306a36Sopenharmony_ci		iwe.u.qual.level = HFA384X_LEVEL_TO_dBm(sta->last_rx_signal);
242262306a36Sopenharmony_ci		iwe.u.qual.noise = HFA384X_LEVEL_TO_dBm(sta->last_rx_silence);
242362306a36Sopenharmony_ci		iwe.u.qual.updated = sta->last_rx_updated;
242462306a36Sopenharmony_ci		iwe.len = IW_EV_QUAL_LEN;
242562306a36Sopenharmony_ci		current_ev = iwe_stream_add_event(info, current_ev, end_buf,
242662306a36Sopenharmony_ci						  &iwe, IW_EV_QUAL_LEN);
242762306a36Sopenharmony_ci
242862306a36Sopenharmony_ci#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT
242962306a36Sopenharmony_ci		if (sta->ap) {
243062306a36Sopenharmony_ci			memset(&iwe, 0, sizeof(iwe));
243162306a36Sopenharmony_ci			iwe.cmd = SIOCGIWESSID;
243262306a36Sopenharmony_ci			iwe.u.data.length = sta->u.ap.ssid_len;
243362306a36Sopenharmony_ci			iwe.u.data.flags = 1;
243462306a36Sopenharmony_ci			current_ev = iwe_stream_add_point(info, current_ev,
243562306a36Sopenharmony_ci							  end_buf, &iwe,
243662306a36Sopenharmony_ci							  sta->u.ap.ssid);
243762306a36Sopenharmony_ci
243862306a36Sopenharmony_ci			memset(&iwe, 0, sizeof(iwe));
243962306a36Sopenharmony_ci			iwe.cmd = SIOCGIWENCODE;
244062306a36Sopenharmony_ci			if (sta->capability & WLAN_CAPABILITY_PRIVACY)
244162306a36Sopenharmony_ci				iwe.u.data.flags =
244262306a36Sopenharmony_ci					IW_ENCODE_ENABLED | IW_ENCODE_NOKEY;
244362306a36Sopenharmony_ci			else
244462306a36Sopenharmony_ci				iwe.u.data.flags = IW_ENCODE_DISABLED;
244562306a36Sopenharmony_ci			current_ev = iwe_stream_add_point(info, current_ev,
244662306a36Sopenharmony_ci							  end_buf, &iwe,
244762306a36Sopenharmony_ci							  sta->u.ap.ssid);
244862306a36Sopenharmony_ci
244962306a36Sopenharmony_ci			if (sta->u.ap.channel > 0 &&
245062306a36Sopenharmony_ci			    sta->u.ap.channel <= FREQ_COUNT) {
245162306a36Sopenharmony_ci				memset(&iwe, 0, sizeof(iwe));
245262306a36Sopenharmony_ci				iwe.cmd = SIOCGIWFREQ;
245362306a36Sopenharmony_ci				iwe.u.freq.m = freq_list[sta->u.ap.channel - 1]
245462306a36Sopenharmony_ci					* 100000;
245562306a36Sopenharmony_ci				iwe.u.freq.e = 1;
245662306a36Sopenharmony_ci				current_ev = iwe_stream_add_event(
245762306a36Sopenharmony_ci					info, current_ev, end_buf, &iwe,
245862306a36Sopenharmony_ci					IW_EV_FREQ_LEN);
245962306a36Sopenharmony_ci			}
246062306a36Sopenharmony_ci
246162306a36Sopenharmony_ci			memset(&iwe, 0, sizeof(iwe));
246262306a36Sopenharmony_ci			iwe.cmd = IWEVCUSTOM;
246362306a36Sopenharmony_ci			sprintf(buf, "beacon_interval=%d",
246462306a36Sopenharmony_ci				sta->listen_interval);
246562306a36Sopenharmony_ci			iwe.u.data.length = strlen(buf);
246662306a36Sopenharmony_ci			current_ev = iwe_stream_add_point(info, current_ev,
246762306a36Sopenharmony_ci							  end_buf, &iwe, buf);
246862306a36Sopenharmony_ci		}
246962306a36Sopenharmony_ci#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */
247062306a36Sopenharmony_ci
247162306a36Sopenharmony_ci		sta->last_rx_updated = IW_QUAL_DBM;
247262306a36Sopenharmony_ci
247362306a36Sopenharmony_ci		/* To be continued, we should make good use of IWEVCUSTOM */
247462306a36Sopenharmony_ci	}
247562306a36Sopenharmony_ci
247662306a36Sopenharmony_ci	spin_unlock_bh(&ap->sta_table_lock);
247762306a36Sopenharmony_ci
247862306a36Sopenharmony_ci	return current_ev - buffer;
247962306a36Sopenharmony_ci}
248062306a36Sopenharmony_ci
248162306a36Sopenharmony_ci
248262306a36Sopenharmony_cistatic int prism2_hostapd_add_sta(struct ap_data *ap,
248362306a36Sopenharmony_ci				  struct prism2_hostapd_param *param)
248462306a36Sopenharmony_ci{
248562306a36Sopenharmony_ci	struct sta_info *sta;
248662306a36Sopenharmony_ci
248762306a36Sopenharmony_ci	spin_lock_bh(&ap->sta_table_lock);
248862306a36Sopenharmony_ci	sta = ap_get_sta(ap, param->sta_addr);
248962306a36Sopenharmony_ci	if (sta)
249062306a36Sopenharmony_ci		atomic_inc(&sta->users);
249162306a36Sopenharmony_ci	spin_unlock_bh(&ap->sta_table_lock);
249262306a36Sopenharmony_ci
249362306a36Sopenharmony_ci	if (sta == NULL) {
249462306a36Sopenharmony_ci		sta = ap_add_sta(ap, param->sta_addr);
249562306a36Sopenharmony_ci		if (sta == NULL)
249662306a36Sopenharmony_ci			return -1;
249762306a36Sopenharmony_ci	}
249862306a36Sopenharmony_ci
249962306a36Sopenharmony_ci	if (!(sta->flags & WLAN_STA_ASSOC) && !sta->ap && sta->local)
250062306a36Sopenharmony_ci		hostap_event_new_sta(sta->local->dev, sta);
250162306a36Sopenharmony_ci
250262306a36Sopenharmony_ci	sta->flags |= WLAN_STA_AUTH | WLAN_STA_ASSOC;
250362306a36Sopenharmony_ci	sta->last_rx = jiffies;
250462306a36Sopenharmony_ci	sta->aid = param->u.add_sta.aid;
250562306a36Sopenharmony_ci	sta->capability = param->u.add_sta.capability;
250662306a36Sopenharmony_ci	sta->tx_supp_rates = param->u.add_sta.tx_supp_rates;
250762306a36Sopenharmony_ci	if (sta->tx_supp_rates & WLAN_RATE_1M)
250862306a36Sopenharmony_ci		sta->supported_rates[0] = 2;
250962306a36Sopenharmony_ci	if (sta->tx_supp_rates & WLAN_RATE_2M)
251062306a36Sopenharmony_ci		sta->supported_rates[1] = 4;
251162306a36Sopenharmony_ci	if (sta->tx_supp_rates & WLAN_RATE_5M5)
251262306a36Sopenharmony_ci		sta->supported_rates[2] = 11;
251362306a36Sopenharmony_ci	if (sta->tx_supp_rates & WLAN_RATE_11M)
251462306a36Sopenharmony_ci		sta->supported_rates[3] = 22;
251562306a36Sopenharmony_ci	prism2_check_tx_rates(sta);
251662306a36Sopenharmony_ci	atomic_dec(&sta->users);
251762306a36Sopenharmony_ci	return 0;
251862306a36Sopenharmony_ci}
251962306a36Sopenharmony_ci
252062306a36Sopenharmony_ci
252162306a36Sopenharmony_cistatic int prism2_hostapd_remove_sta(struct ap_data *ap,
252262306a36Sopenharmony_ci				     struct prism2_hostapd_param *param)
252362306a36Sopenharmony_ci{
252462306a36Sopenharmony_ci	struct sta_info *sta;
252562306a36Sopenharmony_ci
252662306a36Sopenharmony_ci	spin_lock_bh(&ap->sta_table_lock);
252762306a36Sopenharmony_ci	sta = ap_get_sta(ap, param->sta_addr);
252862306a36Sopenharmony_ci	if (sta) {
252962306a36Sopenharmony_ci		ap_sta_hash_del(ap, sta);
253062306a36Sopenharmony_ci		list_del(&sta->list);
253162306a36Sopenharmony_ci	}
253262306a36Sopenharmony_ci	spin_unlock_bh(&ap->sta_table_lock);
253362306a36Sopenharmony_ci
253462306a36Sopenharmony_ci	if (!sta)
253562306a36Sopenharmony_ci		return -ENOENT;
253662306a36Sopenharmony_ci
253762306a36Sopenharmony_ci	if ((sta->flags & WLAN_STA_ASSOC) && !sta->ap && sta->local)
253862306a36Sopenharmony_ci		hostap_event_expired_sta(sta->local->dev, sta);
253962306a36Sopenharmony_ci	ap_free_sta(ap, sta);
254062306a36Sopenharmony_ci
254162306a36Sopenharmony_ci	return 0;
254262306a36Sopenharmony_ci}
254362306a36Sopenharmony_ci
254462306a36Sopenharmony_ci
254562306a36Sopenharmony_cistatic int prism2_hostapd_get_info_sta(struct ap_data *ap,
254662306a36Sopenharmony_ci				       struct prism2_hostapd_param *param)
254762306a36Sopenharmony_ci{
254862306a36Sopenharmony_ci	struct sta_info *sta;
254962306a36Sopenharmony_ci
255062306a36Sopenharmony_ci	spin_lock_bh(&ap->sta_table_lock);
255162306a36Sopenharmony_ci	sta = ap_get_sta(ap, param->sta_addr);
255262306a36Sopenharmony_ci	if (sta)
255362306a36Sopenharmony_ci		atomic_inc(&sta->users);
255462306a36Sopenharmony_ci	spin_unlock_bh(&ap->sta_table_lock);
255562306a36Sopenharmony_ci
255662306a36Sopenharmony_ci	if (!sta)
255762306a36Sopenharmony_ci		return -ENOENT;
255862306a36Sopenharmony_ci
255962306a36Sopenharmony_ci	param->u.get_info_sta.inactive_sec = (jiffies - sta->last_rx) / HZ;
256062306a36Sopenharmony_ci
256162306a36Sopenharmony_ci	atomic_dec(&sta->users);
256262306a36Sopenharmony_ci
256362306a36Sopenharmony_ci	return 1;
256462306a36Sopenharmony_ci}
256562306a36Sopenharmony_ci
256662306a36Sopenharmony_ci
256762306a36Sopenharmony_cistatic int prism2_hostapd_set_flags_sta(struct ap_data *ap,
256862306a36Sopenharmony_ci					struct prism2_hostapd_param *param)
256962306a36Sopenharmony_ci{
257062306a36Sopenharmony_ci	struct sta_info *sta;
257162306a36Sopenharmony_ci
257262306a36Sopenharmony_ci	spin_lock_bh(&ap->sta_table_lock);
257362306a36Sopenharmony_ci	sta = ap_get_sta(ap, param->sta_addr);
257462306a36Sopenharmony_ci	if (sta) {
257562306a36Sopenharmony_ci		sta->flags |= param->u.set_flags_sta.flags_or;
257662306a36Sopenharmony_ci		sta->flags &= param->u.set_flags_sta.flags_and;
257762306a36Sopenharmony_ci	}
257862306a36Sopenharmony_ci	spin_unlock_bh(&ap->sta_table_lock);
257962306a36Sopenharmony_ci
258062306a36Sopenharmony_ci	if (!sta)
258162306a36Sopenharmony_ci		return -ENOENT;
258262306a36Sopenharmony_ci
258362306a36Sopenharmony_ci	return 0;
258462306a36Sopenharmony_ci}
258562306a36Sopenharmony_ci
258662306a36Sopenharmony_ci
258762306a36Sopenharmony_cistatic int prism2_hostapd_sta_clear_stats(struct ap_data *ap,
258862306a36Sopenharmony_ci					  struct prism2_hostapd_param *param)
258962306a36Sopenharmony_ci{
259062306a36Sopenharmony_ci	struct sta_info *sta;
259162306a36Sopenharmony_ci	int rate;
259262306a36Sopenharmony_ci
259362306a36Sopenharmony_ci	spin_lock_bh(&ap->sta_table_lock);
259462306a36Sopenharmony_ci	sta = ap_get_sta(ap, param->sta_addr);
259562306a36Sopenharmony_ci	if (sta) {
259662306a36Sopenharmony_ci		sta->rx_packets = sta->tx_packets = 0;
259762306a36Sopenharmony_ci		sta->rx_bytes = sta->tx_bytes = 0;
259862306a36Sopenharmony_ci		for (rate = 0; rate < WLAN_RATE_COUNT; rate++) {
259962306a36Sopenharmony_ci			sta->tx_count[rate] = 0;
260062306a36Sopenharmony_ci			sta->rx_count[rate] = 0;
260162306a36Sopenharmony_ci		}
260262306a36Sopenharmony_ci	}
260362306a36Sopenharmony_ci	spin_unlock_bh(&ap->sta_table_lock);
260462306a36Sopenharmony_ci
260562306a36Sopenharmony_ci	if (!sta)
260662306a36Sopenharmony_ci		return -ENOENT;
260762306a36Sopenharmony_ci
260862306a36Sopenharmony_ci	return 0;
260962306a36Sopenharmony_ci}
261062306a36Sopenharmony_ci
261162306a36Sopenharmony_ci
261262306a36Sopenharmony_ciint prism2_hostapd(struct ap_data *ap, struct prism2_hostapd_param *param)
261362306a36Sopenharmony_ci{
261462306a36Sopenharmony_ci	switch (param->cmd) {
261562306a36Sopenharmony_ci	case PRISM2_HOSTAPD_FLUSH:
261662306a36Sopenharmony_ci		ap_control_kickall(ap);
261762306a36Sopenharmony_ci		return 0;
261862306a36Sopenharmony_ci	case PRISM2_HOSTAPD_ADD_STA:
261962306a36Sopenharmony_ci		return prism2_hostapd_add_sta(ap, param);
262062306a36Sopenharmony_ci	case PRISM2_HOSTAPD_REMOVE_STA:
262162306a36Sopenharmony_ci		return prism2_hostapd_remove_sta(ap, param);
262262306a36Sopenharmony_ci	case PRISM2_HOSTAPD_GET_INFO_STA:
262362306a36Sopenharmony_ci		return prism2_hostapd_get_info_sta(ap, param);
262462306a36Sopenharmony_ci	case PRISM2_HOSTAPD_SET_FLAGS_STA:
262562306a36Sopenharmony_ci		return prism2_hostapd_set_flags_sta(ap, param);
262662306a36Sopenharmony_ci	case PRISM2_HOSTAPD_STA_CLEAR_STATS:
262762306a36Sopenharmony_ci		return prism2_hostapd_sta_clear_stats(ap, param);
262862306a36Sopenharmony_ci	default:
262962306a36Sopenharmony_ci		printk(KERN_WARNING "prism2_hostapd: unknown cmd=%d\n",
263062306a36Sopenharmony_ci		       param->cmd);
263162306a36Sopenharmony_ci		return -EOPNOTSUPP;
263262306a36Sopenharmony_ci	}
263362306a36Sopenharmony_ci}
263462306a36Sopenharmony_ci
263562306a36Sopenharmony_ci
263662306a36Sopenharmony_ci/* Update station info for host-based TX rate control and return current
263762306a36Sopenharmony_ci * TX rate */
263862306a36Sopenharmony_cistatic int ap_update_sta_tx_rate(struct sta_info *sta, struct net_device *dev)
263962306a36Sopenharmony_ci{
264062306a36Sopenharmony_ci	int ret = sta->tx_rate;
264162306a36Sopenharmony_ci	struct hostap_interface *iface;
264262306a36Sopenharmony_ci	local_info_t *local;
264362306a36Sopenharmony_ci
264462306a36Sopenharmony_ci	iface = netdev_priv(dev);
264562306a36Sopenharmony_ci	local = iface->local;
264662306a36Sopenharmony_ci
264762306a36Sopenharmony_ci	sta->tx_count[sta->tx_rate_idx]++;
264862306a36Sopenharmony_ci	sta->tx_since_last_failure++;
264962306a36Sopenharmony_ci	sta->tx_consecutive_exc = 0;
265062306a36Sopenharmony_ci	if (sta->tx_since_last_failure >= WLAN_RATE_UPDATE_COUNT &&
265162306a36Sopenharmony_ci	    sta->tx_rate_idx < sta->tx_max_rate) {
265262306a36Sopenharmony_ci		/* use next higher rate */
265362306a36Sopenharmony_ci		int old_rate, new_rate;
265462306a36Sopenharmony_ci		old_rate = new_rate = sta->tx_rate_idx;
265562306a36Sopenharmony_ci		while (new_rate < sta->tx_max_rate) {
265662306a36Sopenharmony_ci			new_rate++;
265762306a36Sopenharmony_ci			if (ap_tx_rate_ok(new_rate, sta, local)) {
265862306a36Sopenharmony_ci				sta->tx_rate_idx = new_rate;
265962306a36Sopenharmony_ci				break;
266062306a36Sopenharmony_ci			}
266162306a36Sopenharmony_ci		}
266262306a36Sopenharmony_ci		if (old_rate != sta->tx_rate_idx) {
266362306a36Sopenharmony_ci			switch (sta->tx_rate_idx) {
266462306a36Sopenharmony_ci			case 0: sta->tx_rate = 10; break;
266562306a36Sopenharmony_ci			case 1: sta->tx_rate = 20; break;
266662306a36Sopenharmony_ci			case 2: sta->tx_rate = 55; break;
266762306a36Sopenharmony_ci			case 3: sta->tx_rate = 110; break;
266862306a36Sopenharmony_ci			default: sta->tx_rate = 0; break;
266962306a36Sopenharmony_ci			}
267062306a36Sopenharmony_ci			PDEBUG(DEBUG_AP, "%s: STA %pM TX rate raised to %d\n",
267162306a36Sopenharmony_ci			       dev->name, sta->addr, sta->tx_rate);
267262306a36Sopenharmony_ci		}
267362306a36Sopenharmony_ci		sta->tx_since_last_failure = 0;
267462306a36Sopenharmony_ci	}
267562306a36Sopenharmony_ci
267662306a36Sopenharmony_ci	return ret;
267762306a36Sopenharmony_ci}
267862306a36Sopenharmony_ci
267962306a36Sopenharmony_ci
268062306a36Sopenharmony_ci/* Called only from software IRQ. Called for each TX frame prior possible
268162306a36Sopenharmony_ci * encryption and transmit. */
268262306a36Sopenharmony_ciap_tx_ret hostap_handle_sta_tx(local_info_t *local, struct hostap_tx_data *tx)
268362306a36Sopenharmony_ci{
268462306a36Sopenharmony_ci	struct sta_info *sta = NULL;
268562306a36Sopenharmony_ci	struct sk_buff *skb = tx->skb;
268662306a36Sopenharmony_ci	int set_tim, ret;
268762306a36Sopenharmony_ci	struct ieee80211_hdr *hdr;
268862306a36Sopenharmony_ci	struct hostap_skb_tx_data *meta;
268962306a36Sopenharmony_ci
269062306a36Sopenharmony_ci	meta = (struct hostap_skb_tx_data *) skb->cb;
269162306a36Sopenharmony_ci	ret = AP_TX_CONTINUE;
269262306a36Sopenharmony_ci	if (local->ap == NULL || skb->len < 10 ||
269362306a36Sopenharmony_ci	    meta->iface->type == HOSTAP_INTERFACE_STA)
269462306a36Sopenharmony_ci		goto out;
269562306a36Sopenharmony_ci
269662306a36Sopenharmony_ci	hdr = (struct ieee80211_hdr *) skb->data;
269762306a36Sopenharmony_ci
269862306a36Sopenharmony_ci	if (hdr->addr1[0] & 0x01) {
269962306a36Sopenharmony_ci		/* broadcast/multicast frame - no AP related processing */
270062306a36Sopenharmony_ci		if (local->ap->num_sta <= 0)
270162306a36Sopenharmony_ci			ret = AP_TX_DROP;
270262306a36Sopenharmony_ci		goto out;
270362306a36Sopenharmony_ci	}
270462306a36Sopenharmony_ci
270562306a36Sopenharmony_ci	/* unicast packet - check whether destination STA is associated */
270662306a36Sopenharmony_ci	spin_lock(&local->ap->sta_table_lock);
270762306a36Sopenharmony_ci	sta = ap_get_sta(local->ap, hdr->addr1);
270862306a36Sopenharmony_ci	if (sta)
270962306a36Sopenharmony_ci		atomic_inc(&sta->users);
271062306a36Sopenharmony_ci	spin_unlock(&local->ap->sta_table_lock);
271162306a36Sopenharmony_ci
271262306a36Sopenharmony_ci	if (local->iw_mode == IW_MODE_MASTER && sta == NULL &&
271362306a36Sopenharmony_ci	    !(meta->flags & HOSTAP_TX_FLAGS_WDS) &&
271462306a36Sopenharmony_ci	    meta->iface->type != HOSTAP_INTERFACE_MASTER &&
271562306a36Sopenharmony_ci	    meta->iface->type != HOSTAP_INTERFACE_AP) {
271662306a36Sopenharmony_ci#if 0
271762306a36Sopenharmony_ci		/* This can happen, e.g., when wlan0 is added to a bridge and
271862306a36Sopenharmony_ci		 * bridging code does not know which port is the correct target
271962306a36Sopenharmony_ci		 * for a unicast frame. In this case, the packet is send to all
272062306a36Sopenharmony_ci		 * ports of the bridge. Since this is a valid scenario, do not
272162306a36Sopenharmony_ci		 * print out any errors here. */
272262306a36Sopenharmony_ci		if (net_ratelimit()) {
272362306a36Sopenharmony_ci			printk(KERN_DEBUG "AP: drop packet to non-associated "
272462306a36Sopenharmony_ci			       "STA %pM\n", hdr->addr1);
272562306a36Sopenharmony_ci		}
272662306a36Sopenharmony_ci#endif
272762306a36Sopenharmony_ci		local->ap->tx_drop_nonassoc++;
272862306a36Sopenharmony_ci		ret = AP_TX_DROP;
272962306a36Sopenharmony_ci		goto out;
273062306a36Sopenharmony_ci	}
273162306a36Sopenharmony_ci
273262306a36Sopenharmony_ci	if (sta == NULL)
273362306a36Sopenharmony_ci		goto out;
273462306a36Sopenharmony_ci
273562306a36Sopenharmony_ci	if (!(sta->flags & WLAN_STA_AUTHORIZED))
273662306a36Sopenharmony_ci		ret = AP_TX_CONTINUE_NOT_AUTHORIZED;
273762306a36Sopenharmony_ci
273862306a36Sopenharmony_ci	/* Set tx_rate if using host-based TX rate control */
273962306a36Sopenharmony_ci	if (!local->fw_tx_rate_control)
274062306a36Sopenharmony_ci		local->ap->last_tx_rate = meta->rate =
274162306a36Sopenharmony_ci			ap_update_sta_tx_rate(sta, local->dev);
274262306a36Sopenharmony_ci
274362306a36Sopenharmony_ci	if (local->iw_mode != IW_MODE_MASTER)
274462306a36Sopenharmony_ci		goto out;
274562306a36Sopenharmony_ci
274662306a36Sopenharmony_ci	if (!(sta->flags & WLAN_STA_PS))
274762306a36Sopenharmony_ci		goto out;
274862306a36Sopenharmony_ci
274962306a36Sopenharmony_ci	if (meta->flags & HOSTAP_TX_FLAGS_ADD_MOREDATA) {
275062306a36Sopenharmony_ci		/* indicate to STA that more frames follow */
275162306a36Sopenharmony_ci		hdr->frame_control |=
275262306a36Sopenharmony_ci			cpu_to_le16(IEEE80211_FCTL_MOREDATA);
275362306a36Sopenharmony_ci	}
275462306a36Sopenharmony_ci
275562306a36Sopenharmony_ci	if (meta->flags & HOSTAP_TX_FLAGS_BUFFERED_FRAME) {
275662306a36Sopenharmony_ci		/* packet was already buffered and now send due to
275762306a36Sopenharmony_ci		 * PS poll, so do not rebuffer it */
275862306a36Sopenharmony_ci		goto out;
275962306a36Sopenharmony_ci	}
276062306a36Sopenharmony_ci
276162306a36Sopenharmony_ci	if (skb_queue_len(&sta->tx_buf) >= STA_MAX_TX_BUFFER) {
276262306a36Sopenharmony_ci		PDEBUG(DEBUG_PS, "%s: No more space in STA (%pM)'s"
276362306a36Sopenharmony_ci		       "PS mode buffer\n",
276462306a36Sopenharmony_ci		       local->dev->name, sta->addr);
276562306a36Sopenharmony_ci		/* Make sure that TIM is set for the station (it might not be
276662306a36Sopenharmony_ci		 * after AP wlan hw reset). */
276762306a36Sopenharmony_ci		/* FIX: should fix hw reset to restore bits based on STA
276862306a36Sopenharmony_ci		 * buffer state.. */
276962306a36Sopenharmony_ci		hostap_set_tim(local, sta->aid, 1);
277062306a36Sopenharmony_ci		sta->flags |= WLAN_STA_TIM;
277162306a36Sopenharmony_ci		ret = AP_TX_DROP;
277262306a36Sopenharmony_ci		goto out;
277362306a36Sopenharmony_ci	}
277462306a36Sopenharmony_ci
277562306a36Sopenharmony_ci	/* STA in PS mode, buffer frame for later delivery */
277662306a36Sopenharmony_ci	set_tim = skb_queue_empty(&sta->tx_buf);
277762306a36Sopenharmony_ci	skb_queue_tail(&sta->tx_buf, skb);
277862306a36Sopenharmony_ci	/* FIX: could save RX time to skb and expire buffered frames after
277962306a36Sopenharmony_ci	 * some time if STA does not poll for them */
278062306a36Sopenharmony_ci
278162306a36Sopenharmony_ci	if (set_tim) {
278262306a36Sopenharmony_ci		if (sta->flags & WLAN_STA_TIM)
278362306a36Sopenharmony_ci			PDEBUG(DEBUG_PS2, "Re-setting TIM for aid %d\n",
278462306a36Sopenharmony_ci			       sta->aid);
278562306a36Sopenharmony_ci		hostap_set_tim(local, sta->aid, 1);
278662306a36Sopenharmony_ci		sta->flags |= WLAN_STA_TIM;
278762306a36Sopenharmony_ci	}
278862306a36Sopenharmony_ci
278962306a36Sopenharmony_ci	ret = AP_TX_BUFFERED;
279062306a36Sopenharmony_ci
279162306a36Sopenharmony_ci out:
279262306a36Sopenharmony_ci	if (sta != NULL) {
279362306a36Sopenharmony_ci		if (ret == AP_TX_CONTINUE ||
279462306a36Sopenharmony_ci		    ret == AP_TX_CONTINUE_NOT_AUTHORIZED) {
279562306a36Sopenharmony_ci			sta->tx_packets++;
279662306a36Sopenharmony_ci			sta->tx_bytes += skb->len;
279762306a36Sopenharmony_ci			sta->last_tx = jiffies;
279862306a36Sopenharmony_ci		}
279962306a36Sopenharmony_ci
280062306a36Sopenharmony_ci		if ((ret == AP_TX_CONTINUE ||
280162306a36Sopenharmony_ci		     ret == AP_TX_CONTINUE_NOT_AUTHORIZED) &&
280262306a36Sopenharmony_ci		    sta->crypt && tx->host_encrypt) {
280362306a36Sopenharmony_ci			tx->crypt = sta->crypt;
280462306a36Sopenharmony_ci			tx->sta_ptr = sta; /* hostap_handle_sta_release() will
280562306a36Sopenharmony_ci					    * be called to release sta info
280662306a36Sopenharmony_ci					    * later */
280762306a36Sopenharmony_ci		} else
280862306a36Sopenharmony_ci			atomic_dec(&sta->users);
280962306a36Sopenharmony_ci	}
281062306a36Sopenharmony_ci
281162306a36Sopenharmony_ci	return ret;
281262306a36Sopenharmony_ci}
281362306a36Sopenharmony_ci
281462306a36Sopenharmony_ci
281562306a36Sopenharmony_civoid hostap_handle_sta_release(void *ptr)
281662306a36Sopenharmony_ci{
281762306a36Sopenharmony_ci	struct sta_info *sta = ptr;
281862306a36Sopenharmony_ci	atomic_dec(&sta->users);
281962306a36Sopenharmony_ci}
282062306a36Sopenharmony_ci
282162306a36Sopenharmony_ci
282262306a36Sopenharmony_ci/* Called only as a tasklet (software IRQ) */
282362306a36Sopenharmony_civoid hostap_handle_sta_tx_exc(local_info_t *local, struct sk_buff *skb)
282462306a36Sopenharmony_ci{
282562306a36Sopenharmony_ci	struct sta_info *sta;
282662306a36Sopenharmony_ci	struct ieee80211_hdr *hdr;
282762306a36Sopenharmony_ci	struct hostap_skb_tx_data *meta;
282862306a36Sopenharmony_ci
282962306a36Sopenharmony_ci	hdr = (struct ieee80211_hdr *) skb->data;
283062306a36Sopenharmony_ci	meta = (struct hostap_skb_tx_data *) skb->cb;
283162306a36Sopenharmony_ci
283262306a36Sopenharmony_ci	spin_lock(&local->ap->sta_table_lock);
283362306a36Sopenharmony_ci	sta = ap_get_sta(local->ap, hdr->addr1);
283462306a36Sopenharmony_ci	if (!sta) {
283562306a36Sopenharmony_ci		spin_unlock(&local->ap->sta_table_lock);
283662306a36Sopenharmony_ci		PDEBUG(DEBUG_AP, "%s: Could not find STA %pM"
283762306a36Sopenharmony_ci		       " for this TX error (@%lu)\n",
283862306a36Sopenharmony_ci		       local->dev->name, hdr->addr1, jiffies);
283962306a36Sopenharmony_ci		return;
284062306a36Sopenharmony_ci	}
284162306a36Sopenharmony_ci
284262306a36Sopenharmony_ci	sta->tx_since_last_failure = 0;
284362306a36Sopenharmony_ci	sta->tx_consecutive_exc++;
284462306a36Sopenharmony_ci
284562306a36Sopenharmony_ci	if (sta->tx_consecutive_exc >= WLAN_RATE_DECREASE_THRESHOLD &&
284662306a36Sopenharmony_ci	    sta->tx_rate_idx > 0 && meta->rate <= sta->tx_rate) {
284762306a36Sopenharmony_ci		/* use next lower rate */
284862306a36Sopenharmony_ci		int old, rate;
284962306a36Sopenharmony_ci		old = rate = sta->tx_rate_idx;
285062306a36Sopenharmony_ci		while (rate > 0) {
285162306a36Sopenharmony_ci			rate--;
285262306a36Sopenharmony_ci			if (ap_tx_rate_ok(rate, sta, local)) {
285362306a36Sopenharmony_ci				sta->tx_rate_idx = rate;
285462306a36Sopenharmony_ci				break;
285562306a36Sopenharmony_ci			}
285662306a36Sopenharmony_ci		}
285762306a36Sopenharmony_ci		if (old != sta->tx_rate_idx) {
285862306a36Sopenharmony_ci			switch (sta->tx_rate_idx) {
285962306a36Sopenharmony_ci			case 0: sta->tx_rate = 10; break;
286062306a36Sopenharmony_ci			case 1: sta->tx_rate = 20; break;
286162306a36Sopenharmony_ci			case 2: sta->tx_rate = 55; break;
286262306a36Sopenharmony_ci			case 3: sta->tx_rate = 110; break;
286362306a36Sopenharmony_ci			default: sta->tx_rate = 0; break;
286462306a36Sopenharmony_ci			}
286562306a36Sopenharmony_ci			PDEBUG(DEBUG_AP,
286662306a36Sopenharmony_ci			       "%s: STA %pM TX rate lowered to %d\n",
286762306a36Sopenharmony_ci			       local->dev->name, sta->addr, sta->tx_rate);
286862306a36Sopenharmony_ci		}
286962306a36Sopenharmony_ci		sta->tx_consecutive_exc = 0;
287062306a36Sopenharmony_ci	}
287162306a36Sopenharmony_ci	spin_unlock(&local->ap->sta_table_lock);
287262306a36Sopenharmony_ci}
287362306a36Sopenharmony_ci
287462306a36Sopenharmony_ci
287562306a36Sopenharmony_cistatic void hostap_update_sta_ps2(local_info_t *local, struct sta_info *sta,
287662306a36Sopenharmony_ci				  int pwrmgt, int type, int stype)
287762306a36Sopenharmony_ci{
287862306a36Sopenharmony_ci	if (pwrmgt && !(sta->flags & WLAN_STA_PS)) {
287962306a36Sopenharmony_ci		sta->flags |= WLAN_STA_PS;
288062306a36Sopenharmony_ci		PDEBUG(DEBUG_PS2, "STA %pM changed to use PS "
288162306a36Sopenharmony_ci		       "mode (type=0x%02X, stype=0x%02X)\n",
288262306a36Sopenharmony_ci		       sta->addr, type >> 2, stype >> 4);
288362306a36Sopenharmony_ci	} else if (!pwrmgt && (sta->flags & WLAN_STA_PS)) {
288462306a36Sopenharmony_ci		sta->flags &= ~WLAN_STA_PS;
288562306a36Sopenharmony_ci		PDEBUG(DEBUG_PS2, "STA %pM changed to not use "
288662306a36Sopenharmony_ci		       "PS mode (type=0x%02X, stype=0x%02X)\n",
288762306a36Sopenharmony_ci		       sta->addr, type >> 2, stype >> 4);
288862306a36Sopenharmony_ci		if (type != IEEE80211_FTYPE_CTL ||
288962306a36Sopenharmony_ci		    stype != IEEE80211_STYPE_PSPOLL)
289062306a36Sopenharmony_ci			schedule_packet_send(local, sta);
289162306a36Sopenharmony_ci	}
289262306a36Sopenharmony_ci}
289362306a36Sopenharmony_ci
289462306a36Sopenharmony_ci
289562306a36Sopenharmony_ci/* Called only as a tasklet (software IRQ). Called for each RX frame to update
289662306a36Sopenharmony_ci * STA power saving state. pwrmgt is a flag from 802.11 frame_control field. */
289762306a36Sopenharmony_ciint hostap_update_sta_ps(local_info_t *local, struct ieee80211_hdr *hdr)
289862306a36Sopenharmony_ci{
289962306a36Sopenharmony_ci	struct sta_info *sta;
290062306a36Sopenharmony_ci	u16 fc;
290162306a36Sopenharmony_ci
290262306a36Sopenharmony_ci	spin_lock(&local->ap->sta_table_lock);
290362306a36Sopenharmony_ci	sta = ap_get_sta(local->ap, hdr->addr2);
290462306a36Sopenharmony_ci	if (sta)
290562306a36Sopenharmony_ci		atomic_inc(&sta->users);
290662306a36Sopenharmony_ci	spin_unlock(&local->ap->sta_table_lock);
290762306a36Sopenharmony_ci
290862306a36Sopenharmony_ci	if (!sta)
290962306a36Sopenharmony_ci		return -1;
291062306a36Sopenharmony_ci
291162306a36Sopenharmony_ci	fc = le16_to_cpu(hdr->frame_control);
291262306a36Sopenharmony_ci	hostap_update_sta_ps2(local, sta, fc & IEEE80211_FCTL_PM,
291362306a36Sopenharmony_ci			      fc & IEEE80211_FCTL_FTYPE,
291462306a36Sopenharmony_ci			      fc & IEEE80211_FCTL_STYPE);
291562306a36Sopenharmony_ci
291662306a36Sopenharmony_ci	atomic_dec(&sta->users);
291762306a36Sopenharmony_ci	return 0;
291862306a36Sopenharmony_ci}
291962306a36Sopenharmony_ci
292062306a36Sopenharmony_ci
292162306a36Sopenharmony_ci/* Called only as a tasklet (software IRQ). Called for each RX frame after
292262306a36Sopenharmony_ci * getting RX header and payload from hardware. */
292362306a36Sopenharmony_ciap_rx_ret hostap_handle_sta_rx(local_info_t *local, struct net_device *dev,
292462306a36Sopenharmony_ci			       struct sk_buff *skb,
292562306a36Sopenharmony_ci			       struct hostap_80211_rx_status *rx_stats,
292662306a36Sopenharmony_ci			       int wds)
292762306a36Sopenharmony_ci{
292862306a36Sopenharmony_ci	int ret;
292962306a36Sopenharmony_ci	struct sta_info *sta;
293062306a36Sopenharmony_ci	u16 fc, type, stype;
293162306a36Sopenharmony_ci	struct ieee80211_hdr *hdr;
293262306a36Sopenharmony_ci
293362306a36Sopenharmony_ci	if (local->ap == NULL)
293462306a36Sopenharmony_ci		return AP_RX_CONTINUE;
293562306a36Sopenharmony_ci
293662306a36Sopenharmony_ci	hdr = (struct ieee80211_hdr *) skb->data;
293762306a36Sopenharmony_ci
293862306a36Sopenharmony_ci	fc = le16_to_cpu(hdr->frame_control);
293962306a36Sopenharmony_ci	type = fc & IEEE80211_FCTL_FTYPE;
294062306a36Sopenharmony_ci	stype = fc & IEEE80211_FCTL_STYPE;
294162306a36Sopenharmony_ci
294262306a36Sopenharmony_ci	spin_lock(&local->ap->sta_table_lock);
294362306a36Sopenharmony_ci	sta = ap_get_sta(local->ap, hdr->addr2);
294462306a36Sopenharmony_ci	if (sta)
294562306a36Sopenharmony_ci		atomic_inc(&sta->users);
294662306a36Sopenharmony_ci	spin_unlock(&local->ap->sta_table_lock);
294762306a36Sopenharmony_ci
294862306a36Sopenharmony_ci	if (sta && !(sta->flags & WLAN_STA_AUTHORIZED))
294962306a36Sopenharmony_ci		ret = AP_RX_CONTINUE_NOT_AUTHORIZED;
295062306a36Sopenharmony_ci	else
295162306a36Sopenharmony_ci		ret = AP_RX_CONTINUE;
295262306a36Sopenharmony_ci
295362306a36Sopenharmony_ci
295462306a36Sopenharmony_ci	if (fc & IEEE80211_FCTL_TODS) {
295562306a36Sopenharmony_ci		if (!wds && (sta == NULL || !(sta->flags & WLAN_STA_ASSOC))) {
295662306a36Sopenharmony_ci			if (local->hostapd) {
295762306a36Sopenharmony_ci				prism2_rx_80211(local->apdev, skb, rx_stats,
295862306a36Sopenharmony_ci						PRISM2_RX_NON_ASSOC);
295962306a36Sopenharmony_ci#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT
296062306a36Sopenharmony_ci			} else {
296162306a36Sopenharmony_ci				printk(KERN_DEBUG "%s: dropped received packet"
296262306a36Sopenharmony_ci				       " from non-associated STA %pM"
296362306a36Sopenharmony_ci				       " (type=0x%02x, subtype=0x%02x)\n",
296462306a36Sopenharmony_ci				       dev->name, hdr->addr2,
296562306a36Sopenharmony_ci				       type >> 2, stype >> 4);
296662306a36Sopenharmony_ci				hostap_rx(dev, skb, rx_stats);
296762306a36Sopenharmony_ci#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */
296862306a36Sopenharmony_ci			}
296962306a36Sopenharmony_ci			ret = AP_RX_EXIT;
297062306a36Sopenharmony_ci			goto out;
297162306a36Sopenharmony_ci		}
297262306a36Sopenharmony_ci	} else if (fc & IEEE80211_FCTL_FROMDS) {
297362306a36Sopenharmony_ci		if (!wds) {
297462306a36Sopenharmony_ci			/* FromDS frame - not for us; probably
297562306a36Sopenharmony_ci			 * broadcast/multicast in another BSS - drop */
297662306a36Sopenharmony_ci			if (ether_addr_equal(hdr->addr1, dev->dev_addr)) {
297762306a36Sopenharmony_ci				printk(KERN_DEBUG "Odd.. FromDS packet "
297862306a36Sopenharmony_ci				       "received with own BSSID\n");
297962306a36Sopenharmony_ci				hostap_dump_rx_80211(dev->name, skb, rx_stats);
298062306a36Sopenharmony_ci			}
298162306a36Sopenharmony_ci			ret = AP_RX_DROP;
298262306a36Sopenharmony_ci			goto out;
298362306a36Sopenharmony_ci		}
298462306a36Sopenharmony_ci	} else if (stype == IEEE80211_STYPE_NULLFUNC && sta == NULL &&
298562306a36Sopenharmony_ci		   ether_addr_equal(hdr->addr1, dev->dev_addr)) {
298662306a36Sopenharmony_ci
298762306a36Sopenharmony_ci		if (local->hostapd) {
298862306a36Sopenharmony_ci			prism2_rx_80211(local->apdev, skb, rx_stats,
298962306a36Sopenharmony_ci					PRISM2_RX_NON_ASSOC);
299062306a36Sopenharmony_ci#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT
299162306a36Sopenharmony_ci		} else {
299262306a36Sopenharmony_ci			/* At least Lucent f/w seems to send data::nullfunc
299362306a36Sopenharmony_ci			 * frames with no ToDS flag when the current AP returns
299462306a36Sopenharmony_ci			 * after being unavailable for some time. Speed up
299562306a36Sopenharmony_ci			 * re-association by informing the station about it not
299662306a36Sopenharmony_ci			 * being associated. */
299762306a36Sopenharmony_ci			printk(KERN_DEBUG "%s: rejected received nullfunc frame"
299862306a36Sopenharmony_ci			       " without ToDS from not associated STA %pM\n",
299962306a36Sopenharmony_ci			       dev->name, hdr->addr2);
300062306a36Sopenharmony_ci			hostap_rx(dev, skb, rx_stats);
300162306a36Sopenharmony_ci#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */
300262306a36Sopenharmony_ci		}
300362306a36Sopenharmony_ci		ret = AP_RX_EXIT;
300462306a36Sopenharmony_ci		goto out;
300562306a36Sopenharmony_ci	} else if (stype == IEEE80211_STYPE_NULLFUNC) {
300662306a36Sopenharmony_ci		/* At least Lucent cards seem to send periodic nullfunc
300762306a36Sopenharmony_ci		 * frames with ToDS. Let these through to update SQ
300862306a36Sopenharmony_ci		 * stats and PS state. Nullfunc frames do not contain
300962306a36Sopenharmony_ci		 * any data and they will be dropped below. */
301062306a36Sopenharmony_ci	} else {
301162306a36Sopenharmony_ci		/* If BSSID (Addr3) is foreign, this frame is a normal
301262306a36Sopenharmony_ci		 * broadcast frame from an IBSS network. Drop it silently.
301362306a36Sopenharmony_ci		 * If BSSID is own, report the dropping of this frame. */
301462306a36Sopenharmony_ci		if (ether_addr_equal(hdr->addr3, dev->dev_addr)) {
301562306a36Sopenharmony_ci			printk(KERN_DEBUG "%s: dropped received packet from %pM"
301662306a36Sopenharmony_ci			       " with no ToDS flag "
301762306a36Sopenharmony_ci			       "(type=0x%02x, subtype=0x%02x)\n", dev->name,
301862306a36Sopenharmony_ci			       hdr->addr2, type >> 2, stype >> 4);
301962306a36Sopenharmony_ci			hostap_dump_rx_80211(dev->name, skb, rx_stats);
302062306a36Sopenharmony_ci		}
302162306a36Sopenharmony_ci		ret = AP_RX_DROP;
302262306a36Sopenharmony_ci		goto out;
302362306a36Sopenharmony_ci	}
302462306a36Sopenharmony_ci
302562306a36Sopenharmony_ci	if (sta) {
302662306a36Sopenharmony_ci		hostap_update_sta_ps2(local, sta, fc & IEEE80211_FCTL_PM,
302762306a36Sopenharmony_ci				      type, stype);
302862306a36Sopenharmony_ci
302962306a36Sopenharmony_ci		sta->rx_packets++;
303062306a36Sopenharmony_ci		sta->rx_bytes += skb->len;
303162306a36Sopenharmony_ci		sta->last_rx = jiffies;
303262306a36Sopenharmony_ci	}
303362306a36Sopenharmony_ci
303462306a36Sopenharmony_ci	if (local->ap->nullfunc_ack && stype == IEEE80211_STYPE_NULLFUNC &&
303562306a36Sopenharmony_ci	    fc & IEEE80211_FCTL_TODS) {
303662306a36Sopenharmony_ci		if (local->hostapd) {
303762306a36Sopenharmony_ci			prism2_rx_80211(local->apdev, skb, rx_stats,
303862306a36Sopenharmony_ci					PRISM2_RX_NULLFUNC_ACK);
303962306a36Sopenharmony_ci#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT
304062306a36Sopenharmony_ci		} else {
304162306a36Sopenharmony_ci			/* some STA f/w's seem to require control::ACK frame
304262306a36Sopenharmony_ci			 * for data::nullfunc, but Prism2 f/w 0.8.0 (at least
304362306a36Sopenharmony_ci			 * from Compaq) does not send this.. Try to generate
304462306a36Sopenharmony_ci			 * ACK for these frames from the host driver to make
304562306a36Sopenharmony_ci			 * power saving work with, e.g., Lucent WaveLAN f/w */
304662306a36Sopenharmony_ci			hostap_rx(dev, skb, rx_stats);
304762306a36Sopenharmony_ci#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */
304862306a36Sopenharmony_ci		}
304962306a36Sopenharmony_ci		ret = AP_RX_EXIT;
305062306a36Sopenharmony_ci		goto out;
305162306a36Sopenharmony_ci	}
305262306a36Sopenharmony_ci
305362306a36Sopenharmony_ci out:
305462306a36Sopenharmony_ci	if (sta)
305562306a36Sopenharmony_ci		atomic_dec(&sta->users);
305662306a36Sopenharmony_ci
305762306a36Sopenharmony_ci	return ret;
305862306a36Sopenharmony_ci}
305962306a36Sopenharmony_ci
306062306a36Sopenharmony_ci
306162306a36Sopenharmony_ci/* Called only as a tasklet (software IRQ) */
306262306a36Sopenharmony_ciint hostap_handle_sta_crypto(local_info_t *local,
306362306a36Sopenharmony_ci			     struct ieee80211_hdr *hdr,
306462306a36Sopenharmony_ci			     struct lib80211_crypt_data **crypt,
306562306a36Sopenharmony_ci			     void **sta_ptr)
306662306a36Sopenharmony_ci{
306762306a36Sopenharmony_ci	struct sta_info *sta;
306862306a36Sopenharmony_ci
306962306a36Sopenharmony_ci	spin_lock(&local->ap->sta_table_lock);
307062306a36Sopenharmony_ci	sta = ap_get_sta(local->ap, hdr->addr2);
307162306a36Sopenharmony_ci	if (sta)
307262306a36Sopenharmony_ci		atomic_inc(&sta->users);
307362306a36Sopenharmony_ci	spin_unlock(&local->ap->sta_table_lock);
307462306a36Sopenharmony_ci
307562306a36Sopenharmony_ci	if (!sta)
307662306a36Sopenharmony_ci		return -1;
307762306a36Sopenharmony_ci
307862306a36Sopenharmony_ci	if (sta->crypt) {
307962306a36Sopenharmony_ci		*crypt = sta->crypt;
308062306a36Sopenharmony_ci		*sta_ptr = sta;
308162306a36Sopenharmony_ci		/* hostap_handle_sta_release() will be called to release STA
308262306a36Sopenharmony_ci		 * info */
308362306a36Sopenharmony_ci	} else
308462306a36Sopenharmony_ci		atomic_dec(&sta->users);
308562306a36Sopenharmony_ci
308662306a36Sopenharmony_ci	return 0;
308762306a36Sopenharmony_ci}
308862306a36Sopenharmony_ci
308962306a36Sopenharmony_ci
309062306a36Sopenharmony_ci/* Called only as a tasklet (software IRQ) */
309162306a36Sopenharmony_ciint hostap_is_sta_assoc(struct ap_data *ap, u8 *sta_addr)
309262306a36Sopenharmony_ci{
309362306a36Sopenharmony_ci	struct sta_info *sta;
309462306a36Sopenharmony_ci	int ret = 0;
309562306a36Sopenharmony_ci
309662306a36Sopenharmony_ci	spin_lock(&ap->sta_table_lock);
309762306a36Sopenharmony_ci	sta = ap_get_sta(ap, sta_addr);
309862306a36Sopenharmony_ci	if (sta != NULL && (sta->flags & WLAN_STA_ASSOC) && !sta->ap)
309962306a36Sopenharmony_ci		ret = 1;
310062306a36Sopenharmony_ci	spin_unlock(&ap->sta_table_lock);
310162306a36Sopenharmony_ci
310262306a36Sopenharmony_ci	return ret;
310362306a36Sopenharmony_ci}
310462306a36Sopenharmony_ci
310562306a36Sopenharmony_ci
310662306a36Sopenharmony_ci/* Called only as a tasklet (software IRQ) */
310762306a36Sopenharmony_ciint hostap_is_sta_authorized(struct ap_data *ap, u8 *sta_addr)
310862306a36Sopenharmony_ci{
310962306a36Sopenharmony_ci	struct sta_info *sta;
311062306a36Sopenharmony_ci	int ret = 0;
311162306a36Sopenharmony_ci
311262306a36Sopenharmony_ci	spin_lock(&ap->sta_table_lock);
311362306a36Sopenharmony_ci	sta = ap_get_sta(ap, sta_addr);
311462306a36Sopenharmony_ci	if (sta != NULL && (sta->flags & WLAN_STA_ASSOC) && !sta->ap &&
311562306a36Sopenharmony_ci	    ((sta->flags & WLAN_STA_AUTHORIZED) ||
311662306a36Sopenharmony_ci	     ap->local->ieee_802_1x == 0))
311762306a36Sopenharmony_ci		ret = 1;
311862306a36Sopenharmony_ci	spin_unlock(&ap->sta_table_lock);
311962306a36Sopenharmony_ci
312062306a36Sopenharmony_ci	return ret;
312162306a36Sopenharmony_ci}
312262306a36Sopenharmony_ci
312362306a36Sopenharmony_ci
312462306a36Sopenharmony_ci/* Called only as a tasklet (software IRQ) */
312562306a36Sopenharmony_ciint hostap_add_sta(struct ap_data *ap, u8 *sta_addr)
312662306a36Sopenharmony_ci{
312762306a36Sopenharmony_ci	struct sta_info *sta;
312862306a36Sopenharmony_ci	int ret = 1;
312962306a36Sopenharmony_ci
313062306a36Sopenharmony_ci	if (!ap)
313162306a36Sopenharmony_ci		return -1;
313262306a36Sopenharmony_ci
313362306a36Sopenharmony_ci	spin_lock(&ap->sta_table_lock);
313462306a36Sopenharmony_ci	sta = ap_get_sta(ap, sta_addr);
313562306a36Sopenharmony_ci	if (sta)
313662306a36Sopenharmony_ci		ret = 0;
313762306a36Sopenharmony_ci	spin_unlock(&ap->sta_table_lock);
313862306a36Sopenharmony_ci
313962306a36Sopenharmony_ci	if (ret == 1) {
314062306a36Sopenharmony_ci		sta = ap_add_sta(ap, sta_addr);
314162306a36Sopenharmony_ci		if (!sta)
314262306a36Sopenharmony_ci			return -1;
314362306a36Sopenharmony_ci		sta->flags = WLAN_STA_AUTH | WLAN_STA_ASSOC;
314462306a36Sopenharmony_ci		sta->ap = 1;
314562306a36Sopenharmony_ci		memset(sta->supported_rates, 0, sizeof(sta->supported_rates));
314662306a36Sopenharmony_ci		/* No way of knowing which rates are supported since we did not
314762306a36Sopenharmony_ci		 * get supported rates element from beacon/assoc req. Assume
314862306a36Sopenharmony_ci		 * that remote end supports all 802.11b rates. */
314962306a36Sopenharmony_ci		sta->supported_rates[0] = 0x82;
315062306a36Sopenharmony_ci		sta->supported_rates[1] = 0x84;
315162306a36Sopenharmony_ci		sta->supported_rates[2] = 0x0b;
315262306a36Sopenharmony_ci		sta->supported_rates[3] = 0x16;
315362306a36Sopenharmony_ci		sta->tx_supp_rates = WLAN_RATE_1M | WLAN_RATE_2M |
315462306a36Sopenharmony_ci			WLAN_RATE_5M5 | WLAN_RATE_11M;
315562306a36Sopenharmony_ci		sta->tx_rate = 110;
315662306a36Sopenharmony_ci		sta->tx_max_rate = sta->tx_rate_idx = 3;
315762306a36Sopenharmony_ci	}
315862306a36Sopenharmony_ci
315962306a36Sopenharmony_ci	return ret;
316062306a36Sopenharmony_ci}
316162306a36Sopenharmony_ci
316262306a36Sopenharmony_ci
316362306a36Sopenharmony_ci/* Called only as a tasklet (software IRQ) */
316462306a36Sopenharmony_ciint hostap_update_rx_stats(struct ap_data *ap,
316562306a36Sopenharmony_ci			   struct ieee80211_hdr *hdr,
316662306a36Sopenharmony_ci			   struct hostap_80211_rx_status *rx_stats)
316762306a36Sopenharmony_ci{
316862306a36Sopenharmony_ci	struct sta_info *sta;
316962306a36Sopenharmony_ci
317062306a36Sopenharmony_ci	if (!ap)
317162306a36Sopenharmony_ci		return -1;
317262306a36Sopenharmony_ci
317362306a36Sopenharmony_ci	spin_lock(&ap->sta_table_lock);
317462306a36Sopenharmony_ci	sta = ap_get_sta(ap, hdr->addr2);
317562306a36Sopenharmony_ci	if (sta) {
317662306a36Sopenharmony_ci		sta->last_rx_silence = rx_stats->noise;
317762306a36Sopenharmony_ci		sta->last_rx_signal = rx_stats->signal;
317862306a36Sopenharmony_ci		sta->last_rx_rate = rx_stats->rate;
317962306a36Sopenharmony_ci		sta->last_rx_updated = IW_QUAL_ALL_UPDATED | IW_QUAL_DBM;
318062306a36Sopenharmony_ci		if (rx_stats->rate == 10)
318162306a36Sopenharmony_ci			sta->rx_count[0]++;
318262306a36Sopenharmony_ci		else if (rx_stats->rate == 20)
318362306a36Sopenharmony_ci			sta->rx_count[1]++;
318462306a36Sopenharmony_ci		else if (rx_stats->rate == 55)
318562306a36Sopenharmony_ci			sta->rx_count[2]++;
318662306a36Sopenharmony_ci		else if (rx_stats->rate == 110)
318762306a36Sopenharmony_ci			sta->rx_count[3]++;
318862306a36Sopenharmony_ci	}
318962306a36Sopenharmony_ci	spin_unlock(&ap->sta_table_lock);
319062306a36Sopenharmony_ci
319162306a36Sopenharmony_ci	return sta ? 0 : -1;
319262306a36Sopenharmony_ci}
319362306a36Sopenharmony_ci
319462306a36Sopenharmony_ci
319562306a36Sopenharmony_civoid hostap_update_rates(local_info_t *local)
319662306a36Sopenharmony_ci{
319762306a36Sopenharmony_ci	struct sta_info *sta;
319862306a36Sopenharmony_ci	struct ap_data *ap = local->ap;
319962306a36Sopenharmony_ci
320062306a36Sopenharmony_ci	if (!ap)
320162306a36Sopenharmony_ci		return;
320262306a36Sopenharmony_ci
320362306a36Sopenharmony_ci	spin_lock_bh(&ap->sta_table_lock);
320462306a36Sopenharmony_ci	list_for_each_entry(sta, &ap->sta_list, list) {
320562306a36Sopenharmony_ci		prism2_check_tx_rates(sta);
320662306a36Sopenharmony_ci	}
320762306a36Sopenharmony_ci	spin_unlock_bh(&ap->sta_table_lock);
320862306a36Sopenharmony_ci}
320962306a36Sopenharmony_ci
321062306a36Sopenharmony_ci
321162306a36Sopenharmony_civoid * ap_crypt_get_ptrs(struct ap_data *ap, u8 *addr, int permanent,
321262306a36Sopenharmony_ci			 struct lib80211_crypt_data ***crypt)
321362306a36Sopenharmony_ci{
321462306a36Sopenharmony_ci	struct sta_info *sta;
321562306a36Sopenharmony_ci
321662306a36Sopenharmony_ci	spin_lock_bh(&ap->sta_table_lock);
321762306a36Sopenharmony_ci	sta = ap_get_sta(ap, addr);
321862306a36Sopenharmony_ci	if (sta)
321962306a36Sopenharmony_ci		atomic_inc(&sta->users);
322062306a36Sopenharmony_ci	spin_unlock_bh(&ap->sta_table_lock);
322162306a36Sopenharmony_ci
322262306a36Sopenharmony_ci	if (!sta && permanent)
322362306a36Sopenharmony_ci		sta = ap_add_sta(ap, addr);
322462306a36Sopenharmony_ci
322562306a36Sopenharmony_ci	if (!sta)
322662306a36Sopenharmony_ci		return NULL;
322762306a36Sopenharmony_ci
322862306a36Sopenharmony_ci	if (permanent)
322962306a36Sopenharmony_ci		sta->flags |= WLAN_STA_PERM;
323062306a36Sopenharmony_ci
323162306a36Sopenharmony_ci	*crypt = &sta->crypt;
323262306a36Sopenharmony_ci
323362306a36Sopenharmony_ci	return sta;
323462306a36Sopenharmony_ci}
323562306a36Sopenharmony_ci
323662306a36Sopenharmony_ci
323762306a36Sopenharmony_civoid hostap_add_wds_links(local_info_t *local)
323862306a36Sopenharmony_ci{
323962306a36Sopenharmony_ci	struct ap_data *ap = local->ap;
324062306a36Sopenharmony_ci	struct sta_info *sta;
324162306a36Sopenharmony_ci
324262306a36Sopenharmony_ci	spin_lock_bh(&ap->sta_table_lock);
324362306a36Sopenharmony_ci	list_for_each_entry(sta, &ap->sta_list, list) {
324462306a36Sopenharmony_ci		if (sta->ap)
324562306a36Sopenharmony_ci			hostap_wds_link_oper(local, sta->addr, WDS_ADD);
324662306a36Sopenharmony_ci	}
324762306a36Sopenharmony_ci	spin_unlock_bh(&ap->sta_table_lock);
324862306a36Sopenharmony_ci
324962306a36Sopenharmony_ci	schedule_work(&local->ap->wds_oper_queue);
325062306a36Sopenharmony_ci}
325162306a36Sopenharmony_ci
325262306a36Sopenharmony_ci
325362306a36Sopenharmony_civoid hostap_wds_link_oper(local_info_t *local, u8 *addr, wds_oper_type type)
325462306a36Sopenharmony_ci{
325562306a36Sopenharmony_ci	struct wds_oper_data *entry;
325662306a36Sopenharmony_ci
325762306a36Sopenharmony_ci	entry = kmalloc(sizeof(*entry), GFP_ATOMIC);
325862306a36Sopenharmony_ci	if (!entry)
325962306a36Sopenharmony_ci		return;
326062306a36Sopenharmony_ci	memcpy(entry->addr, addr, ETH_ALEN);
326162306a36Sopenharmony_ci	entry->type = type;
326262306a36Sopenharmony_ci	spin_lock_bh(&local->lock);
326362306a36Sopenharmony_ci	entry->next = local->ap->wds_oper_entries;
326462306a36Sopenharmony_ci	local->ap->wds_oper_entries = entry;
326562306a36Sopenharmony_ci	spin_unlock_bh(&local->lock);
326662306a36Sopenharmony_ci
326762306a36Sopenharmony_ci	schedule_work(&local->ap->wds_oper_queue);
326862306a36Sopenharmony_ci}
326962306a36Sopenharmony_ci
327062306a36Sopenharmony_ci
327162306a36Sopenharmony_ciEXPORT_SYMBOL(hostap_init_data);
327262306a36Sopenharmony_ciEXPORT_SYMBOL(hostap_init_ap_proc);
327362306a36Sopenharmony_ciEXPORT_SYMBOL(hostap_free_data);
327462306a36Sopenharmony_ciEXPORT_SYMBOL(hostap_check_sta_fw_version);
327562306a36Sopenharmony_ciEXPORT_SYMBOL(hostap_handle_sta_tx_exc);
327662306a36Sopenharmony_ci#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT
327762306a36Sopenharmony_ci#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */
3278