18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Intersil Prism2 driver with Host AP (software access point) support
48c2ecf20Sopenharmony_ci * Copyright (c) 2001-2002, SSH Communications Security Corp and Jouni Malinen
58c2ecf20Sopenharmony_ci * <j@w1.fi>
68c2ecf20Sopenharmony_ci * Copyright (c) 2002-2005, Jouni Malinen <j@w1.fi>
78c2ecf20Sopenharmony_ci *
88c2ecf20Sopenharmony_ci * This file is to be included into hostap.c when S/W AP functionality is
98c2ecf20Sopenharmony_ci * compiled.
108c2ecf20Sopenharmony_ci *
118c2ecf20Sopenharmony_ci * AP:  FIX:
128c2ecf20Sopenharmony_ci * - if unicast Class 2 (assoc,reassoc,disassoc) frame received from
138c2ecf20Sopenharmony_ci *   unauthenticated STA, send deauth. frame (8802.11: 5.5)
148c2ecf20Sopenharmony_ci * - if unicast Class 3 (data with to/from DS,deauth,pspoll) frame received
158c2ecf20Sopenharmony_ci *   from authenticated, but unassoc STA, send disassoc frame (8802.11: 5.5)
168c2ecf20Sopenharmony_ci * - if unicast Class 3 received from unauthenticated STA, send deauth. frame
178c2ecf20Sopenharmony_ci *   (8802.11: 5.5)
188c2ecf20Sopenharmony_ci */
198c2ecf20Sopenharmony_ci
208c2ecf20Sopenharmony_ci#include <linux/proc_fs.h>
218c2ecf20Sopenharmony_ci#include <linux/seq_file.h>
228c2ecf20Sopenharmony_ci#include <linux/delay.h>
238c2ecf20Sopenharmony_ci#include <linux/random.h>
248c2ecf20Sopenharmony_ci#include <linux/if_arp.h>
258c2ecf20Sopenharmony_ci#include <linux/slab.h>
268c2ecf20Sopenharmony_ci#include <linux/export.h>
278c2ecf20Sopenharmony_ci#include <linux/moduleparam.h>
288c2ecf20Sopenharmony_ci#include <linux/etherdevice.h>
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_ci#include "hostap_wlan.h"
318c2ecf20Sopenharmony_ci#include "hostap.h"
328c2ecf20Sopenharmony_ci#include "hostap_ap.h"
338c2ecf20Sopenharmony_ci
348c2ecf20Sopenharmony_cistatic int other_ap_policy[MAX_PARM_DEVICES] = { AP_OTHER_AP_SKIP_ALL,
358c2ecf20Sopenharmony_ci						 DEF_INTS };
368c2ecf20Sopenharmony_cimodule_param_array(other_ap_policy, int, NULL, 0444);
378c2ecf20Sopenharmony_ciMODULE_PARM_DESC(other_ap_policy, "Other AP beacon monitoring policy (0-3)");
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_cistatic int ap_max_inactivity[MAX_PARM_DEVICES] = { AP_MAX_INACTIVITY_SEC,
408c2ecf20Sopenharmony_ci						   DEF_INTS };
418c2ecf20Sopenharmony_cimodule_param_array(ap_max_inactivity, int, NULL, 0444);
428c2ecf20Sopenharmony_ciMODULE_PARM_DESC(ap_max_inactivity, "AP timeout (in seconds) for station "
438c2ecf20Sopenharmony_ci		 "inactivity");
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_cistatic int ap_bridge_packets[MAX_PARM_DEVICES] = { 1, DEF_INTS };
468c2ecf20Sopenharmony_cimodule_param_array(ap_bridge_packets, int, NULL, 0444);
478c2ecf20Sopenharmony_ciMODULE_PARM_DESC(ap_bridge_packets, "Bridge packets directly between "
488c2ecf20Sopenharmony_ci		 "stations");
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_cistatic int autom_ap_wds[MAX_PARM_DEVICES] = { 0, DEF_INTS };
518c2ecf20Sopenharmony_cimodule_param_array(autom_ap_wds, int, NULL, 0444);
528c2ecf20Sopenharmony_ciMODULE_PARM_DESC(autom_ap_wds, "Add WDS connections to other APs "
538c2ecf20Sopenharmony_ci		 "automatically");
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_ci
568c2ecf20Sopenharmony_cistatic struct sta_info* ap_get_sta(struct ap_data *ap, u8 *sta);
578c2ecf20Sopenharmony_cistatic void hostap_event_expired_sta(struct net_device *dev,
588c2ecf20Sopenharmony_ci				     struct sta_info *sta);
598c2ecf20Sopenharmony_cistatic void handle_add_proc_queue(struct work_struct *work);
608c2ecf20Sopenharmony_ci
618c2ecf20Sopenharmony_ci#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT
628c2ecf20Sopenharmony_cistatic void handle_wds_oper_queue(struct work_struct *work);
638c2ecf20Sopenharmony_cistatic void prism2_send_mgmt(struct net_device *dev,
648c2ecf20Sopenharmony_ci			     u16 type_subtype, char *body,
658c2ecf20Sopenharmony_ci			     int body_len, u8 *addr, u16 tx_cb_idx);
668c2ecf20Sopenharmony_ci#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_ci#if !defined(PRISM2_NO_PROCFS_DEBUG) && defined(CONFIG_PROC_FS)
708c2ecf20Sopenharmony_cistatic int ap_debug_proc_show(struct seq_file *m, void *v)
718c2ecf20Sopenharmony_ci{
728c2ecf20Sopenharmony_ci	struct ap_data *ap = PDE_DATA(file_inode(m->file));
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_ci	seq_printf(m, "BridgedUnicastFrames=%u\n", ap->bridged_unicast);
758c2ecf20Sopenharmony_ci	seq_printf(m, "BridgedMulticastFrames=%u\n", ap->bridged_multicast);
768c2ecf20Sopenharmony_ci	seq_printf(m, "max_inactivity=%u\n", ap->max_inactivity / HZ);
778c2ecf20Sopenharmony_ci	seq_printf(m, "bridge_packets=%u\n", ap->bridge_packets);
788c2ecf20Sopenharmony_ci	seq_printf(m, "nullfunc_ack=%u\n", ap->nullfunc_ack);
798c2ecf20Sopenharmony_ci	seq_printf(m, "autom_ap_wds=%u\n", ap->autom_ap_wds);
808c2ecf20Sopenharmony_ci	seq_printf(m, "auth_algs=%u\n", ap->local->auth_algs);
818c2ecf20Sopenharmony_ci	seq_printf(m, "tx_drop_nonassoc=%u\n", ap->tx_drop_nonassoc);
828c2ecf20Sopenharmony_ci	return 0;
838c2ecf20Sopenharmony_ci}
848c2ecf20Sopenharmony_ci#endif
858c2ecf20Sopenharmony_ci
868c2ecf20Sopenharmony_cistatic void ap_sta_hash_add(struct ap_data *ap, struct sta_info *sta)
878c2ecf20Sopenharmony_ci{
888c2ecf20Sopenharmony_ci	sta->hnext = ap->sta_hash[STA_HASH(sta->addr)];
898c2ecf20Sopenharmony_ci	ap->sta_hash[STA_HASH(sta->addr)] = sta;
908c2ecf20Sopenharmony_ci}
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_cistatic void ap_sta_hash_del(struct ap_data *ap, struct sta_info *sta)
938c2ecf20Sopenharmony_ci{
948c2ecf20Sopenharmony_ci	struct sta_info *s;
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_ci	s = ap->sta_hash[STA_HASH(sta->addr)];
978c2ecf20Sopenharmony_ci	if (s == NULL) return;
988c2ecf20Sopenharmony_ci	if (ether_addr_equal(s->addr, sta->addr)) {
998c2ecf20Sopenharmony_ci		ap->sta_hash[STA_HASH(sta->addr)] = s->hnext;
1008c2ecf20Sopenharmony_ci		return;
1018c2ecf20Sopenharmony_ci	}
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_ci	while (s->hnext != NULL && !ether_addr_equal(s->hnext->addr, sta->addr))
1048c2ecf20Sopenharmony_ci		s = s->hnext;
1058c2ecf20Sopenharmony_ci	if (s->hnext != NULL)
1068c2ecf20Sopenharmony_ci		s->hnext = s->hnext->hnext;
1078c2ecf20Sopenharmony_ci	else
1088c2ecf20Sopenharmony_ci		printk("AP: could not remove STA %pM from hash table\n",
1098c2ecf20Sopenharmony_ci		       sta->addr);
1108c2ecf20Sopenharmony_ci}
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_cistatic void ap_free_sta(struct ap_data *ap, struct sta_info *sta)
1138c2ecf20Sopenharmony_ci{
1148c2ecf20Sopenharmony_ci	if (sta->ap && sta->local)
1158c2ecf20Sopenharmony_ci		hostap_event_expired_sta(sta->local->dev, sta);
1168c2ecf20Sopenharmony_ci
1178c2ecf20Sopenharmony_ci	if (ap->proc != NULL) {
1188c2ecf20Sopenharmony_ci		char name[20];
1198c2ecf20Sopenharmony_ci		sprintf(name, "%pM", sta->addr);
1208c2ecf20Sopenharmony_ci		remove_proc_entry(name, ap->proc);
1218c2ecf20Sopenharmony_ci	}
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_ci	if (sta->crypt) {
1248c2ecf20Sopenharmony_ci		sta->crypt->ops->deinit(sta->crypt->priv);
1258c2ecf20Sopenharmony_ci		kfree(sta->crypt);
1268c2ecf20Sopenharmony_ci		sta->crypt = NULL;
1278c2ecf20Sopenharmony_ci	}
1288c2ecf20Sopenharmony_ci
1298c2ecf20Sopenharmony_ci	skb_queue_purge(&sta->tx_buf);
1308c2ecf20Sopenharmony_ci
1318c2ecf20Sopenharmony_ci	ap->num_sta--;
1328c2ecf20Sopenharmony_ci#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT
1338c2ecf20Sopenharmony_ci	if (sta->aid > 0)
1348c2ecf20Sopenharmony_ci		ap->sta_aid[sta->aid - 1] = NULL;
1358c2ecf20Sopenharmony_ci
1368c2ecf20Sopenharmony_ci	if (!sta->ap)
1378c2ecf20Sopenharmony_ci		kfree(sta->u.sta.challenge);
1388c2ecf20Sopenharmony_ci	del_timer_sync(&sta->timer);
1398c2ecf20Sopenharmony_ci#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */
1408c2ecf20Sopenharmony_ci
1418c2ecf20Sopenharmony_ci	kfree(sta);
1428c2ecf20Sopenharmony_ci}
1438c2ecf20Sopenharmony_ci
1448c2ecf20Sopenharmony_ci
1458c2ecf20Sopenharmony_cistatic void hostap_set_tim(local_info_t *local, int aid, int set)
1468c2ecf20Sopenharmony_ci{
1478c2ecf20Sopenharmony_ci	if (local->func->set_tim)
1488c2ecf20Sopenharmony_ci		local->func->set_tim(local->dev, aid, set);
1498c2ecf20Sopenharmony_ci}
1508c2ecf20Sopenharmony_ci
1518c2ecf20Sopenharmony_ci
1528c2ecf20Sopenharmony_cistatic void hostap_event_new_sta(struct net_device *dev, struct sta_info *sta)
1538c2ecf20Sopenharmony_ci{
1548c2ecf20Sopenharmony_ci	union iwreq_data wrqu;
1558c2ecf20Sopenharmony_ci	memset(&wrqu, 0, sizeof(wrqu));
1568c2ecf20Sopenharmony_ci	memcpy(wrqu.addr.sa_data, sta->addr, ETH_ALEN);
1578c2ecf20Sopenharmony_ci	wrqu.addr.sa_family = ARPHRD_ETHER;
1588c2ecf20Sopenharmony_ci	wireless_send_event(dev, IWEVREGISTERED, &wrqu, NULL);
1598c2ecf20Sopenharmony_ci}
1608c2ecf20Sopenharmony_ci
1618c2ecf20Sopenharmony_ci
1628c2ecf20Sopenharmony_cistatic void hostap_event_expired_sta(struct net_device *dev,
1638c2ecf20Sopenharmony_ci				     struct sta_info *sta)
1648c2ecf20Sopenharmony_ci{
1658c2ecf20Sopenharmony_ci	union iwreq_data wrqu;
1668c2ecf20Sopenharmony_ci	memset(&wrqu, 0, sizeof(wrqu));
1678c2ecf20Sopenharmony_ci	memcpy(wrqu.addr.sa_data, sta->addr, ETH_ALEN);
1688c2ecf20Sopenharmony_ci	wrqu.addr.sa_family = ARPHRD_ETHER;
1698c2ecf20Sopenharmony_ci	wireless_send_event(dev, IWEVEXPIRED, &wrqu, NULL);
1708c2ecf20Sopenharmony_ci}
1718c2ecf20Sopenharmony_ci
1728c2ecf20Sopenharmony_ci
1738c2ecf20Sopenharmony_ci#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT
1748c2ecf20Sopenharmony_ci
1758c2ecf20Sopenharmony_cistatic void ap_handle_timer(struct timer_list *t)
1768c2ecf20Sopenharmony_ci{
1778c2ecf20Sopenharmony_ci	struct sta_info *sta = from_timer(sta, t, timer);
1788c2ecf20Sopenharmony_ci	local_info_t *local;
1798c2ecf20Sopenharmony_ci	struct ap_data *ap;
1808c2ecf20Sopenharmony_ci	unsigned long next_time = 0;
1818c2ecf20Sopenharmony_ci	int was_assoc;
1828c2ecf20Sopenharmony_ci
1838c2ecf20Sopenharmony_ci	if (sta == NULL || sta->local == NULL || sta->local->ap == NULL) {
1848c2ecf20Sopenharmony_ci		PDEBUG(DEBUG_AP, "ap_handle_timer() called with NULL data\n");
1858c2ecf20Sopenharmony_ci		return;
1868c2ecf20Sopenharmony_ci	}
1878c2ecf20Sopenharmony_ci
1888c2ecf20Sopenharmony_ci	local = sta->local;
1898c2ecf20Sopenharmony_ci	ap = local->ap;
1908c2ecf20Sopenharmony_ci	was_assoc = sta->flags & WLAN_STA_ASSOC;
1918c2ecf20Sopenharmony_ci
1928c2ecf20Sopenharmony_ci	if (atomic_read(&sta->users) != 0)
1938c2ecf20Sopenharmony_ci		next_time = jiffies + HZ;
1948c2ecf20Sopenharmony_ci	else if ((sta->flags & WLAN_STA_PERM) && !(sta->flags & WLAN_STA_AUTH))
1958c2ecf20Sopenharmony_ci		next_time = jiffies + ap->max_inactivity;
1968c2ecf20Sopenharmony_ci
1978c2ecf20Sopenharmony_ci	if (time_before(jiffies, sta->last_rx + ap->max_inactivity)) {
1988c2ecf20Sopenharmony_ci		/* station activity detected; reset timeout state */
1998c2ecf20Sopenharmony_ci		sta->timeout_next = STA_NULLFUNC;
2008c2ecf20Sopenharmony_ci		next_time = sta->last_rx + ap->max_inactivity;
2018c2ecf20Sopenharmony_ci	} else if (sta->timeout_next == STA_DISASSOC &&
2028c2ecf20Sopenharmony_ci		   !(sta->flags & WLAN_STA_PENDING_POLL)) {
2038c2ecf20Sopenharmony_ci		/* STA ACKed data nullfunc frame poll */
2048c2ecf20Sopenharmony_ci		sta->timeout_next = STA_NULLFUNC;
2058c2ecf20Sopenharmony_ci		next_time = jiffies + ap->max_inactivity;
2068c2ecf20Sopenharmony_ci	}
2078c2ecf20Sopenharmony_ci
2088c2ecf20Sopenharmony_ci	if (next_time) {
2098c2ecf20Sopenharmony_ci		sta->timer.expires = next_time;
2108c2ecf20Sopenharmony_ci		add_timer(&sta->timer);
2118c2ecf20Sopenharmony_ci		return;
2128c2ecf20Sopenharmony_ci	}
2138c2ecf20Sopenharmony_ci
2148c2ecf20Sopenharmony_ci	if (sta->ap)
2158c2ecf20Sopenharmony_ci		sta->timeout_next = STA_DEAUTH;
2168c2ecf20Sopenharmony_ci
2178c2ecf20Sopenharmony_ci	if (sta->timeout_next == STA_DEAUTH && !(sta->flags & WLAN_STA_PERM)) {
2188c2ecf20Sopenharmony_ci		spin_lock(&ap->sta_table_lock);
2198c2ecf20Sopenharmony_ci		ap_sta_hash_del(ap, sta);
2208c2ecf20Sopenharmony_ci		list_del(&sta->list);
2218c2ecf20Sopenharmony_ci		spin_unlock(&ap->sta_table_lock);
2228c2ecf20Sopenharmony_ci		sta->flags &= ~(WLAN_STA_AUTH | WLAN_STA_ASSOC);
2238c2ecf20Sopenharmony_ci	} else if (sta->timeout_next == STA_DISASSOC)
2248c2ecf20Sopenharmony_ci		sta->flags &= ~WLAN_STA_ASSOC;
2258c2ecf20Sopenharmony_ci
2268c2ecf20Sopenharmony_ci	if (was_assoc && !(sta->flags & WLAN_STA_ASSOC) && !sta->ap)
2278c2ecf20Sopenharmony_ci		hostap_event_expired_sta(local->dev, sta);
2288c2ecf20Sopenharmony_ci
2298c2ecf20Sopenharmony_ci	if (sta->timeout_next == STA_DEAUTH && sta->aid > 0 &&
2308c2ecf20Sopenharmony_ci	    !skb_queue_empty(&sta->tx_buf)) {
2318c2ecf20Sopenharmony_ci		hostap_set_tim(local, sta->aid, 0);
2328c2ecf20Sopenharmony_ci		sta->flags &= ~WLAN_STA_TIM;
2338c2ecf20Sopenharmony_ci	}
2348c2ecf20Sopenharmony_ci
2358c2ecf20Sopenharmony_ci	if (sta->ap) {
2368c2ecf20Sopenharmony_ci		if (ap->autom_ap_wds) {
2378c2ecf20Sopenharmony_ci			PDEBUG(DEBUG_AP, "%s: removing automatic WDS "
2388c2ecf20Sopenharmony_ci			       "connection to AP %pM\n",
2398c2ecf20Sopenharmony_ci			       local->dev->name, sta->addr);
2408c2ecf20Sopenharmony_ci			hostap_wds_link_oper(local, sta->addr, WDS_DEL);
2418c2ecf20Sopenharmony_ci		}
2428c2ecf20Sopenharmony_ci	} else if (sta->timeout_next == STA_NULLFUNC) {
2438c2ecf20Sopenharmony_ci		/* send data frame to poll STA and check whether this frame
2448c2ecf20Sopenharmony_ci		 * is ACKed */
2458c2ecf20Sopenharmony_ci		/* FIX: IEEE80211_STYPE_NULLFUNC would be more appropriate, but
2468c2ecf20Sopenharmony_ci		 * it is apparently not retried so TX Exc events are not
2478c2ecf20Sopenharmony_ci		 * received for it */
2488c2ecf20Sopenharmony_ci		sta->flags |= WLAN_STA_PENDING_POLL;
2498c2ecf20Sopenharmony_ci		prism2_send_mgmt(local->dev, IEEE80211_FTYPE_DATA |
2508c2ecf20Sopenharmony_ci				 IEEE80211_STYPE_DATA, NULL, 0,
2518c2ecf20Sopenharmony_ci				 sta->addr, ap->tx_callback_poll);
2528c2ecf20Sopenharmony_ci	} else {
2538c2ecf20Sopenharmony_ci		int deauth = sta->timeout_next == STA_DEAUTH;
2548c2ecf20Sopenharmony_ci		__le16 resp;
2558c2ecf20Sopenharmony_ci		PDEBUG(DEBUG_AP, "%s: sending %s info to STA %pM"
2568c2ecf20Sopenharmony_ci		       "(last=%lu, jiffies=%lu)\n",
2578c2ecf20Sopenharmony_ci		       local->dev->name,
2588c2ecf20Sopenharmony_ci		       deauth ? "deauthentication" : "disassociation",
2598c2ecf20Sopenharmony_ci		       sta->addr, sta->last_rx, jiffies);
2608c2ecf20Sopenharmony_ci
2618c2ecf20Sopenharmony_ci		resp = cpu_to_le16(deauth ? WLAN_REASON_PREV_AUTH_NOT_VALID :
2628c2ecf20Sopenharmony_ci				   WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY);
2638c2ecf20Sopenharmony_ci		prism2_send_mgmt(local->dev, IEEE80211_FTYPE_MGMT |
2648c2ecf20Sopenharmony_ci				 (deauth ? IEEE80211_STYPE_DEAUTH :
2658c2ecf20Sopenharmony_ci				  IEEE80211_STYPE_DISASSOC),
2668c2ecf20Sopenharmony_ci				 (char *) &resp, 2, sta->addr, 0);
2678c2ecf20Sopenharmony_ci	}
2688c2ecf20Sopenharmony_ci
2698c2ecf20Sopenharmony_ci	if (sta->timeout_next == STA_DEAUTH) {
2708c2ecf20Sopenharmony_ci		if (sta->flags & WLAN_STA_PERM) {
2718c2ecf20Sopenharmony_ci			PDEBUG(DEBUG_AP, "%s: STA %pM"
2728c2ecf20Sopenharmony_ci			       " would have been removed, "
2738c2ecf20Sopenharmony_ci			       "but it has 'perm' flag\n",
2748c2ecf20Sopenharmony_ci			       local->dev->name, sta->addr);
2758c2ecf20Sopenharmony_ci		} else
2768c2ecf20Sopenharmony_ci			ap_free_sta(ap, sta);
2778c2ecf20Sopenharmony_ci		return;
2788c2ecf20Sopenharmony_ci	}
2798c2ecf20Sopenharmony_ci
2808c2ecf20Sopenharmony_ci	if (sta->timeout_next == STA_NULLFUNC) {
2818c2ecf20Sopenharmony_ci		sta->timeout_next = STA_DISASSOC;
2828c2ecf20Sopenharmony_ci		sta->timer.expires = jiffies + AP_DISASSOC_DELAY;
2838c2ecf20Sopenharmony_ci	} else {
2848c2ecf20Sopenharmony_ci		sta->timeout_next = STA_DEAUTH;
2858c2ecf20Sopenharmony_ci		sta->timer.expires = jiffies + AP_DEAUTH_DELAY;
2868c2ecf20Sopenharmony_ci	}
2878c2ecf20Sopenharmony_ci
2888c2ecf20Sopenharmony_ci	add_timer(&sta->timer);
2898c2ecf20Sopenharmony_ci}
2908c2ecf20Sopenharmony_ci
2918c2ecf20Sopenharmony_ci
2928c2ecf20Sopenharmony_civoid hostap_deauth_all_stas(struct net_device *dev, struct ap_data *ap,
2938c2ecf20Sopenharmony_ci			    int resend)
2948c2ecf20Sopenharmony_ci{
2958c2ecf20Sopenharmony_ci	u8 addr[ETH_ALEN];
2968c2ecf20Sopenharmony_ci	__le16 resp;
2978c2ecf20Sopenharmony_ci	int i;
2988c2ecf20Sopenharmony_ci
2998c2ecf20Sopenharmony_ci	PDEBUG(DEBUG_AP, "%s: Deauthenticate all stations\n", dev->name);
3008c2ecf20Sopenharmony_ci	eth_broadcast_addr(addr);
3018c2ecf20Sopenharmony_ci
3028c2ecf20Sopenharmony_ci	resp = cpu_to_le16(WLAN_REASON_PREV_AUTH_NOT_VALID);
3038c2ecf20Sopenharmony_ci
3048c2ecf20Sopenharmony_ci	/* deauth message sent; try to resend it few times; the message is
3058c2ecf20Sopenharmony_ci	 * broadcast, so it may be delayed until next DTIM; there is not much
3068c2ecf20Sopenharmony_ci	 * else we can do at this point since the driver is going to be shut
3078c2ecf20Sopenharmony_ci	 * down */
3088c2ecf20Sopenharmony_ci	for (i = 0; i < 5; i++) {
3098c2ecf20Sopenharmony_ci		prism2_send_mgmt(dev, IEEE80211_FTYPE_MGMT |
3108c2ecf20Sopenharmony_ci				 IEEE80211_STYPE_DEAUTH,
3118c2ecf20Sopenharmony_ci				 (char *) &resp, 2, addr, 0);
3128c2ecf20Sopenharmony_ci
3138c2ecf20Sopenharmony_ci		if (!resend || ap->num_sta <= 0)
3148c2ecf20Sopenharmony_ci			return;
3158c2ecf20Sopenharmony_ci
3168c2ecf20Sopenharmony_ci		mdelay(50);
3178c2ecf20Sopenharmony_ci	}
3188c2ecf20Sopenharmony_ci}
3198c2ecf20Sopenharmony_ci
3208c2ecf20Sopenharmony_ci
3218c2ecf20Sopenharmony_cistatic int ap_control_proc_show(struct seq_file *m, void *v)
3228c2ecf20Sopenharmony_ci{
3238c2ecf20Sopenharmony_ci	struct ap_data *ap = PDE_DATA(file_inode(m->file));
3248c2ecf20Sopenharmony_ci	char *policy_txt;
3258c2ecf20Sopenharmony_ci	struct mac_entry *entry;
3268c2ecf20Sopenharmony_ci
3278c2ecf20Sopenharmony_ci	if (v == SEQ_START_TOKEN) {
3288c2ecf20Sopenharmony_ci		switch (ap->mac_restrictions.policy) {
3298c2ecf20Sopenharmony_ci		case MAC_POLICY_OPEN:
3308c2ecf20Sopenharmony_ci			policy_txt = "open";
3318c2ecf20Sopenharmony_ci			break;
3328c2ecf20Sopenharmony_ci		case MAC_POLICY_ALLOW:
3338c2ecf20Sopenharmony_ci			policy_txt = "allow";
3348c2ecf20Sopenharmony_ci			break;
3358c2ecf20Sopenharmony_ci		case MAC_POLICY_DENY:
3368c2ecf20Sopenharmony_ci			policy_txt = "deny";
3378c2ecf20Sopenharmony_ci			break;
3388c2ecf20Sopenharmony_ci		default:
3398c2ecf20Sopenharmony_ci			policy_txt = "unknown";
3408c2ecf20Sopenharmony_ci			break;
3418c2ecf20Sopenharmony_ci		}
3428c2ecf20Sopenharmony_ci		seq_printf(m, "MAC policy: %s\n", policy_txt);
3438c2ecf20Sopenharmony_ci		seq_printf(m, "MAC entries: %u\n", ap->mac_restrictions.entries);
3448c2ecf20Sopenharmony_ci		seq_puts(m, "MAC list:\n");
3458c2ecf20Sopenharmony_ci		return 0;
3468c2ecf20Sopenharmony_ci	}
3478c2ecf20Sopenharmony_ci
3488c2ecf20Sopenharmony_ci	entry = v;
3498c2ecf20Sopenharmony_ci	seq_printf(m, "%pM\n", entry->addr);
3508c2ecf20Sopenharmony_ci	return 0;
3518c2ecf20Sopenharmony_ci}
3528c2ecf20Sopenharmony_ci
3538c2ecf20Sopenharmony_cistatic void *ap_control_proc_start(struct seq_file *m, loff_t *_pos)
3548c2ecf20Sopenharmony_ci{
3558c2ecf20Sopenharmony_ci	struct ap_data *ap = PDE_DATA(file_inode(m->file));
3568c2ecf20Sopenharmony_ci	spin_lock_bh(&ap->mac_restrictions.lock);
3578c2ecf20Sopenharmony_ci	return seq_list_start_head(&ap->mac_restrictions.mac_list, *_pos);
3588c2ecf20Sopenharmony_ci}
3598c2ecf20Sopenharmony_ci
3608c2ecf20Sopenharmony_cistatic void *ap_control_proc_next(struct seq_file *m, void *v, loff_t *_pos)
3618c2ecf20Sopenharmony_ci{
3628c2ecf20Sopenharmony_ci	struct ap_data *ap = PDE_DATA(file_inode(m->file));
3638c2ecf20Sopenharmony_ci	return seq_list_next(v, &ap->mac_restrictions.mac_list, _pos);
3648c2ecf20Sopenharmony_ci}
3658c2ecf20Sopenharmony_ci
3668c2ecf20Sopenharmony_cistatic void ap_control_proc_stop(struct seq_file *m, void *v)
3678c2ecf20Sopenharmony_ci{
3688c2ecf20Sopenharmony_ci	struct ap_data *ap = PDE_DATA(file_inode(m->file));
3698c2ecf20Sopenharmony_ci	spin_unlock_bh(&ap->mac_restrictions.lock);
3708c2ecf20Sopenharmony_ci}
3718c2ecf20Sopenharmony_ci
3728c2ecf20Sopenharmony_cistatic const struct seq_operations ap_control_proc_seqops = {
3738c2ecf20Sopenharmony_ci	.start	= ap_control_proc_start,
3748c2ecf20Sopenharmony_ci	.next	= ap_control_proc_next,
3758c2ecf20Sopenharmony_ci	.stop	= ap_control_proc_stop,
3768c2ecf20Sopenharmony_ci	.show	= ap_control_proc_show,
3778c2ecf20Sopenharmony_ci};
3788c2ecf20Sopenharmony_ci
3798c2ecf20Sopenharmony_ciint ap_control_add_mac(struct mac_restrictions *mac_restrictions, u8 *mac)
3808c2ecf20Sopenharmony_ci{
3818c2ecf20Sopenharmony_ci	struct mac_entry *entry;
3828c2ecf20Sopenharmony_ci
3838c2ecf20Sopenharmony_ci	entry = kmalloc(sizeof(struct mac_entry), GFP_KERNEL);
3848c2ecf20Sopenharmony_ci	if (entry == NULL)
3858c2ecf20Sopenharmony_ci		return -ENOMEM;
3868c2ecf20Sopenharmony_ci
3878c2ecf20Sopenharmony_ci	memcpy(entry->addr, mac, ETH_ALEN);
3888c2ecf20Sopenharmony_ci
3898c2ecf20Sopenharmony_ci	spin_lock_bh(&mac_restrictions->lock);
3908c2ecf20Sopenharmony_ci	list_add_tail(&entry->list, &mac_restrictions->mac_list);
3918c2ecf20Sopenharmony_ci	mac_restrictions->entries++;
3928c2ecf20Sopenharmony_ci	spin_unlock_bh(&mac_restrictions->lock);
3938c2ecf20Sopenharmony_ci
3948c2ecf20Sopenharmony_ci	return 0;
3958c2ecf20Sopenharmony_ci}
3968c2ecf20Sopenharmony_ci
3978c2ecf20Sopenharmony_ci
3988c2ecf20Sopenharmony_ciint ap_control_del_mac(struct mac_restrictions *mac_restrictions, u8 *mac)
3998c2ecf20Sopenharmony_ci{
4008c2ecf20Sopenharmony_ci	struct list_head *ptr;
4018c2ecf20Sopenharmony_ci	struct mac_entry *entry;
4028c2ecf20Sopenharmony_ci
4038c2ecf20Sopenharmony_ci	spin_lock_bh(&mac_restrictions->lock);
4048c2ecf20Sopenharmony_ci	for (ptr = mac_restrictions->mac_list.next;
4058c2ecf20Sopenharmony_ci	     ptr != &mac_restrictions->mac_list; ptr = ptr->next) {
4068c2ecf20Sopenharmony_ci		entry = list_entry(ptr, struct mac_entry, list);
4078c2ecf20Sopenharmony_ci
4088c2ecf20Sopenharmony_ci		if (ether_addr_equal(entry->addr, mac)) {
4098c2ecf20Sopenharmony_ci			list_del(ptr);
4108c2ecf20Sopenharmony_ci			kfree(entry);
4118c2ecf20Sopenharmony_ci			mac_restrictions->entries--;
4128c2ecf20Sopenharmony_ci			spin_unlock_bh(&mac_restrictions->lock);
4138c2ecf20Sopenharmony_ci			return 0;
4148c2ecf20Sopenharmony_ci		}
4158c2ecf20Sopenharmony_ci	}
4168c2ecf20Sopenharmony_ci	spin_unlock_bh(&mac_restrictions->lock);
4178c2ecf20Sopenharmony_ci	return -1;
4188c2ecf20Sopenharmony_ci}
4198c2ecf20Sopenharmony_ci
4208c2ecf20Sopenharmony_ci
4218c2ecf20Sopenharmony_cistatic int ap_control_mac_deny(struct mac_restrictions *mac_restrictions,
4228c2ecf20Sopenharmony_ci			       u8 *mac)
4238c2ecf20Sopenharmony_ci{
4248c2ecf20Sopenharmony_ci	struct mac_entry *entry;
4258c2ecf20Sopenharmony_ci	int found = 0;
4268c2ecf20Sopenharmony_ci
4278c2ecf20Sopenharmony_ci	if (mac_restrictions->policy == MAC_POLICY_OPEN)
4288c2ecf20Sopenharmony_ci		return 0;
4298c2ecf20Sopenharmony_ci
4308c2ecf20Sopenharmony_ci	spin_lock_bh(&mac_restrictions->lock);
4318c2ecf20Sopenharmony_ci	list_for_each_entry(entry, &mac_restrictions->mac_list, list) {
4328c2ecf20Sopenharmony_ci		if (ether_addr_equal(entry->addr, mac)) {
4338c2ecf20Sopenharmony_ci			found = 1;
4348c2ecf20Sopenharmony_ci			break;
4358c2ecf20Sopenharmony_ci		}
4368c2ecf20Sopenharmony_ci	}
4378c2ecf20Sopenharmony_ci	spin_unlock_bh(&mac_restrictions->lock);
4388c2ecf20Sopenharmony_ci
4398c2ecf20Sopenharmony_ci	if (mac_restrictions->policy == MAC_POLICY_ALLOW)
4408c2ecf20Sopenharmony_ci		return !found;
4418c2ecf20Sopenharmony_ci	else
4428c2ecf20Sopenharmony_ci		return found;
4438c2ecf20Sopenharmony_ci}
4448c2ecf20Sopenharmony_ci
4458c2ecf20Sopenharmony_ci
4468c2ecf20Sopenharmony_civoid ap_control_flush_macs(struct mac_restrictions *mac_restrictions)
4478c2ecf20Sopenharmony_ci{
4488c2ecf20Sopenharmony_ci	struct list_head *ptr, *n;
4498c2ecf20Sopenharmony_ci	struct mac_entry *entry;
4508c2ecf20Sopenharmony_ci
4518c2ecf20Sopenharmony_ci	if (mac_restrictions->entries == 0)
4528c2ecf20Sopenharmony_ci		return;
4538c2ecf20Sopenharmony_ci
4548c2ecf20Sopenharmony_ci	spin_lock_bh(&mac_restrictions->lock);
4558c2ecf20Sopenharmony_ci	for (ptr = mac_restrictions->mac_list.next, n = ptr->next;
4568c2ecf20Sopenharmony_ci	     ptr != &mac_restrictions->mac_list;
4578c2ecf20Sopenharmony_ci	     ptr = n, n = ptr->next) {
4588c2ecf20Sopenharmony_ci		entry = list_entry(ptr, struct mac_entry, list);
4598c2ecf20Sopenharmony_ci		list_del(ptr);
4608c2ecf20Sopenharmony_ci		kfree(entry);
4618c2ecf20Sopenharmony_ci	}
4628c2ecf20Sopenharmony_ci	mac_restrictions->entries = 0;
4638c2ecf20Sopenharmony_ci	spin_unlock_bh(&mac_restrictions->lock);
4648c2ecf20Sopenharmony_ci}
4658c2ecf20Sopenharmony_ci
4668c2ecf20Sopenharmony_ci
4678c2ecf20Sopenharmony_ciint ap_control_kick_mac(struct ap_data *ap, struct net_device *dev, u8 *mac)
4688c2ecf20Sopenharmony_ci{
4698c2ecf20Sopenharmony_ci	struct sta_info *sta;
4708c2ecf20Sopenharmony_ci	__le16 resp;
4718c2ecf20Sopenharmony_ci
4728c2ecf20Sopenharmony_ci	spin_lock_bh(&ap->sta_table_lock);
4738c2ecf20Sopenharmony_ci	sta = ap_get_sta(ap, mac);
4748c2ecf20Sopenharmony_ci	if (sta) {
4758c2ecf20Sopenharmony_ci		ap_sta_hash_del(ap, sta);
4768c2ecf20Sopenharmony_ci		list_del(&sta->list);
4778c2ecf20Sopenharmony_ci	}
4788c2ecf20Sopenharmony_ci	spin_unlock_bh(&ap->sta_table_lock);
4798c2ecf20Sopenharmony_ci
4808c2ecf20Sopenharmony_ci	if (!sta)
4818c2ecf20Sopenharmony_ci		return -EINVAL;
4828c2ecf20Sopenharmony_ci
4838c2ecf20Sopenharmony_ci	resp = cpu_to_le16(WLAN_REASON_PREV_AUTH_NOT_VALID);
4848c2ecf20Sopenharmony_ci	prism2_send_mgmt(dev, IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_DEAUTH,
4858c2ecf20Sopenharmony_ci			 (char *) &resp, 2, sta->addr, 0);
4868c2ecf20Sopenharmony_ci
4878c2ecf20Sopenharmony_ci	if ((sta->flags & WLAN_STA_ASSOC) && !sta->ap)
4888c2ecf20Sopenharmony_ci		hostap_event_expired_sta(dev, sta);
4898c2ecf20Sopenharmony_ci
4908c2ecf20Sopenharmony_ci	ap_free_sta(ap, sta);
4918c2ecf20Sopenharmony_ci
4928c2ecf20Sopenharmony_ci	return 0;
4938c2ecf20Sopenharmony_ci}
4948c2ecf20Sopenharmony_ci
4958c2ecf20Sopenharmony_ci#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */
4968c2ecf20Sopenharmony_ci
4978c2ecf20Sopenharmony_ci
4988c2ecf20Sopenharmony_civoid ap_control_kickall(struct ap_data *ap)
4998c2ecf20Sopenharmony_ci{
5008c2ecf20Sopenharmony_ci	struct list_head *ptr, *n;
5018c2ecf20Sopenharmony_ci	struct sta_info *sta;
5028c2ecf20Sopenharmony_ci
5038c2ecf20Sopenharmony_ci	spin_lock_bh(&ap->sta_table_lock);
5048c2ecf20Sopenharmony_ci	for (ptr = ap->sta_list.next, n = ptr->next; ptr != &ap->sta_list;
5058c2ecf20Sopenharmony_ci	     ptr = n, n = ptr->next) {
5068c2ecf20Sopenharmony_ci		sta = list_entry(ptr, struct sta_info, list);
5078c2ecf20Sopenharmony_ci		ap_sta_hash_del(ap, sta);
5088c2ecf20Sopenharmony_ci		list_del(&sta->list);
5098c2ecf20Sopenharmony_ci		if ((sta->flags & WLAN_STA_ASSOC) && !sta->ap && sta->local)
5108c2ecf20Sopenharmony_ci			hostap_event_expired_sta(sta->local->dev, sta);
5118c2ecf20Sopenharmony_ci		ap_free_sta(ap, sta);
5128c2ecf20Sopenharmony_ci	}
5138c2ecf20Sopenharmony_ci	spin_unlock_bh(&ap->sta_table_lock);
5148c2ecf20Sopenharmony_ci}
5158c2ecf20Sopenharmony_ci
5168c2ecf20Sopenharmony_ci
5178c2ecf20Sopenharmony_ci#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT
5188c2ecf20Sopenharmony_ci
5198c2ecf20Sopenharmony_cistatic int prism2_ap_proc_show(struct seq_file *m, void *v)
5208c2ecf20Sopenharmony_ci{
5218c2ecf20Sopenharmony_ci	struct sta_info *sta = v;
5228c2ecf20Sopenharmony_ci	int i;
5238c2ecf20Sopenharmony_ci
5248c2ecf20Sopenharmony_ci	if (v == SEQ_START_TOKEN) {
5258c2ecf20Sopenharmony_ci		seq_printf(m, "# BSSID CHAN SIGNAL NOISE RATE SSID FLAGS\n");
5268c2ecf20Sopenharmony_ci		return 0;
5278c2ecf20Sopenharmony_ci	}
5288c2ecf20Sopenharmony_ci
5298c2ecf20Sopenharmony_ci	if (!sta->ap)
5308c2ecf20Sopenharmony_ci		return 0;
5318c2ecf20Sopenharmony_ci
5328c2ecf20Sopenharmony_ci	seq_printf(m, "%pM %d %d %d %d '",
5338c2ecf20Sopenharmony_ci		   sta->addr,
5348c2ecf20Sopenharmony_ci		   sta->u.ap.channel, sta->last_rx_signal,
5358c2ecf20Sopenharmony_ci		   sta->last_rx_silence, sta->last_rx_rate);
5368c2ecf20Sopenharmony_ci
5378c2ecf20Sopenharmony_ci	for (i = 0; i < sta->u.ap.ssid_len; i++) {
5388c2ecf20Sopenharmony_ci		if (sta->u.ap.ssid[i] >= 32 && sta->u.ap.ssid[i] < 127)
5398c2ecf20Sopenharmony_ci			seq_putc(m, sta->u.ap.ssid[i]);
5408c2ecf20Sopenharmony_ci		else
5418c2ecf20Sopenharmony_ci			seq_printf(m, "<%02x>", sta->u.ap.ssid[i]);
5428c2ecf20Sopenharmony_ci	}
5438c2ecf20Sopenharmony_ci
5448c2ecf20Sopenharmony_ci	seq_putc(m, '\'');
5458c2ecf20Sopenharmony_ci	if (sta->capability & WLAN_CAPABILITY_ESS)
5468c2ecf20Sopenharmony_ci		seq_puts(m, " [ESS]");
5478c2ecf20Sopenharmony_ci	if (sta->capability & WLAN_CAPABILITY_IBSS)
5488c2ecf20Sopenharmony_ci		seq_puts(m, " [IBSS]");
5498c2ecf20Sopenharmony_ci	if (sta->capability & WLAN_CAPABILITY_PRIVACY)
5508c2ecf20Sopenharmony_ci		seq_puts(m, " [WEP]");
5518c2ecf20Sopenharmony_ci	seq_putc(m, '\n');
5528c2ecf20Sopenharmony_ci	return 0;
5538c2ecf20Sopenharmony_ci}
5548c2ecf20Sopenharmony_ci
5558c2ecf20Sopenharmony_cistatic void *prism2_ap_proc_start(struct seq_file *m, loff_t *_pos)
5568c2ecf20Sopenharmony_ci{
5578c2ecf20Sopenharmony_ci	struct ap_data *ap = PDE_DATA(file_inode(m->file));
5588c2ecf20Sopenharmony_ci	spin_lock_bh(&ap->sta_table_lock);
5598c2ecf20Sopenharmony_ci	return seq_list_start_head(&ap->sta_list, *_pos);
5608c2ecf20Sopenharmony_ci}
5618c2ecf20Sopenharmony_ci
5628c2ecf20Sopenharmony_cistatic void *prism2_ap_proc_next(struct seq_file *m, void *v, loff_t *_pos)
5638c2ecf20Sopenharmony_ci{
5648c2ecf20Sopenharmony_ci	struct ap_data *ap = PDE_DATA(file_inode(m->file));
5658c2ecf20Sopenharmony_ci	return seq_list_next(v, &ap->sta_list, _pos);
5668c2ecf20Sopenharmony_ci}
5678c2ecf20Sopenharmony_ci
5688c2ecf20Sopenharmony_cistatic void prism2_ap_proc_stop(struct seq_file *m, void *v)
5698c2ecf20Sopenharmony_ci{
5708c2ecf20Sopenharmony_ci	struct ap_data *ap = PDE_DATA(file_inode(m->file));
5718c2ecf20Sopenharmony_ci	spin_unlock_bh(&ap->sta_table_lock);
5728c2ecf20Sopenharmony_ci}
5738c2ecf20Sopenharmony_ci
5748c2ecf20Sopenharmony_cistatic const struct seq_operations prism2_ap_proc_seqops = {
5758c2ecf20Sopenharmony_ci	.start	= prism2_ap_proc_start,
5768c2ecf20Sopenharmony_ci	.next	= prism2_ap_proc_next,
5778c2ecf20Sopenharmony_ci	.stop	= prism2_ap_proc_stop,
5788c2ecf20Sopenharmony_ci	.show	= prism2_ap_proc_show,
5798c2ecf20Sopenharmony_ci};
5808c2ecf20Sopenharmony_ci#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */
5818c2ecf20Sopenharmony_ci
5828c2ecf20Sopenharmony_ci
5838c2ecf20Sopenharmony_civoid hostap_check_sta_fw_version(struct ap_data *ap, int sta_fw_ver)
5848c2ecf20Sopenharmony_ci{
5858c2ecf20Sopenharmony_ci	if (!ap)
5868c2ecf20Sopenharmony_ci		return;
5878c2ecf20Sopenharmony_ci
5888c2ecf20Sopenharmony_ci	if (sta_fw_ver == PRISM2_FW_VER(0,8,0)) {
5898c2ecf20Sopenharmony_ci		PDEBUG(DEBUG_AP, "Using data::nullfunc ACK workaround - "
5908c2ecf20Sopenharmony_ci		       "firmware upgrade recommended\n");
5918c2ecf20Sopenharmony_ci		ap->nullfunc_ack = 1;
5928c2ecf20Sopenharmony_ci	} else
5938c2ecf20Sopenharmony_ci		ap->nullfunc_ack = 0;
5948c2ecf20Sopenharmony_ci
5958c2ecf20Sopenharmony_ci	if (sta_fw_ver == PRISM2_FW_VER(1,4,2)) {
5968c2ecf20Sopenharmony_ci		printk(KERN_WARNING "%s: Warning: secondary station firmware "
5978c2ecf20Sopenharmony_ci		       "version 1.4.2 does not seem to work in Host AP mode\n",
5988c2ecf20Sopenharmony_ci		       ap->local->dev->name);
5998c2ecf20Sopenharmony_ci	}
6008c2ecf20Sopenharmony_ci}
6018c2ecf20Sopenharmony_ci
6028c2ecf20Sopenharmony_ci
6038c2ecf20Sopenharmony_ci/* Called only as a tasklet (software IRQ) */
6048c2ecf20Sopenharmony_cistatic void hostap_ap_tx_cb(struct sk_buff *skb, int ok, void *data)
6058c2ecf20Sopenharmony_ci{
6068c2ecf20Sopenharmony_ci	struct ap_data *ap = data;
6078c2ecf20Sopenharmony_ci	struct ieee80211_hdr *hdr;
6088c2ecf20Sopenharmony_ci
6098c2ecf20Sopenharmony_ci	if (!ap->local->hostapd || !ap->local->apdev) {
6108c2ecf20Sopenharmony_ci		dev_kfree_skb(skb);
6118c2ecf20Sopenharmony_ci		return;
6128c2ecf20Sopenharmony_ci	}
6138c2ecf20Sopenharmony_ci
6148c2ecf20Sopenharmony_ci	/* Pass the TX callback frame to the hostapd; use 802.11 header version
6158c2ecf20Sopenharmony_ci	 * 1 to indicate failure (no ACK) and 2 success (frame ACKed) */
6168c2ecf20Sopenharmony_ci
6178c2ecf20Sopenharmony_ci	hdr = (struct ieee80211_hdr *) skb->data;
6188c2ecf20Sopenharmony_ci	hdr->frame_control &= cpu_to_le16(~IEEE80211_FCTL_VERS);
6198c2ecf20Sopenharmony_ci	hdr->frame_control |= cpu_to_le16(ok ? BIT(1) : BIT(0));
6208c2ecf20Sopenharmony_ci
6218c2ecf20Sopenharmony_ci	skb->dev = ap->local->apdev;
6228c2ecf20Sopenharmony_ci	skb_pull(skb, hostap_80211_get_hdrlen(hdr->frame_control));
6238c2ecf20Sopenharmony_ci	skb->pkt_type = PACKET_OTHERHOST;
6248c2ecf20Sopenharmony_ci	skb->protocol = cpu_to_be16(ETH_P_802_2);
6258c2ecf20Sopenharmony_ci	memset(skb->cb, 0, sizeof(skb->cb));
6268c2ecf20Sopenharmony_ci	netif_rx(skb);
6278c2ecf20Sopenharmony_ci}
6288c2ecf20Sopenharmony_ci
6298c2ecf20Sopenharmony_ci
6308c2ecf20Sopenharmony_ci#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT
6318c2ecf20Sopenharmony_ci/* Called only as a tasklet (software IRQ) */
6328c2ecf20Sopenharmony_cistatic void hostap_ap_tx_cb_auth(struct sk_buff *skb, int ok, void *data)
6338c2ecf20Sopenharmony_ci{
6348c2ecf20Sopenharmony_ci	struct ap_data *ap = data;
6358c2ecf20Sopenharmony_ci	struct net_device *dev = ap->local->dev;
6368c2ecf20Sopenharmony_ci	struct ieee80211_hdr *hdr;
6378c2ecf20Sopenharmony_ci	u16 auth_alg, auth_transaction, status;
6388c2ecf20Sopenharmony_ci	__le16 *pos;
6398c2ecf20Sopenharmony_ci	struct sta_info *sta = NULL;
6408c2ecf20Sopenharmony_ci	char *txt = NULL;
6418c2ecf20Sopenharmony_ci
6428c2ecf20Sopenharmony_ci	if (ap->local->hostapd) {
6438c2ecf20Sopenharmony_ci		dev_kfree_skb(skb);
6448c2ecf20Sopenharmony_ci		return;
6458c2ecf20Sopenharmony_ci	}
6468c2ecf20Sopenharmony_ci
6478c2ecf20Sopenharmony_ci	hdr = (struct ieee80211_hdr *) skb->data;
6488c2ecf20Sopenharmony_ci	if (!ieee80211_is_auth(hdr->frame_control) ||
6498c2ecf20Sopenharmony_ci	    skb->len < IEEE80211_MGMT_HDR_LEN + 6) {
6508c2ecf20Sopenharmony_ci		printk(KERN_DEBUG "%s: hostap_ap_tx_cb_auth received invalid "
6518c2ecf20Sopenharmony_ci		       "frame\n", dev->name);
6528c2ecf20Sopenharmony_ci		dev_kfree_skb(skb);
6538c2ecf20Sopenharmony_ci		return;
6548c2ecf20Sopenharmony_ci	}
6558c2ecf20Sopenharmony_ci
6568c2ecf20Sopenharmony_ci	pos = (__le16 *) (skb->data + IEEE80211_MGMT_HDR_LEN);
6578c2ecf20Sopenharmony_ci	auth_alg = le16_to_cpu(*pos++);
6588c2ecf20Sopenharmony_ci	auth_transaction = le16_to_cpu(*pos++);
6598c2ecf20Sopenharmony_ci	status = le16_to_cpu(*pos++);
6608c2ecf20Sopenharmony_ci
6618c2ecf20Sopenharmony_ci	if (!ok) {
6628c2ecf20Sopenharmony_ci		txt = "frame was not ACKed";
6638c2ecf20Sopenharmony_ci		goto done;
6648c2ecf20Sopenharmony_ci	}
6658c2ecf20Sopenharmony_ci
6668c2ecf20Sopenharmony_ci	spin_lock(&ap->sta_table_lock);
6678c2ecf20Sopenharmony_ci	sta = ap_get_sta(ap, hdr->addr1);
6688c2ecf20Sopenharmony_ci	if (sta)
6698c2ecf20Sopenharmony_ci		atomic_inc(&sta->users);
6708c2ecf20Sopenharmony_ci	spin_unlock(&ap->sta_table_lock);
6718c2ecf20Sopenharmony_ci
6728c2ecf20Sopenharmony_ci	if (!sta) {
6738c2ecf20Sopenharmony_ci		txt = "STA not found";
6748c2ecf20Sopenharmony_ci		goto done;
6758c2ecf20Sopenharmony_ci	}
6768c2ecf20Sopenharmony_ci
6778c2ecf20Sopenharmony_ci	if (status == WLAN_STATUS_SUCCESS &&
6788c2ecf20Sopenharmony_ci	    ((auth_alg == WLAN_AUTH_OPEN && auth_transaction == 2) ||
6798c2ecf20Sopenharmony_ci	     (auth_alg == WLAN_AUTH_SHARED_KEY && auth_transaction == 4))) {
6808c2ecf20Sopenharmony_ci		txt = "STA authenticated";
6818c2ecf20Sopenharmony_ci		sta->flags |= WLAN_STA_AUTH;
6828c2ecf20Sopenharmony_ci		sta->last_auth = jiffies;
6838c2ecf20Sopenharmony_ci	} else if (status != WLAN_STATUS_SUCCESS)
6848c2ecf20Sopenharmony_ci		txt = "authentication failed";
6858c2ecf20Sopenharmony_ci
6868c2ecf20Sopenharmony_ci done:
6878c2ecf20Sopenharmony_ci	if (sta)
6888c2ecf20Sopenharmony_ci		atomic_dec(&sta->users);
6898c2ecf20Sopenharmony_ci	if (txt) {
6908c2ecf20Sopenharmony_ci		PDEBUG(DEBUG_AP, "%s: %pM auth_cb - alg=%d "
6918c2ecf20Sopenharmony_ci		       "trans#=%d status=%d - %s\n",
6928c2ecf20Sopenharmony_ci		       dev->name, hdr->addr1,
6938c2ecf20Sopenharmony_ci		       auth_alg, auth_transaction, status, txt);
6948c2ecf20Sopenharmony_ci	}
6958c2ecf20Sopenharmony_ci	dev_kfree_skb(skb);
6968c2ecf20Sopenharmony_ci}
6978c2ecf20Sopenharmony_ci
6988c2ecf20Sopenharmony_ci
6998c2ecf20Sopenharmony_ci/* Called only as a tasklet (software IRQ) */
7008c2ecf20Sopenharmony_cistatic void hostap_ap_tx_cb_assoc(struct sk_buff *skb, int ok, void *data)
7018c2ecf20Sopenharmony_ci{
7028c2ecf20Sopenharmony_ci	struct ap_data *ap = data;
7038c2ecf20Sopenharmony_ci	struct net_device *dev = ap->local->dev;
7048c2ecf20Sopenharmony_ci	struct ieee80211_hdr *hdr;
7058c2ecf20Sopenharmony_ci	u16 status;
7068c2ecf20Sopenharmony_ci	__le16 *pos;
7078c2ecf20Sopenharmony_ci	struct sta_info *sta = NULL;
7088c2ecf20Sopenharmony_ci	char *txt = NULL;
7098c2ecf20Sopenharmony_ci
7108c2ecf20Sopenharmony_ci	if (ap->local->hostapd) {
7118c2ecf20Sopenharmony_ci		dev_kfree_skb(skb);
7128c2ecf20Sopenharmony_ci		return;
7138c2ecf20Sopenharmony_ci	}
7148c2ecf20Sopenharmony_ci
7158c2ecf20Sopenharmony_ci	hdr = (struct ieee80211_hdr *) skb->data;
7168c2ecf20Sopenharmony_ci	if ((!ieee80211_is_assoc_resp(hdr->frame_control) &&
7178c2ecf20Sopenharmony_ci	     !ieee80211_is_reassoc_resp(hdr->frame_control)) ||
7188c2ecf20Sopenharmony_ci	    skb->len < IEEE80211_MGMT_HDR_LEN + 4) {
7198c2ecf20Sopenharmony_ci		printk(KERN_DEBUG "%s: hostap_ap_tx_cb_assoc received invalid "
7208c2ecf20Sopenharmony_ci		       "frame\n", dev->name);
7218c2ecf20Sopenharmony_ci		dev_kfree_skb(skb);
7228c2ecf20Sopenharmony_ci		return;
7238c2ecf20Sopenharmony_ci	}
7248c2ecf20Sopenharmony_ci
7258c2ecf20Sopenharmony_ci	if (!ok) {
7268c2ecf20Sopenharmony_ci		txt = "frame was not ACKed";
7278c2ecf20Sopenharmony_ci		goto done;
7288c2ecf20Sopenharmony_ci	}
7298c2ecf20Sopenharmony_ci
7308c2ecf20Sopenharmony_ci	spin_lock(&ap->sta_table_lock);
7318c2ecf20Sopenharmony_ci	sta = ap_get_sta(ap, hdr->addr1);
7328c2ecf20Sopenharmony_ci	if (sta)
7338c2ecf20Sopenharmony_ci		atomic_inc(&sta->users);
7348c2ecf20Sopenharmony_ci	spin_unlock(&ap->sta_table_lock);
7358c2ecf20Sopenharmony_ci
7368c2ecf20Sopenharmony_ci	if (!sta) {
7378c2ecf20Sopenharmony_ci		txt = "STA not found";
7388c2ecf20Sopenharmony_ci		goto done;
7398c2ecf20Sopenharmony_ci	}
7408c2ecf20Sopenharmony_ci
7418c2ecf20Sopenharmony_ci	pos = (__le16 *) (skb->data + IEEE80211_MGMT_HDR_LEN);
7428c2ecf20Sopenharmony_ci	pos++;
7438c2ecf20Sopenharmony_ci	status = le16_to_cpu(*pos++);
7448c2ecf20Sopenharmony_ci	if (status == WLAN_STATUS_SUCCESS) {
7458c2ecf20Sopenharmony_ci		if (!(sta->flags & WLAN_STA_ASSOC))
7468c2ecf20Sopenharmony_ci			hostap_event_new_sta(dev, sta);
7478c2ecf20Sopenharmony_ci		txt = "STA associated";
7488c2ecf20Sopenharmony_ci		sta->flags |= WLAN_STA_ASSOC;
7498c2ecf20Sopenharmony_ci		sta->last_assoc = jiffies;
7508c2ecf20Sopenharmony_ci	} else
7518c2ecf20Sopenharmony_ci		txt = "association failed";
7528c2ecf20Sopenharmony_ci
7538c2ecf20Sopenharmony_ci done:
7548c2ecf20Sopenharmony_ci	if (sta)
7558c2ecf20Sopenharmony_ci		atomic_dec(&sta->users);
7568c2ecf20Sopenharmony_ci	if (txt) {
7578c2ecf20Sopenharmony_ci		PDEBUG(DEBUG_AP, "%s: %pM assoc_cb - %s\n",
7588c2ecf20Sopenharmony_ci		       dev->name, hdr->addr1, txt);
7598c2ecf20Sopenharmony_ci	}
7608c2ecf20Sopenharmony_ci	dev_kfree_skb(skb);
7618c2ecf20Sopenharmony_ci}
7628c2ecf20Sopenharmony_ci
7638c2ecf20Sopenharmony_ci/* Called only as a tasklet (software IRQ); TX callback for poll frames used
7648c2ecf20Sopenharmony_ci * in verifying whether the STA is still present. */
7658c2ecf20Sopenharmony_cistatic void hostap_ap_tx_cb_poll(struct sk_buff *skb, int ok, void *data)
7668c2ecf20Sopenharmony_ci{
7678c2ecf20Sopenharmony_ci	struct ap_data *ap = data;
7688c2ecf20Sopenharmony_ci	struct ieee80211_hdr *hdr;
7698c2ecf20Sopenharmony_ci	struct sta_info *sta;
7708c2ecf20Sopenharmony_ci
7718c2ecf20Sopenharmony_ci	if (skb->len < 24)
7728c2ecf20Sopenharmony_ci		goto fail;
7738c2ecf20Sopenharmony_ci	hdr = (struct ieee80211_hdr *) skb->data;
7748c2ecf20Sopenharmony_ci	if (ok) {
7758c2ecf20Sopenharmony_ci		spin_lock(&ap->sta_table_lock);
7768c2ecf20Sopenharmony_ci		sta = ap_get_sta(ap, hdr->addr1);
7778c2ecf20Sopenharmony_ci		if (sta)
7788c2ecf20Sopenharmony_ci			sta->flags &= ~WLAN_STA_PENDING_POLL;
7798c2ecf20Sopenharmony_ci		spin_unlock(&ap->sta_table_lock);
7808c2ecf20Sopenharmony_ci	} else {
7818c2ecf20Sopenharmony_ci		PDEBUG(DEBUG_AP,
7828c2ecf20Sopenharmony_ci		       "%s: STA %pM did not ACK activity poll frame\n",
7838c2ecf20Sopenharmony_ci		       ap->local->dev->name, hdr->addr1);
7848c2ecf20Sopenharmony_ci	}
7858c2ecf20Sopenharmony_ci
7868c2ecf20Sopenharmony_ci fail:
7878c2ecf20Sopenharmony_ci	dev_kfree_skb(skb);
7888c2ecf20Sopenharmony_ci}
7898c2ecf20Sopenharmony_ci#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */
7908c2ecf20Sopenharmony_ci
7918c2ecf20Sopenharmony_ci
7928c2ecf20Sopenharmony_civoid hostap_init_data(local_info_t *local)
7938c2ecf20Sopenharmony_ci{
7948c2ecf20Sopenharmony_ci	struct ap_data *ap = local->ap;
7958c2ecf20Sopenharmony_ci
7968c2ecf20Sopenharmony_ci	if (ap == NULL) {
7978c2ecf20Sopenharmony_ci		printk(KERN_WARNING "hostap_init_data: ap == NULL\n");
7988c2ecf20Sopenharmony_ci		return;
7998c2ecf20Sopenharmony_ci	}
8008c2ecf20Sopenharmony_ci	memset(ap, 0, sizeof(struct ap_data));
8018c2ecf20Sopenharmony_ci	ap->local = local;
8028c2ecf20Sopenharmony_ci
8038c2ecf20Sopenharmony_ci	ap->ap_policy = GET_INT_PARM(other_ap_policy, local->card_idx);
8048c2ecf20Sopenharmony_ci	ap->bridge_packets = GET_INT_PARM(ap_bridge_packets, local->card_idx);
8058c2ecf20Sopenharmony_ci	ap->max_inactivity =
8068c2ecf20Sopenharmony_ci		GET_INT_PARM(ap_max_inactivity, local->card_idx) * HZ;
8078c2ecf20Sopenharmony_ci	ap->autom_ap_wds = GET_INT_PARM(autom_ap_wds, local->card_idx);
8088c2ecf20Sopenharmony_ci
8098c2ecf20Sopenharmony_ci	spin_lock_init(&ap->sta_table_lock);
8108c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&ap->sta_list);
8118c2ecf20Sopenharmony_ci
8128c2ecf20Sopenharmony_ci	/* Initialize task queue structure for AP management */
8138c2ecf20Sopenharmony_ci	INIT_WORK(&local->ap->add_sta_proc_queue, handle_add_proc_queue);
8148c2ecf20Sopenharmony_ci
8158c2ecf20Sopenharmony_ci	ap->tx_callback_idx =
8168c2ecf20Sopenharmony_ci		hostap_tx_callback_register(local, hostap_ap_tx_cb, ap);
8178c2ecf20Sopenharmony_ci	if (ap->tx_callback_idx == 0)
8188c2ecf20Sopenharmony_ci		printk(KERN_WARNING "%s: failed to register TX callback for "
8198c2ecf20Sopenharmony_ci		       "AP\n", local->dev->name);
8208c2ecf20Sopenharmony_ci#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT
8218c2ecf20Sopenharmony_ci	INIT_WORK(&local->ap->wds_oper_queue, handle_wds_oper_queue);
8228c2ecf20Sopenharmony_ci
8238c2ecf20Sopenharmony_ci	ap->tx_callback_auth =
8248c2ecf20Sopenharmony_ci		hostap_tx_callback_register(local, hostap_ap_tx_cb_auth, ap);
8258c2ecf20Sopenharmony_ci	ap->tx_callback_assoc =
8268c2ecf20Sopenharmony_ci		hostap_tx_callback_register(local, hostap_ap_tx_cb_assoc, ap);
8278c2ecf20Sopenharmony_ci	ap->tx_callback_poll =
8288c2ecf20Sopenharmony_ci		hostap_tx_callback_register(local, hostap_ap_tx_cb_poll, ap);
8298c2ecf20Sopenharmony_ci	if (ap->tx_callback_auth == 0 || ap->tx_callback_assoc == 0 ||
8308c2ecf20Sopenharmony_ci		ap->tx_callback_poll == 0)
8318c2ecf20Sopenharmony_ci		printk(KERN_WARNING "%s: failed to register TX callback for "
8328c2ecf20Sopenharmony_ci		       "AP\n", local->dev->name);
8338c2ecf20Sopenharmony_ci
8348c2ecf20Sopenharmony_ci	spin_lock_init(&ap->mac_restrictions.lock);
8358c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&ap->mac_restrictions.mac_list);
8368c2ecf20Sopenharmony_ci#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */
8378c2ecf20Sopenharmony_ci
8388c2ecf20Sopenharmony_ci	ap->initialized = 1;
8398c2ecf20Sopenharmony_ci}
8408c2ecf20Sopenharmony_ci
8418c2ecf20Sopenharmony_ci
8428c2ecf20Sopenharmony_civoid hostap_init_ap_proc(local_info_t *local)
8438c2ecf20Sopenharmony_ci{
8448c2ecf20Sopenharmony_ci	struct ap_data *ap = local->ap;
8458c2ecf20Sopenharmony_ci
8468c2ecf20Sopenharmony_ci	ap->proc = local->proc;
8478c2ecf20Sopenharmony_ci	if (ap->proc == NULL)
8488c2ecf20Sopenharmony_ci		return;
8498c2ecf20Sopenharmony_ci
8508c2ecf20Sopenharmony_ci#ifndef PRISM2_NO_PROCFS_DEBUG
8518c2ecf20Sopenharmony_ci	proc_create_single_data("ap_debug", 0, ap->proc, ap_debug_proc_show, ap);
8528c2ecf20Sopenharmony_ci#endif /* PRISM2_NO_PROCFS_DEBUG */
8538c2ecf20Sopenharmony_ci
8548c2ecf20Sopenharmony_ci#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT
8558c2ecf20Sopenharmony_ci	proc_create_seq_data("ap_control", 0, ap->proc, &ap_control_proc_seqops,
8568c2ecf20Sopenharmony_ci			ap);
8578c2ecf20Sopenharmony_ci	proc_create_seq_data("ap", 0, ap->proc, &prism2_ap_proc_seqops, ap);
8588c2ecf20Sopenharmony_ci#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */
8598c2ecf20Sopenharmony_ci
8608c2ecf20Sopenharmony_ci}
8618c2ecf20Sopenharmony_ci
8628c2ecf20Sopenharmony_ci
8638c2ecf20Sopenharmony_civoid hostap_free_data(struct ap_data *ap)
8648c2ecf20Sopenharmony_ci{
8658c2ecf20Sopenharmony_ci	struct sta_info *n, *sta;
8668c2ecf20Sopenharmony_ci
8678c2ecf20Sopenharmony_ci	if (ap == NULL || !ap->initialized) {
8688c2ecf20Sopenharmony_ci		printk(KERN_DEBUG "hostap_free_data: ap has not yet been "
8698c2ecf20Sopenharmony_ci		       "initialized - skip resource freeing\n");
8708c2ecf20Sopenharmony_ci		return;
8718c2ecf20Sopenharmony_ci	}
8728c2ecf20Sopenharmony_ci
8738c2ecf20Sopenharmony_ci	flush_work(&ap->add_sta_proc_queue);
8748c2ecf20Sopenharmony_ci
8758c2ecf20Sopenharmony_ci#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT
8768c2ecf20Sopenharmony_ci	flush_work(&ap->wds_oper_queue);
8778c2ecf20Sopenharmony_ci	if (ap->crypt)
8788c2ecf20Sopenharmony_ci		ap->crypt->deinit(ap->crypt_priv);
8798c2ecf20Sopenharmony_ci	ap->crypt = ap->crypt_priv = NULL;
8808c2ecf20Sopenharmony_ci#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */
8818c2ecf20Sopenharmony_ci
8828c2ecf20Sopenharmony_ci	list_for_each_entry_safe(sta, n, &ap->sta_list, list) {
8838c2ecf20Sopenharmony_ci		ap_sta_hash_del(ap, sta);
8848c2ecf20Sopenharmony_ci		list_del(&sta->list);
8858c2ecf20Sopenharmony_ci		if ((sta->flags & WLAN_STA_ASSOC) && !sta->ap && sta->local)
8868c2ecf20Sopenharmony_ci			hostap_event_expired_sta(sta->local->dev, sta);
8878c2ecf20Sopenharmony_ci		ap_free_sta(ap, sta);
8888c2ecf20Sopenharmony_ci	}
8898c2ecf20Sopenharmony_ci
8908c2ecf20Sopenharmony_ci#ifndef PRISM2_NO_PROCFS_DEBUG
8918c2ecf20Sopenharmony_ci	if (ap->proc != NULL) {
8928c2ecf20Sopenharmony_ci		remove_proc_entry("ap_debug", ap->proc);
8938c2ecf20Sopenharmony_ci	}
8948c2ecf20Sopenharmony_ci#endif /* PRISM2_NO_PROCFS_DEBUG */
8958c2ecf20Sopenharmony_ci
8968c2ecf20Sopenharmony_ci#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT
8978c2ecf20Sopenharmony_ci	if (ap->proc != NULL) {
8988c2ecf20Sopenharmony_ci	  remove_proc_entry("ap", ap->proc);
8998c2ecf20Sopenharmony_ci		remove_proc_entry("ap_control", ap->proc);
9008c2ecf20Sopenharmony_ci	}
9018c2ecf20Sopenharmony_ci	ap_control_flush_macs(&ap->mac_restrictions);
9028c2ecf20Sopenharmony_ci#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */
9038c2ecf20Sopenharmony_ci
9048c2ecf20Sopenharmony_ci	ap->initialized = 0;
9058c2ecf20Sopenharmony_ci}
9068c2ecf20Sopenharmony_ci
9078c2ecf20Sopenharmony_ci
9088c2ecf20Sopenharmony_ci/* caller should have mutex for AP STA list handling */
9098c2ecf20Sopenharmony_cistatic struct sta_info* ap_get_sta(struct ap_data *ap, u8 *sta)
9108c2ecf20Sopenharmony_ci{
9118c2ecf20Sopenharmony_ci	struct sta_info *s;
9128c2ecf20Sopenharmony_ci
9138c2ecf20Sopenharmony_ci	s = ap->sta_hash[STA_HASH(sta)];
9148c2ecf20Sopenharmony_ci	while (s != NULL && !ether_addr_equal(s->addr, sta))
9158c2ecf20Sopenharmony_ci		s = s->hnext;
9168c2ecf20Sopenharmony_ci	return s;
9178c2ecf20Sopenharmony_ci}
9188c2ecf20Sopenharmony_ci
9198c2ecf20Sopenharmony_ci
9208c2ecf20Sopenharmony_ci#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT
9218c2ecf20Sopenharmony_ci
9228c2ecf20Sopenharmony_ci/* Called from timer handler and from scheduled AP queue handlers */
9238c2ecf20Sopenharmony_cistatic void prism2_send_mgmt(struct net_device *dev,
9248c2ecf20Sopenharmony_ci			     u16 type_subtype, char *body,
9258c2ecf20Sopenharmony_ci			     int body_len, u8 *addr, u16 tx_cb_idx)
9268c2ecf20Sopenharmony_ci{
9278c2ecf20Sopenharmony_ci	struct hostap_interface *iface;
9288c2ecf20Sopenharmony_ci	local_info_t *local;
9298c2ecf20Sopenharmony_ci	struct ieee80211_hdr *hdr;
9308c2ecf20Sopenharmony_ci	u16 fc;
9318c2ecf20Sopenharmony_ci	struct sk_buff *skb;
9328c2ecf20Sopenharmony_ci	struct hostap_skb_tx_data *meta;
9338c2ecf20Sopenharmony_ci	int hdrlen;
9348c2ecf20Sopenharmony_ci
9358c2ecf20Sopenharmony_ci	iface = netdev_priv(dev);
9368c2ecf20Sopenharmony_ci	local = iface->local;
9378c2ecf20Sopenharmony_ci	dev = local->dev; /* always use master radio device */
9388c2ecf20Sopenharmony_ci	iface = netdev_priv(dev);
9398c2ecf20Sopenharmony_ci
9408c2ecf20Sopenharmony_ci	if (!(dev->flags & IFF_UP)) {
9418c2ecf20Sopenharmony_ci		PDEBUG(DEBUG_AP, "%s: prism2_send_mgmt - device is not UP - "
9428c2ecf20Sopenharmony_ci		       "cannot send frame\n", dev->name);
9438c2ecf20Sopenharmony_ci		return;
9448c2ecf20Sopenharmony_ci	}
9458c2ecf20Sopenharmony_ci
9468c2ecf20Sopenharmony_ci	skb = dev_alloc_skb(sizeof(*hdr) + body_len);
9478c2ecf20Sopenharmony_ci	if (skb == NULL) {
9488c2ecf20Sopenharmony_ci		PDEBUG(DEBUG_AP, "%s: prism2_send_mgmt failed to allocate "
9498c2ecf20Sopenharmony_ci		       "skb\n", dev->name);
9508c2ecf20Sopenharmony_ci		return;
9518c2ecf20Sopenharmony_ci	}
9528c2ecf20Sopenharmony_ci
9538c2ecf20Sopenharmony_ci	fc = type_subtype;
9548c2ecf20Sopenharmony_ci	hdrlen = hostap_80211_get_hdrlen(cpu_to_le16(type_subtype));
9558c2ecf20Sopenharmony_ci	hdr = skb_put_zero(skb, hdrlen);
9568c2ecf20Sopenharmony_ci	if (body)
9578c2ecf20Sopenharmony_ci		skb_put_data(skb, body, body_len);
9588c2ecf20Sopenharmony_ci
9598c2ecf20Sopenharmony_ci	/* FIX: ctrl::ack sending used special HFA384X_TX_CTRL_802_11
9608c2ecf20Sopenharmony_ci	 * tx_control instead of using local->tx_control */
9618c2ecf20Sopenharmony_ci
9628c2ecf20Sopenharmony_ci
9638c2ecf20Sopenharmony_ci	memcpy(hdr->addr1, addr, ETH_ALEN); /* DA / RA */
9648c2ecf20Sopenharmony_ci	if (ieee80211_is_data(hdr->frame_control)) {
9658c2ecf20Sopenharmony_ci		fc |= IEEE80211_FCTL_FROMDS;
9668c2ecf20Sopenharmony_ci		memcpy(hdr->addr2, dev->dev_addr, ETH_ALEN); /* BSSID */
9678c2ecf20Sopenharmony_ci		memcpy(hdr->addr3, dev->dev_addr, ETH_ALEN); /* SA */
9688c2ecf20Sopenharmony_ci	} else if (ieee80211_is_ctl(hdr->frame_control)) {
9698c2ecf20Sopenharmony_ci		/* control:ACK does not have addr2 or addr3 */
9708c2ecf20Sopenharmony_ci		eth_zero_addr(hdr->addr2);
9718c2ecf20Sopenharmony_ci		eth_zero_addr(hdr->addr3);
9728c2ecf20Sopenharmony_ci	} else {
9738c2ecf20Sopenharmony_ci		memcpy(hdr->addr2, dev->dev_addr, ETH_ALEN); /* SA */
9748c2ecf20Sopenharmony_ci		memcpy(hdr->addr3, dev->dev_addr, ETH_ALEN); /* BSSID */
9758c2ecf20Sopenharmony_ci	}
9768c2ecf20Sopenharmony_ci
9778c2ecf20Sopenharmony_ci	hdr->frame_control = cpu_to_le16(fc);
9788c2ecf20Sopenharmony_ci
9798c2ecf20Sopenharmony_ci	meta = (struct hostap_skb_tx_data *) skb->cb;
9808c2ecf20Sopenharmony_ci	memset(meta, 0, sizeof(*meta));
9818c2ecf20Sopenharmony_ci	meta->magic = HOSTAP_SKB_TX_DATA_MAGIC;
9828c2ecf20Sopenharmony_ci	meta->iface = iface;
9838c2ecf20Sopenharmony_ci	meta->tx_cb_idx = tx_cb_idx;
9848c2ecf20Sopenharmony_ci
9858c2ecf20Sopenharmony_ci	skb->dev = dev;
9868c2ecf20Sopenharmony_ci	skb_reset_mac_header(skb);
9878c2ecf20Sopenharmony_ci	skb_reset_network_header(skb);
9888c2ecf20Sopenharmony_ci	dev_queue_xmit(skb);
9898c2ecf20Sopenharmony_ci}
9908c2ecf20Sopenharmony_ci#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */
9918c2ecf20Sopenharmony_ci
9928c2ecf20Sopenharmony_ci#ifdef CONFIG_PROC_FS
9938c2ecf20Sopenharmony_cistatic int prism2_sta_proc_show(struct seq_file *m, void *v)
9948c2ecf20Sopenharmony_ci{
9958c2ecf20Sopenharmony_ci	struct sta_info *sta = m->private;
9968c2ecf20Sopenharmony_ci	int i;
9978c2ecf20Sopenharmony_ci
9988c2ecf20Sopenharmony_ci	/* FIX: possible race condition.. the STA data could have just expired,
9998c2ecf20Sopenharmony_ci	 * but proc entry was still here so that the read could have started;
10008c2ecf20Sopenharmony_ci	 * some locking should be done here.. */
10018c2ecf20Sopenharmony_ci
10028c2ecf20Sopenharmony_ci	seq_printf(m,
10038c2ecf20Sopenharmony_ci		   "%s=%pM\nusers=%d\naid=%d\n"
10048c2ecf20Sopenharmony_ci		   "flags=0x%04x%s%s%s%s%s%s%s\n"
10058c2ecf20Sopenharmony_ci		   "capability=0x%02x\nlisten_interval=%d\nsupported_rates=",
10068c2ecf20Sopenharmony_ci		   sta->ap ? "AP" : "STA",
10078c2ecf20Sopenharmony_ci		   sta->addr, atomic_read(&sta->users), sta->aid,
10088c2ecf20Sopenharmony_ci		   sta->flags,
10098c2ecf20Sopenharmony_ci		   sta->flags & WLAN_STA_AUTH ? " AUTH" : "",
10108c2ecf20Sopenharmony_ci		   sta->flags & WLAN_STA_ASSOC ? " ASSOC" : "",
10118c2ecf20Sopenharmony_ci		   sta->flags & WLAN_STA_PS ? " PS" : "",
10128c2ecf20Sopenharmony_ci		   sta->flags & WLAN_STA_TIM ? " TIM" : "",
10138c2ecf20Sopenharmony_ci		   sta->flags & WLAN_STA_PERM ? " PERM" : "",
10148c2ecf20Sopenharmony_ci		   sta->flags & WLAN_STA_AUTHORIZED ? " AUTHORIZED" : "",
10158c2ecf20Sopenharmony_ci		   sta->flags & WLAN_STA_PENDING_POLL ? " POLL" : "",
10168c2ecf20Sopenharmony_ci		   sta->capability, sta->listen_interval);
10178c2ecf20Sopenharmony_ci	/* supported_rates: 500 kbit/s units with msb ignored */
10188c2ecf20Sopenharmony_ci	for (i = 0; i < sizeof(sta->supported_rates); i++)
10198c2ecf20Sopenharmony_ci		if (sta->supported_rates[i] != 0)
10208c2ecf20Sopenharmony_ci			seq_printf(m, "%d%sMbps ",
10218c2ecf20Sopenharmony_ci				   (sta->supported_rates[i] & 0x7f) / 2,
10228c2ecf20Sopenharmony_ci				   sta->supported_rates[i] & 1 ? ".5" : "");
10238c2ecf20Sopenharmony_ci	seq_printf(m,
10248c2ecf20Sopenharmony_ci		   "\njiffies=%lu\nlast_auth=%lu\nlast_assoc=%lu\n"
10258c2ecf20Sopenharmony_ci		   "last_rx=%lu\nlast_tx=%lu\nrx_packets=%lu\n"
10268c2ecf20Sopenharmony_ci		   "tx_packets=%lu\n"
10278c2ecf20Sopenharmony_ci		   "rx_bytes=%lu\ntx_bytes=%lu\nbuffer_count=%d\n"
10288c2ecf20Sopenharmony_ci		   "last_rx: silence=%d dBm signal=%d dBm rate=%d%s Mbps\n"
10298c2ecf20Sopenharmony_ci		   "tx_rate=%d\ntx[1M]=%d\ntx[2M]=%d\ntx[5.5M]=%d\n"
10308c2ecf20Sopenharmony_ci		   "tx[11M]=%d\n"
10318c2ecf20Sopenharmony_ci		   "rx[1M]=%d\nrx[2M]=%d\nrx[5.5M]=%d\nrx[11M]=%d\n",
10328c2ecf20Sopenharmony_ci		   jiffies, sta->last_auth, sta->last_assoc, sta->last_rx,
10338c2ecf20Sopenharmony_ci		   sta->last_tx,
10348c2ecf20Sopenharmony_ci		   sta->rx_packets, sta->tx_packets, sta->rx_bytes,
10358c2ecf20Sopenharmony_ci		   sta->tx_bytes, skb_queue_len(&sta->tx_buf),
10368c2ecf20Sopenharmony_ci		   sta->last_rx_silence,
10378c2ecf20Sopenharmony_ci		   sta->last_rx_signal, sta->last_rx_rate / 10,
10388c2ecf20Sopenharmony_ci		   sta->last_rx_rate % 10 ? ".5" : "",
10398c2ecf20Sopenharmony_ci		   sta->tx_rate, sta->tx_count[0], sta->tx_count[1],
10408c2ecf20Sopenharmony_ci		   sta->tx_count[2], sta->tx_count[3],  sta->rx_count[0],
10418c2ecf20Sopenharmony_ci		   sta->rx_count[1], sta->rx_count[2], sta->rx_count[3]);
10428c2ecf20Sopenharmony_ci	if (sta->crypt && sta->crypt->ops && sta->crypt->ops->print_stats)
10438c2ecf20Sopenharmony_ci		sta->crypt->ops->print_stats(m, sta->crypt->priv);
10448c2ecf20Sopenharmony_ci#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT
10458c2ecf20Sopenharmony_ci	if (sta->ap) {
10468c2ecf20Sopenharmony_ci		if (sta->u.ap.channel >= 0)
10478c2ecf20Sopenharmony_ci			seq_printf(m, "channel=%d\n", sta->u.ap.channel);
10488c2ecf20Sopenharmony_ci		seq_puts(m, "ssid=");
10498c2ecf20Sopenharmony_ci		for (i = 0; i < sta->u.ap.ssid_len; i++) {
10508c2ecf20Sopenharmony_ci			if (sta->u.ap.ssid[i] >= 32 && sta->u.ap.ssid[i] < 127)
10518c2ecf20Sopenharmony_ci				seq_putc(m, sta->u.ap.ssid[i]);
10528c2ecf20Sopenharmony_ci			else
10538c2ecf20Sopenharmony_ci				seq_printf(m, "<%02x>", sta->u.ap.ssid[i]);
10548c2ecf20Sopenharmony_ci		}
10558c2ecf20Sopenharmony_ci		seq_putc(m, '\n');
10568c2ecf20Sopenharmony_ci	}
10578c2ecf20Sopenharmony_ci#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */
10588c2ecf20Sopenharmony_ci
10598c2ecf20Sopenharmony_ci	return 0;
10608c2ecf20Sopenharmony_ci}
10618c2ecf20Sopenharmony_ci#endif
10628c2ecf20Sopenharmony_ci
10638c2ecf20Sopenharmony_cistatic void handle_add_proc_queue(struct work_struct *work)
10648c2ecf20Sopenharmony_ci{
10658c2ecf20Sopenharmony_ci	struct ap_data *ap = container_of(work, struct ap_data,
10668c2ecf20Sopenharmony_ci					  add_sta_proc_queue);
10678c2ecf20Sopenharmony_ci	struct sta_info *sta;
10688c2ecf20Sopenharmony_ci	char name[20];
10698c2ecf20Sopenharmony_ci	struct add_sta_proc_data *entry, *prev;
10708c2ecf20Sopenharmony_ci
10718c2ecf20Sopenharmony_ci	entry = ap->add_sta_proc_entries;
10728c2ecf20Sopenharmony_ci	ap->add_sta_proc_entries = NULL;
10738c2ecf20Sopenharmony_ci
10748c2ecf20Sopenharmony_ci	while (entry) {
10758c2ecf20Sopenharmony_ci		spin_lock_bh(&ap->sta_table_lock);
10768c2ecf20Sopenharmony_ci		sta = ap_get_sta(ap, entry->addr);
10778c2ecf20Sopenharmony_ci		if (sta)
10788c2ecf20Sopenharmony_ci			atomic_inc(&sta->users);
10798c2ecf20Sopenharmony_ci		spin_unlock_bh(&ap->sta_table_lock);
10808c2ecf20Sopenharmony_ci
10818c2ecf20Sopenharmony_ci		if (sta) {
10828c2ecf20Sopenharmony_ci			sprintf(name, "%pM", sta->addr);
10838c2ecf20Sopenharmony_ci			sta->proc = proc_create_single_data(
10848c2ecf20Sopenharmony_ci				name, 0, ap->proc,
10858c2ecf20Sopenharmony_ci				prism2_sta_proc_show, sta);
10868c2ecf20Sopenharmony_ci
10878c2ecf20Sopenharmony_ci			atomic_dec(&sta->users);
10888c2ecf20Sopenharmony_ci		}
10898c2ecf20Sopenharmony_ci
10908c2ecf20Sopenharmony_ci		prev = entry;
10918c2ecf20Sopenharmony_ci		entry = entry->next;
10928c2ecf20Sopenharmony_ci		kfree(prev);
10938c2ecf20Sopenharmony_ci	}
10948c2ecf20Sopenharmony_ci}
10958c2ecf20Sopenharmony_ci
10968c2ecf20Sopenharmony_ci
10978c2ecf20Sopenharmony_cistatic struct sta_info * ap_add_sta(struct ap_data *ap, u8 *addr)
10988c2ecf20Sopenharmony_ci{
10998c2ecf20Sopenharmony_ci	struct sta_info *sta;
11008c2ecf20Sopenharmony_ci
11018c2ecf20Sopenharmony_ci	sta = kzalloc(sizeof(struct sta_info), GFP_ATOMIC);
11028c2ecf20Sopenharmony_ci	if (sta == NULL) {
11038c2ecf20Sopenharmony_ci		PDEBUG(DEBUG_AP, "AP: kmalloc failed\n");
11048c2ecf20Sopenharmony_ci		return NULL;
11058c2ecf20Sopenharmony_ci	}
11068c2ecf20Sopenharmony_ci
11078c2ecf20Sopenharmony_ci	/* initialize STA info data */
11088c2ecf20Sopenharmony_ci	sta->local = ap->local;
11098c2ecf20Sopenharmony_ci	skb_queue_head_init(&sta->tx_buf);
11108c2ecf20Sopenharmony_ci	memcpy(sta->addr, addr, ETH_ALEN);
11118c2ecf20Sopenharmony_ci
11128c2ecf20Sopenharmony_ci	atomic_inc(&sta->users);
11138c2ecf20Sopenharmony_ci	spin_lock_bh(&ap->sta_table_lock);
11148c2ecf20Sopenharmony_ci	list_add(&sta->list, &ap->sta_list);
11158c2ecf20Sopenharmony_ci	ap->num_sta++;
11168c2ecf20Sopenharmony_ci	ap_sta_hash_add(ap, sta);
11178c2ecf20Sopenharmony_ci	spin_unlock_bh(&ap->sta_table_lock);
11188c2ecf20Sopenharmony_ci
11198c2ecf20Sopenharmony_ci	if (ap->proc) {
11208c2ecf20Sopenharmony_ci		struct add_sta_proc_data *entry;
11218c2ecf20Sopenharmony_ci		/* schedule a non-interrupt context process to add a procfs
11228c2ecf20Sopenharmony_ci		 * entry for the STA since procfs code use GFP_KERNEL */
11238c2ecf20Sopenharmony_ci		entry = kmalloc(sizeof(*entry), GFP_ATOMIC);
11248c2ecf20Sopenharmony_ci		if (entry) {
11258c2ecf20Sopenharmony_ci			memcpy(entry->addr, sta->addr, ETH_ALEN);
11268c2ecf20Sopenharmony_ci			entry->next = ap->add_sta_proc_entries;
11278c2ecf20Sopenharmony_ci			ap->add_sta_proc_entries = entry;
11288c2ecf20Sopenharmony_ci			schedule_work(&ap->add_sta_proc_queue);
11298c2ecf20Sopenharmony_ci		} else
11308c2ecf20Sopenharmony_ci			printk(KERN_DEBUG "Failed to add STA proc data\n");
11318c2ecf20Sopenharmony_ci	}
11328c2ecf20Sopenharmony_ci
11338c2ecf20Sopenharmony_ci#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT
11348c2ecf20Sopenharmony_ci	timer_setup(&sta->timer, ap_handle_timer, 0);
11358c2ecf20Sopenharmony_ci	sta->timer.expires = jiffies + ap->max_inactivity;
11368c2ecf20Sopenharmony_ci	if (!ap->local->hostapd)
11378c2ecf20Sopenharmony_ci		add_timer(&sta->timer);
11388c2ecf20Sopenharmony_ci#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */
11398c2ecf20Sopenharmony_ci
11408c2ecf20Sopenharmony_ci	return sta;
11418c2ecf20Sopenharmony_ci}
11428c2ecf20Sopenharmony_ci
11438c2ecf20Sopenharmony_ci
11448c2ecf20Sopenharmony_cistatic int ap_tx_rate_ok(int rateidx, struct sta_info *sta,
11458c2ecf20Sopenharmony_ci			 local_info_t *local)
11468c2ecf20Sopenharmony_ci{
11478c2ecf20Sopenharmony_ci	if (rateidx > sta->tx_max_rate ||
11488c2ecf20Sopenharmony_ci	    !(sta->tx_supp_rates & (1 << rateidx)))
11498c2ecf20Sopenharmony_ci		return 0;
11508c2ecf20Sopenharmony_ci
11518c2ecf20Sopenharmony_ci	if (local->tx_rate_control != 0 &&
11528c2ecf20Sopenharmony_ci	    !(local->tx_rate_control & (1 << rateidx)))
11538c2ecf20Sopenharmony_ci		return 0;
11548c2ecf20Sopenharmony_ci
11558c2ecf20Sopenharmony_ci	return 1;
11568c2ecf20Sopenharmony_ci}
11578c2ecf20Sopenharmony_ci
11588c2ecf20Sopenharmony_ci
11598c2ecf20Sopenharmony_cistatic void prism2_check_tx_rates(struct sta_info *sta)
11608c2ecf20Sopenharmony_ci{
11618c2ecf20Sopenharmony_ci	int i;
11628c2ecf20Sopenharmony_ci
11638c2ecf20Sopenharmony_ci	sta->tx_supp_rates = 0;
11648c2ecf20Sopenharmony_ci	for (i = 0; i < sizeof(sta->supported_rates); i++) {
11658c2ecf20Sopenharmony_ci		if ((sta->supported_rates[i] & 0x7f) == 2)
11668c2ecf20Sopenharmony_ci			sta->tx_supp_rates |= WLAN_RATE_1M;
11678c2ecf20Sopenharmony_ci		if ((sta->supported_rates[i] & 0x7f) == 4)
11688c2ecf20Sopenharmony_ci			sta->tx_supp_rates |= WLAN_RATE_2M;
11698c2ecf20Sopenharmony_ci		if ((sta->supported_rates[i] & 0x7f) == 11)
11708c2ecf20Sopenharmony_ci			sta->tx_supp_rates |= WLAN_RATE_5M5;
11718c2ecf20Sopenharmony_ci		if ((sta->supported_rates[i] & 0x7f) == 22)
11728c2ecf20Sopenharmony_ci			sta->tx_supp_rates |= WLAN_RATE_11M;
11738c2ecf20Sopenharmony_ci	}
11748c2ecf20Sopenharmony_ci	sta->tx_max_rate = sta->tx_rate = sta->tx_rate_idx = 0;
11758c2ecf20Sopenharmony_ci	if (sta->tx_supp_rates & WLAN_RATE_1M) {
11768c2ecf20Sopenharmony_ci		sta->tx_max_rate = 0;
11778c2ecf20Sopenharmony_ci		if (ap_tx_rate_ok(0, sta, sta->local)) {
11788c2ecf20Sopenharmony_ci			sta->tx_rate = 10;
11798c2ecf20Sopenharmony_ci			sta->tx_rate_idx = 0;
11808c2ecf20Sopenharmony_ci		}
11818c2ecf20Sopenharmony_ci	}
11828c2ecf20Sopenharmony_ci	if (sta->tx_supp_rates & WLAN_RATE_2M) {
11838c2ecf20Sopenharmony_ci		sta->tx_max_rate = 1;
11848c2ecf20Sopenharmony_ci		if (ap_tx_rate_ok(1, sta, sta->local)) {
11858c2ecf20Sopenharmony_ci			sta->tx_rate = 20;
11868c2ecf20Sopenharmony_ci			sta->tx_rate_idx = 1;
11878c2ecf20Sopenharmony_ci		}
11888c2ecf20Sopenharmony_ci	}
11898c2ecf20Sopenharmony_ci	if (sta->tx_supp_rates & WLAN_RATE_5M5) {
11908c2ecf20Sopenharmony_ci		sta->tx_max_rate = 2;
11918c2ecf20Sopenharmony_ci		if (ap_tx_rate_ok(2, sta, sta->local)) {
11928c2ecf20Sopenharmony_ci			sta->tx_rate = 55;
11938c2ecf20Sopenharmony_ci			sta->tx_rate_idx = 2;
11948c2ecf20Sopenharmony_ci		}
11958c2ecf20Sopenharmony_ci	}
11968c2ecf20Sopenharmony_ci	if (sta->tx_supp_rates & WLAN_RATE_11M) {
11978c2ecf20Sopenharmony_ci		sta->tx_max_rate = 3;
11988c2ecf20Sopenharmony_ci		if (ap_tx_rate_ok(3, sta, sta->local)) {
11998c2ecf20Sopenharmony_ci			sta->tx_rate = 110;
12008c2ecf20Sopenharmony_ci			sta->tx_rate_idx = 3;
12018c2ecf20Sopenharmony_ci		}
12028c2ecf20Sopenharmony_ci	}
12038c2ecf20Sopenharmony_ci}
12048c2ecf20Sopenharmony_ci
12058c2ecf20Sopenharmony_ci
12068c2ecf20Sopenharmony_ci#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT
12078c2ecf20Sopenharmony_ci
12088c2ecf20Sopenharmony_cistatic void ap_crypt_init(struct ap_data *ap)
12098c2ecf20Sopenharmony_ci{
12108c2ecf20Sopenharmony_ci	ap->crypt = lib80211_get_crypto_ops("WEP");
12118c2ecf20Sopenharmony_ci
12128c2ecf20Sopenharmony_ci	if (ap->crypt) {
12138c2ecf20Sopenharmony_ci		if (ap->crypt->init) {
12148c2ecf20Sopenharmony_ci			ap->crypt_priv = ap->crypt->init(0);
12158c2ecf20Sopenharmony_ci			if (ap->crypt_priv == NULL)
12168c2ecf20Sopenharmony_ci				ap->crypt = NULL;
12178c2ecf20Sopenharmony_ci			else {
12188c2ecf20Sopenharmony_ci				u8 key[WEP_KEY_LEN];
12198c2ecf20Sopenharmony_ci				get_random_bytes(key, WEP_KEY_LEN);
12208c2ecf20Sopenharmony_ci				ap->crypt->set_key(key, WEP_KEY_LEN, NULL,
12218c2ecf20Sopenharmony_ci						   ap->crypt_priv);
12228c2ecf20Sopenharmony_ci			}
12238c2ecf20Sopenharmony_ci		}
12248c2ecf20Sopenharmony_ci	}
12258c2ecf20Sopenharmony_ci
12268c2ecf20Sopenharmony_ci	if (ap->crypt == NULL) {
12278c2ecf20Sopenharmony_ci		printk(KERN_WARNING "AP could not initialize WEP: load module "
12288c2ecf20Sopenharmony_ci		       "lib80211_crypt_wep.ko\n");
12298c2ecf20Sopenharmony_ci	}
12308c2ecf20Sopenharmony_ci}
12318c2ecf20Sopenharmony_ci
12328c2ecf20Sopenharmony_ci
12338c2ecf20Sopenharmony_ci/* Generate challenge data for shared key authentication. IEEE 802.11 specifies
12348c2ecf20Sopenharmony_ci * that WEP algorithm is used for generating challenge. This should be unique,
12358c2ecf20Sopenharmony_ci * but otherwise there is not really need for randomness etc. Initialize WEP
12368c2ecf20Sopenharmony_ci * with pseudo random key and then use increasing IV to get unique challenge
12378c2ecf20Sopenharmony_ci * streams.
12388c2ecf20Sopenharmony_ci *
12398c2ecf20Sopenharmony_ci * Called only as a scheduled task for pending AP frames.
12408c2ecf20Sopenharmony_ci */
12418c2ecf20Sopenharmony_cistatic char * ap_auth_make_challenge(struct ap_data *ap)
12428c2ecf20Sopenharmony_ci{
12438c2ecf20Sopenharmony_ci	char *tmpbuf;
12448c2ecf20Sopenharmony_ci	struct sk_buff *skb;
12458c2ecf20Sopenharmony_ci
12468c2ecf20Sopenharmony_ci	if (ap->crypt == NULL) {
12478c2ecf20Sopenharmony_ci		ap_crypt_init(ap);
12488c2ecf20Sopenharmony_ci		if (ap->crypt == NULL)
12498c2ecf20Sopenharmony_ci			return NULL;
12508c2ecf20Sopenharmony_ci	}
12518c2ecf20Sopenharmony_ci
12528c2ecf20Sopenharmony_ci	tmpbuf = kmalloc(WLAN_AUTH_CHALLENGE_LEN, GFP_ATOMIC);
12538c2ecf20Sopenharmony_ci	if (tmpbuf == NULL) {
12548c2ecf20Sopenharmony_ci		PDEBUG(DEBUG_AP, "AP: kmalloc failed for challenge\n");
12558c2ecf20Sopenharmony_ci		return NULL;
12568c2ecf20Sopenharmony_ci	}
12578c2ecf20Sopenharmony_ci
12588c2ecf20Sopenharmony_ci	skb = dev_alloc_skb(WLAN_AUTH_CHALLENGE_LEN +
12598c2ecf20Sopenharmony_ci			    ap->crypt->extra_mpdu_prefix_len +
12608c2ecf20Sopenharmony_ci			    ap->crypt->extra_mpdu_postfix_len);
12618c2ecf20Sopenharmony_ci	if (skb == NULL) {
12628c2ecf20Sopenharmony_ci		kfree(tmpbuf);
12638c2ecf20Sopenharmony_ci		return NULL;
12648c2ecf20Sopenharmony_ci	}
12658c2ecf20Sopenharmony_ci
12668c2ecf20Sopenharmony_ci	skb_reserve(skb, ap->crypt->extra_mpdu_prefix_len);
12678c2ecf20Sopenharmony_ci	skb_put_zero(skb, WLAN_AUTH_CHALLENGE_LEN);
12688c2ecf20Sopenharmony_ci	if (ap->crypt->encrypt_mpdu(skb, 0, ap->crypt_priv)) {
12698c2ecf20Sopenharmony_ci		dev_kfree_skb(skb);
12708c2ecf20Sopenharmony_ci		kfree(tmpbuf);
12718c2ecf20Sopenharmony_ci		return NULL;
12728c2ecf20Sopenharmony_ci	}
12738c2ecf20Sopenharmony_ci
12748c2ecf20Sopenharmony_ci	skb_copy_from_linear_data_offset(skb, ap->crypt->extra_mpdu_prefix_len,
12758c2ecf20Sopenharmony_ci					 tmpbuf, WLAN_AUTH_CHALLENGE_LEN);
12768c2ecf20Sopenharmony_ci	dev_kfree_skb(skb);
12778c2ecf20Sopenharmony_ci
12788c2ecf20Sopenharmony_ci	return tmpbuf;
12798c2ecf20Sopenharmony_ci}
12808c2ecf20Sopenharmony_ci
12818c2ecf20Sopenharmony_ci
12828c2ecf20Sopenharmony_ci/* Called only as a scheduled task for pending AP frames. */
12838c2ecf20Sopenharmony_cistatic void handle_authen(local_info_t *local, struct sk_buff *skb,
12848c2ecf20Sopenharmony_ci			  struct hostap_80211_rx_status *rx_stats)
12858c2ecf20Sopenharmony_ci{
12868c2ecf20Sopenharmony_ci	struct net_device *dev = local->dev;
12878c2ecf20Sopenharmony_ci	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
12888c2ecf20Sopenharmony_ci	size_t hdrlen;
12898c2ecf20Sopenharmony_ci	struct ap_data *ap = local->ap;
12908c2ecf20Sopenharmony_ci	char body[8 + WLAN_AUTH_CHALLENGE_LEN], *challenge = NULL;
12918c2ecf20Sopenharmony_ci	int len, olen;
12928c2ecf20Sopenharmony_ci	u16 auth_alg, auth_transaction, status_code;
12938c2ecf20Sopenharmony_ci	__le16 *pos;
12948c2ecf20Sopenharmony_ci	u16 resp = WLAN_STATUS_SUCCESS;
12958c2ecf20Sopenharmony_ci	struct sta_info *sta = NULL;
12968c2ecf20Sopenharmony_ci	struct lib80211_crypt_data *crypt;
12978c2ecf20Sopenharmony_ci	char *txt = "";
12988c2ecf20Sopenharmony_ci
12998c2ecf20Sopenharmony_ci	len = skb->len - IEEE80211_MGMT_HDR_LEN;
13008c2ecf20Sopenharmony_ci
13018c2ecf20Sopenharmony_ci	hdrlen = hostap_80211_get_hdrlen(hdr->frame_control);
13028c2ecf20Sopenharmony_ci
13038c2ecf20Sopenharmony_ci	if (len < 6) {
13048c2ecf20Sopenharmony_ci		PDEBUG(DEBUG_AP, "%s: handle_authen - too short payload "
13058c2ecf20Sopenharmony_ci		       "(len=%d) from %pM\n", dev->name, len, hdr->addr2);
13068c2ecf20Sopenharmony_ci		return;
13078c2ecf20Sopenharmony_ci	}
13088c2ecf20Sopenharmony_ci
13098c2ecf20Sopenharmony_ci	spin_lock_bh(&local->ap->sta_table_lock);
13108c2ecf20Sopenharmony_ci	sta = ap_get_sta(local->ap, hdr->addr2);
13118c2ecf20Sopenharmony_ci	if (sta)
13128c2ecf20Sopenharmony_ci		atomic_inc(&sta->users);
13138c2ecf20Sopenharmony_ci	spin_unlock_bh(&local->ap->sta_table_lock);
13148c2ecf20Sopenharmony_ci
13158c2ecf20Sopenharmony_ci	if (sta && sta->crypt)
13168c2ecf20Sopenharmony_ci		crypt = sta->crypt;
13178c2ecf20Sopenharmony_ci	else {
13188c2ecf20Sopenharmony_ci		int idx = 0;
13198c2ecf20Sopenharmony_ci		if (skb->len >= hdrlen + 3)
13208c2ecf20Sopenharmony_ci			idx = skb->data[hdrlen + 3] >> 6;
13218c2ecf20Sopenharmony_ci		crypt = local->crypt_info.crypt[idx];
13228c2ecf20Sopenharmony_ci	}
13238c2ecf20Sopenharmony_ci
13248c2ecf20Sopenharmony_ci	pos = (__le16 *) (skb->data + IEEE80211_MGMT_HDR_LEN);
13258c2ecf20Sopenharmony_ci	auth_alg = __le16_to_cpu(*pos);
13268c2ecf20Sopenharmony_ci	pos++;
13278c2ecf20Sopenharmony_ci	auth_transaction = __le16_to_cpu(*pos);
13288c2ecf20Sopenharmony_ci	pos++;
13298c2ecf20Sopenharmony_ci	status_code = __le16_to_cpu(*pos);
13308c2ecf20Sopenharmony_ci	pos++;
13318c2ecf20Sopenharmony_ci
13328c2ecf20Sopenharmony_ci	if (ether_addr_equal(dev->dev_addr, hdr->addr2) ||
13338c2ecf20Sopenharmony_ci	    ap_control_mac_deny(&ap->mac_restrictions, hdr->addr2)) {
13348c2ecf20Sopenharmony_ci		txt = "authentication denied";
13358c2ecf20Sopenharmony_ci		resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
13368c2ecf20Sopenharmony_ci		goto fail;
13378c2ecf20Sopenharmony_ci	}
13388c2ecf20Sopenharmony_ci
13398c2ecf20Sopenharmony_ci	if (((local->auth_algs & PRISM2_AUTH_OPEN) &&
13408c2ecf20Sopenharmony_ci	     auth_alg == WLAN_AUTH_OPEN) ||
13418c2ecf20Sopenharmony_ci	    ((local->auth_algs & PRISM2_AUTH_SHARED_KEY) &&
13428c2ecf20Sopenharmony_ci	     crypt && auth_alg == WLAN_AUTH_SHARED_KEY)) {
13438c2ecf20Sopenharmony_ci	} else {
13448c2ecf20Sopenharmony_ci		txt = "unsupported algorithm";
13458c2ecf20Sopenharmony_ci		resp = WLAN_STATUS_NOT_SUPPORTED_AUTH_ALG;
13468c2ecf20Sopenharmony_ci		goto fail;
13478c2ecf20Sopenharmony_ci	}
13488c2ecf20Sopenharmony_ci
13498c2ecf20Sopenharmony_ci	if (len >= 8) {
13508c2ecf20Sopenharmony_ci		u8 *u = (u8 *) pos;
13518c2ecf20Sopenharmony_ci		if (*u == WLAN_EID_CHALLENGE) {
13528c2ecf20Sopenharmony_ci			if (*(u + 1) != WLAN_AUTH_CHALLENGE_LEN) {
13538c2ecf20Sopenharmony_ci				txt = "invalid challenge len";
13548c2ecf20Sopenharmony_ci				resp = WLAN_STATUS_CHALLENGE_FAIL;
13558c2ecf20Sopenharmony_ci				goto fail;
13568c2ecf20Sopenharmony_ci			}
13578c2ecf20Sopenharmony_ci			if (len - 8 < WLAN_AUTH_CHALLENGE_LEN) {
13588c2ecf20Sopenharmony_ci				txt = "challenge underflow";
13598c2ecf20Sopenharmony_ci				resp = WLAN_STATUS_CHALLENGE_FAIL;
13608c2ecf20Sopenharmony_ci				goto fail;
13618c2ecf20Sopenharmony_ci			}
13628c2ecf20Sopenharmony_ci			challenge = (char *) (u + 2);
13638c2ecf20Sopenharmony_ci		}
13648c2ecf20Sopenharmony_ci	}
13658c2ecf20Sopenharmony_ci
13668c2ecf20Sopenharmony_ci	if (sta && sta->ap) {
13678c2ecf20Sopenharmony_ci		if (time_after(jiffies, sta->u.ap.last_beacon +
13688c2ecf20Sopenharmony_ci			       (10 * sta->listen_interval * HZ) / 1024)) {
13698c2ecf20Sopenharmony_ci			PDEBUG(DEBUG_AP, "%s: no beacons received for a while,"
13708c2ecf20Sopenharmony_ci			       " assuming AP %pM is now STA\n",
13718c2ecf20Sopenharmony_ci			       dev->name, sta->addr);
13728c2ecf20Sopenharmony_ci			sta->ap = 0;
13738c2ecf20Sopenharmony_ci			sta->flags = 0;
13748c2ecf20Sopenharmony_ci			sta->u.sta.challenge = NULL;
13758c2ecf20Sopenharmony_ci		} else {
13768c2ecf20Sopenharmony_ci			txt = "AP trying to authenticate?";
13778c2ecf20Sopenharmony_ci			resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
13788c2ecf20Sopenharmony_ci			goto fail;
13798c2ecf20Sopenharmony_ci		}
13808c2ecf20Sopenharmony_ci	}
13818c2ecf20Sopenharmony_ci
13828c2ecf20Sopenharmony_ci	if ((auth_alg == WLAN_AUTH_OPEN && auth_transaction == 1) ||
13838c2ecf20Sopenharmony_ci	    (auth_alg == WLAN_AUTH_SHARED_KEY &&
13848c2ecf20Sopenharmony_ci	     (auth_transaction == 1 ||
13858c2ecf20Sopenharmony_ci	      (auth_transaction == 3 && sta != NULL &&
13868c2ecf20Sopenharmony_ci	       sta->u.sta.challenge != NULL)))) {
13878c2ecf20Sopenharmony_ci	} else {
13888c2ecf20Sopenharmony_ci		txt = "unknown authentication transaction number";
13898c2ecf20Sopenharmony_ci		resp = WLAN_STATUS_UNKNOWN_AUTH_TRANSACTION;
13908c2ecf20Sopenharmony_ci		goto fail;
13918c2ecf20Sopenharmony_ci	}
13928c2ecf20Sopenharmony_ci
13938c2ecf20Sopenharmony_ci	if (sta == NULL) {
13948c2ecf20Sopenharmony_ci		txt = "new STA";
13958c2ecf20Sopenharmony_ci
13968c2ecf20Sopenharmony_ci		if (local->ap->num_sta >= MAX_STA_COUNT) {
13978c2ecf20Sopenharmony_ci			/* FIX: might try to remove some old STAs first? */
13988c2ecf20Sopenharmony_ci			txt = "no more room for new STAs";
13998c2ecf20Sopenharmony_ci			resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
14008c2ecf20Sopenharmony_ci			goto fail;
14018c2ecf20Sopenharmony_ci		}
14028c2ecf20Sopenharmony_ci
14038c2ecf20Sopenharmony_ci		sta = ap_add_sta(local->ap, hdr->addr2);
14048c2ecf20Sopenharmony_ci		if (sta == NULL) {
14058c2ecf20Sopenharmony_ci			txt = "ap_add_sta failed";
14068c2ecf20Sopenharmony_ci			resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
14078c2ecf20Sopenharmony_ci			goto fail;
14088c2ecf20Sopenharmony_ci		}
14098c2ecf20Sopenharmony_ci	}
14108c2ecf20Sopenharmony_ci
14118c2ecf20Sopenharmony_ci	switch (auth_alg) {
14128c2ecf20Sopenharmony_ci	case WLAN_AUTH_OPEN:
14138c2ecf20Sopenharmony_ci		txt = "authOK";
14148c2ecf20Sopenharmony_ci		/* IEEE 802.11 standard is not completely clear about
14158c2ecf20Sopenharmony_ci		 * whether STA is considered authenticated after
14168c2ecf20Sopenharmony_ci		 * authentication OK frame has been send or after it
14178c2ecf20Sopenharmony_ci		 * has been ACKed. In order to reduce interoperability
14188c2ecf20Sopenharmony_ci		 * issues, mark the STA authenticated before ACK. */
14198c2ecf20Sopenharmony_ci		sta->flags |= WLAN_STA_AUTH;
14208c2ecf20Sopenharmony_ci		break;
14218c2ecf20Sopenharmony_ci
14228c2ecf20Sopenharmony_ci	case WLAN_AUTH_SHARED_KEY:
14238c2ecf20Sopenharmony_ci		if (auth_transaction == 1) {
14248c2ecf20Sopenharmony_ci			if (sta->u.sta.challenge == NULL) {
14258c2ecf20Sopenharmony_ci				sta->u.sta.challenge =
14268c2ecf20Sopenharmony_ci					ap_auth_make_challenge(local->ap);
14278c2ecf20Sopenharmony_ci				if (sta->u.sta.challenge == NULL) {
14288c2ecf20Sopenharmony_ci					resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
14298c2ecf20Sopenharmony_ci					goto fail;
14308c2ecf20Sopenharmony_ci				}
14318c2ecf20Sopenharmony_ci			}
14328c2ecf20Sopenharmony_ci		} else {
14338c2ecf20Sopenharmony_ci			if (sta->u.sta.challenge == NULL ||
14348c2ecf20Sopenharmony_ci			    challenge == NULL ||
14358c2ecf20Sopenharmony_ci			    memcmp(sta->u.sta.challenge, challenge,
14368c2ecf20Sopenharmony_ci				   WLAN_AUTH_CHALLENGE_LEN) != 0 ||
14378c2ecf20Sopenharmony_ci			    !ieee80211_has_protected(hdr->frame_control)) {
14388c2ecf20Sopenharmony_ci				txt = "challenge response incorrect";
14398c2ecf20Sopenharmony_ci				resp = WLAN_STATUS_CHALLENGE_FAIL;
14408c2ecf20Sopenharmony_ci				goto fail;
14418c2ecf20Sopenharmony_ci			}
14428c2ecf20Sopenharmony_ci
14438c2ecf20Sopenharmony_ci			txt = "challenge OK - authOK";
14448c2ecf20Sopenharmony_ci			/* IEEE 802.11 standard is not completely clear about
14458c2ecf20Sopenharmony_ci			 * whether STA is considered authenticated after
14468c2ecf20Sopenharmony_ci			 * authentication OK frame has been send or after it
14478c2ecf20Sopenharmony_ci			 * has been ACKed. In order to reduce interoperability
14488c2ecf20Sopenharmony_ci			 * issues, mark the STA authenticated before ACK. */
14498c2ecf20Sopenharmony_ci			sta->flags |= WLAN_STA_AUTH;
14508c2ecf20Sopenharmony_ci			kfree(sta->u.sta.challenge);
14518c2ecf20Sopenharmony_ci			sta->u.sta.challenge = NULL;
14528c2ecf20Sopenharmony_ci		}
14538c2ecf20Sopenharmony_ci		break;
14548c2ecf20Sopenharmony_ci	}
14558c2ecf20Sopenharmony_ci
14568c2ecf20Sopenharmony_ci fail:
14578c2ecf20Sopenharmony_ci	pos = (__le16 *) body;
14588c2ecf20Sopenharmony_ci	*pos = cpu_to_le16(auth_alg);
14598c2ecf20Sopenharmony_ci	pos++;
14608c2ecf20Sopenharmony_ci	*pos = cpu_to_le16(auth_transaction + 1);
14618c2ecf20Sopenharmony_ci	pos++;
14628c2ecf20Sopenharmony_ci	*pos = cpu_to_le16(resp); /* status_code */
14638c2ecf20Sopenharmony_ci	pos++;
14648c2ecf20Sopenharmony_ci	olen = 6;
14658c2ecf20Sopenharmony_ci
14668c2ecf20Sopenharmony_ci	if (resp == WLAN_STATUS_SUCCESS && sta != NULL &&
14678c2ecf20Sopenharmony_ci	    sta->u.sta.challenge != NULL &&
14688c2ecf20Sopenharmony_ci	    auth_alg == WLAN_AUTH_SHARED_KEY && auth_transaction == 1) {
14698c2ecf20Sopenharmony_ci		u8 *tmp = (u8 *) pos;
14708c2ecf20Sopenharmony_ci		*tmp++ = WLAN_EID_CHALLENGE;
14718c2ecf20Sopenharmony_ci		*tmp++ = WLAN_AUTH_CHALLENGE_LEN;
14728c2ecf20Sopenharmony_ci		pos++;
14738c2ecf20Sopenharmony_ci		memcpy(pos, sta->u.sta.challenge, WLAN_AUTH_CHALLENGE_LEN);
14748c2ecf20Sopenharmony_ci		olen += 2 + WLAN_AUTH_CHALLENGE_LEN;
14758c2ecf20Sopenharmony_ci	}
14768c2ecf20Sopenharmony_ci
14778c2ecf20Sopenharmony_ci	prism2_send_mgmt(dev, IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_AUTH,
14788c2ecf20Sopenharmony_ci			 body, olen, hdr->addr2, ap->tx_callback_auth);
14798c2ecf20Sopenharmony_ci
14808c2ecf20Sopenharmony_ci	if (sta) {
14818c2ecf20Sopenharmony_ci		sta->last_rx = jiffies;
14828c2ecf20Sopenharmony_ci		atomic_dec(&sta->users);
14838c2ecf20Sopenharmony_ci	}
14848c2ecf20Sopenharmony_ci
14858c2ecf20Sopenharmony_ci	if (resp) {
14868c2ecf20Sopenharmony_ci		PDEBUG(DEBUG_AP, "%s: %pM auth (alg=%d "
14878c2ecf20Sopenharmony_ci		       "trans#=%d stat=%d len=%d fc=%04x) ==> %d (%s)\n",
14888c2ecf20Sopenharmony_ci		       dev->name, hdr->addr2,
14898c2ecf20Sopenharmony_ci		       auth_alg, auth_transaction, status_code, len,
14908c2ecf20Sopenharmony_ci		       le16_to_cpu(hdr->frame_control), resp, txt);
14918c2ecf20Sopenharmony_ci	}
14928c2ecf20Sopenharmony_ci}
14938c2ecf20Sopenharmony_ci
14948c2ecf20Sopenharmony_ci
14958c2ecf20Sopenharmony_ci/* Called only as a scheduled task for pending AP frames. */
14968c2ecf20Sopenharmony_cistatic void handle_assoc(local_info_t *local, struct sk_buff *skb,
14978c2ecf20Sopenharmony_ci			 struct hostap_80211_rx_status *rx_stats, int reassoc)
14988c2ecf20Sopenharmony_ci{
14998c2ecf20Sopenharmony_ci	struct net_device *dev = local->dev;
15008c2ecf20Sopenharmony_ci	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
15018c2ecf20Sopenharmony_ci	char body[12], *p, *lpos;
15028c2ecf20Sopenharmony_ci	int len, left;
15038c2ecf20Sopenharmony_ci	__le16 *pos;
15048c2ecf20Sopenharmony_ci	u16 resp = WLAN_STATUS_SUCCESS;
15058c2ecf20Sopenharmony_ci	struct sta_info *sta = NULL;
15068c2ecf20Sopenharmony_ci	int send_deauth = 0;
15078c2ecf20Sopenharmony_ci	char __always_unused *txt = "";
15088c2ecf20Sopenharmony_ci	u8 prev_ap[ETH_ALEN];
15098c2ecf20Sopenharmony_ci
15108c2ecf20Sopenharmony_ci	left = len = skb->len - IEEE80211_MGMT_HDR_LEN;
15118c2ecf20Sopenharmony_ci
15128c2ecf20Sopenharmony_ci	if (len < (reassoc ? 10 : 4)) {
15138c2ecf20Sopenharmony_ci		PDEBUG(DEBUG_AP, "%s: handle_assoc - too short payload "
15148c2ecf20Sopenharmony_ci		       "(len=%d, reassoc=%d) from %pM\n",
15158c2ecf20Sopenharmony_ci		       dev->name, len, reassoc, hdr->addr2);
15168c2ecf20Sopenharmony_ci		return;
15178c2ecf20Sopenharmony_ci	}
15188c2ecf20Sopenharmony_ci
15198c2ecf20Sopenharmony_ci	spin_lock_bh(&local->ap->sta_table_lock);
15208c2ecf20Sopenharmony_ci	sta = ap_get_sta(local->ap, hdr->addr2);
15218c2ecf20Sopenharmony_ci	if (sta == NULL || (sta->flags & WLAN_STA_AUTH) == 0) {
15228c2ecf20Sopenharmony_ci		spin_unlock_bh(&local->ap->sta_table_lock);
15238c2ecf20Sopenharmony_ci		txt = "trying to associate before authentication";
15248c2ecf20Sopenharmony_ci		send_deauth = 1;
15258c2ecf20Sopenharmony_ci		resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
15268c2ecf20Sopenharmony_ci		sta = NULL; /* do not decrement sta->users */
15278c2ecf20Sopenharmony_ci		goto fail;
15288c2ecf20Sopenharmony_ci	}
15298c2ecf20Sopenharmony_ci	atomic_inc(&sta->users);
15308c2ecf20Sopenharmony_ci	spin_unlock_bh(&local->ap->sta_table_lock);
15318c2ecf20Sopenharmony_ci
15328c2ecf20Sopenharmony_ci	pos = (__le16 *) (skb->data + IEEE80211_MGMT_HDR_LEN);
15338c2ecf20Sopenharmony_ci	sta->capability = __le16_to_cpu(*pos);
15348c2ecf20Sopenharmony_ci	pos++; left -= 2;
15358c2ecf20Sopenharmony_ci	sta->listen_interval = __le16_to_cpu(*pos);
15368c2ecf20Sopenharmony_ci	pos++; left -= 2;
15378c2ecf20Sopenharmony_ci
15388c2ecf20Sopenharmony_ci	if (reassoc) {
15398c2ecf20Sopenharmony_ci		memcpy(prev_ap, pos, ETH_ALEN);
15408c2ecf20Sopenharmony_ci		pos++; pos++; pos++; left -= 6;
15418c2ecf20Sopenharmony_ci	} else
15428c2ecf20Sopenharmony_ci		eth_zero_addr(prev_ap);
15438c2ecf20Sopenharmony_ci
15448c2ecf20Sopenharmony_ci	if (left >= 2) {
15458c2ecf20Sopenharmony_ci		unsigned int ileft;
15468c2ecf20Sopenharmony_ci		unsigned char *u = (unsigned char *) pos;
15478c2ecf20Sopenharmony_ci
15488c2ecf20Sopenharmony_ci		if (*u == WLAN_EID_SSID) {
15498c2ecf20Sopenharmony_ci			u++; left--;
15508c2ecf20Sopenharmony_ci			ileft = *u;
15518c2ecf20Sopenharmony_ci			u++; left--;
15528c2ecf20Sopenharmony_ci
15538c2ecf20Sopenharmony_ci			if (ileft > left || ileft > MAX_SSID_LEN) {
15548c2ecf20Sopenharmony_ci				txt = "SSID overflow";
15558c2ecf20Sopenharmony_ci				resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
15568c2ecf20Sopenharmony_ci				goto fail;
15578c2ecf20Sopenharmony_ci			}
15588c2ecf20Sopenharmony_ci
15598c2ecf20Sopenharmony_ci			if (ileft != strlen(local->essid) ||
15608c2ecf20Sopenharmony_ci			    memcmp(local->essid, u, ileft) != 0) {
15618c2ecf20Sopenharmony_ci				txt = "not our SSID";
15628c2ecf20Sopenharmony_ci				resp = WLAN_STATUS_ASSOC_DENIED_UNSPEC;
15638c2ecf20Sopenharmony_ci				goto fail;
15648c2ecf20Sopenharmony_ci			}
15658c2ecf20Sopenharmony_ci
15668c2ecf20Sopenharmony_ci			u += ileft;
15678c2ecf20Sopenharmony_ci			left -= ileft;
15688c2ecf20Sopenharmony_ci		}
15698c2ecf20Sopenharmony_ci
15708c2ecf20Sopenharmony_ci		if (left >= 2 && *u == WLAN_EID_SUPP_RATES) {
15718c2ecf20Sopenharmony_ci			u++; left--;
15728c2ecf20Sopenharmony_ci			ileft = *u;
15738c2ecf20Sopenharmony_ci			u++; left--;
15748c2ecf20Sopenharmony_ci
15758c2ecf20Sopenharmony_ci			if (ileft > left || ileft == 0 ||
15768c2ecf20Sopenharmony_ci			    ileft > WLAN_SUPP_RATES_MAX) {
15778c2ecf20Sopenharmony_ci				txt = "SUPP_RATES len error";
15788c2ecf20Sopenharmony_ci				resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
15798c2ecf20Sopenharmony_ci				goto fail;
15808c2ecf20Sopenharmony_ci			}
15818c2ecf20Sopenharmony_ci
15828c2ecf20Sopenharmony_ci			memset(sta->supported_rates, 0,
15838c2ecf20Sopenharmony_ci			       sizeof(sta->supported_rates));
15848c2ecf20Sopenharmony_ci			memcpy(sta->supported_rates, u, ileft);
15858c2ecf20Sopenharmony_ci			prism2_check_tx_rates(sta);
15868c2ecf20Sopenharmony_ci
15878c2ecf20Sopenharmony_ci			u += ileft;
15888c2ecf20Sopenharmony_ci			left -= ileft;
15898c2ecf20Sopenharmony_ci		}
15908c2ecf20Sopenharmony_ci
15918c2ecf20Sopenharmony_ci		if (left > 0) {
15928c2ecf20Sopenharmony_ci			PDEBUG(DEBUG_AP, "%s: assoc from %pM"
15938c2ecf20Sopenharmony_ci			       " with extra data (%d bytes) [",
15948c2ecf20Sopenharmony_ci			       dev->name, hdr->addr2, left);
15958c2ecf20Sopenharmony_ci			while (left > 0) {
15968c2ecf20Sopenharmony_ci				PDEBUG2(DEBUG_AP, "<%02x>", *u);
15978c2ecf20Sopenharmony_ci				u++; left--;
15988c2ecf20Sopenharmony_ci			}
15998c2ecf20Sopenharmony_ci			PDEBUG2(DEBUG_AP, "]\n");
16008c2ecf20Sopenharmony_ci		}
16018c2ecf20Sopenharmony_ci	} else {
16028c2ecf20Sopenharmony_ci		txt = "frame underflow";
16038c2ecf20Sopenharmony_ci		resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
16048c2ecf20Sopenharmony_ci		goto fail;
16058c2ecf20Sopenharmony_ci	}
16068c2ecf20Sopenharmony_ci
16078c2ecf20Sopenharmony_ci	/* get a unique AID */
16088c2ecf20Sopenharmony_ci	if (sta->aid > 0)
16098c2ecf20Sopenharmony_ci		txt = "OK, old AID";
16108c2ecf20Sopenharmony_ci	else {
16118c2ecf20Sopenharmony_ci		spin_lock_bh(&local->ap->sta_table_lock);
16128c2ecf20Sopenharmony_ci		for (sta->aid = 1; sta->aid <= MAX_AID_TABLE_SIZE; sta->aid++)
16138c2ecf20Sopenharmony_ci			if (local->ap->sta_aid[sta->aid - 1] == NULL)
16148c2ecf20Sopenharmony_ci				break;
16158c2ecf20Sopenharmony_ci		if (sta->aid > MAX_AID_TABLE_SIZE) {
16168c2ecf20Sopenharmony_ci			sta->aid = 0;
16178c2ecf20Sopenharmony_ci			spin_unlock_bh(&local->ap->sta_table_lock);
16188c2ecf20Sopenharmony_ci			resp = WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA;
16198c2ecf20Sopenharmony_ci			txt = "no room for more AIDs";
16208c2ecf20Sopenharmony_ci		} else {
16218c2ecf20Sopenharmony_ci			local->ap->sta_aid[sta->aid - 1] = sta;
16228c2ecf20Sopenharmony_ci			spin_unlock_bh(&local->ap->sta_table_lock);
16238c2ecf20Sopenharmony_ci			txt = "OK, new AID";
16248c2ecf20Sopenharmony_ci		}
16258c2ecf20Sopenharmony_ci	}
16268c2ecf20Sopenharmony_ci
16278c2ecf20Sopenharmony_ci fail:
16288c2ecf20Sopenharmony_ci	pos = (__le16 *) body;
16298c2ecf20Sopenharmony_ci
16308c2ecf20Sopenharmony_ci	if (send_deauth) {
16318c2ecf20Sopenharmony_ci		*pos = cpu_to_le16(WLAN_REASON_STA_REQ_ASSOC_WITHOUT_AUTH);
16328c2ecf20Sopenharmony_ci		pos++;
16338c2ecf20Sopenharmony_ci	} else {
16348c2ecf20Sopenharmony_ci		/* FIX: CF-Pollable and CF-PollReq should be set to match the
16358c2ecf20Sopenharmony_ci		 * values in beacons/probe responses */
16368c2ecf20Sopenharmony_ci		/* FIX: how about privacy and WEP? */
16378c2ecf20Sopenharmony_ci		/* capability */
16388c2ecf20Sopenharmony_ci		*pos = cpu_to_le16(WLAN_CAPABILITY_ESS);
16398c2ecf20Sopenharmony_ci		pos++;
16408c2ecf20Sopenharmony_ci
16418c2ecf20Sopenharmony_ci		/* status_code */
16428c2ecf20Sopenharmony_ci		*pos = cpu_to_le16(resp);
16438c2ecf20Sopenharmony_ci		pos++;
16448c2ecf20Sopenharmony_ci
16458c2ecf20Sopenharmony_ci		*pos = cpu_to_le16((sta && sta->aid > 0 ? sta->aid : 0) |
16468c2ecf20Sopenharmony_ci				     BIT(14) | BIT(15)); /* AID */
16478c2ecf20Sopenharmony_ci		pos++;
16488c2ecf20Sopenharmony_ci
16498c2ecf20Sopenharmony_ci		/* Supported rates (Information element) */
16508c2ecf20Sopenharmony_ci		p = (char *) pos;
16518c2ecf20Sopenharmony_ci		*p++ = WLAN_EID_SUPP_RATES;
16528c2ecf20Sopenharmony_ci		lpos = p;
16538c2ecf20Sopenharmony_ci		*p++ = 0; /* len */
16548c2ecf20Sopenharmony_ci		if (local->tx_rate_control & WLAN_RATE_1M) {
16558c2ecf20Sopenharmony_ci			*p++ = local->basic_rates & WLAN_RATE_1M ? 0x82 : 0x02;
16568c2ecf20Sopenharmony_ci			(*lpos)++;
16578c2ecf20Sopenharmony_ci		}
16588c2ecf20Sopenharmony_ci		if (local->tx_rate_control & WLAN_RATE_2M) {
16598c2ecf20Sopenharmony_ci			*p++ = local->basic_rates & WLAN_RATE_2M ? 0x84 : 0x04;
16608c2ecf20Sopenharmony_ci			(*lpos)++;
16618c2ecf20Sopenharmony_ci		}
16628c2ecf20Sopenharmony_ci		if (local->tx_rate_control & WLAN_RATE_5M5) {
16638c2ecf20Sopenharmony_ci			*p++ = local->basic_rates & WLAN_RATE_5M5 ?
16648c2ecf20Sopenharmony_ci				0x8b : 0x0b;
16658c2ecf20Sopenharmony_ci			(*lpos)++;
16668c2ecf20Sopenharmony_ci		}
16678c2ecf20Sopenharmony_ci		if (local->tx_rate_control & WLAN_RATE_11M) {
16688c2ecf20Sopenharmony_ci			*p++ = local->basic_rates & WLAN_RATE_11M ?
16698c2ecf20Sopenharmony_ci				0x96 : 0x16;
16708c2ecf20Sopenharmony_ci			(*lpos)++;
16718c2ecf20Sopenharmony_ci		}
16728c2ecf20Sopenharmony_ci		pos = (__le16 *) p;
16738c2ecf20Sopenharmony_ci	}
16748c2ecf20Sopenharmony_ci
16758c2ecf20Sopenharmony_ci	prism2_send_mgmt(dev, IEEE80211_FTYPE_MGMT |
16768c2ecf20Sopenharmony_ci			 (send_deauth ? IEEE80211_STYPE_DEAUTH :
16778c2ecf20Sopenharmony_ci			  (reassoc ? IEEE80211_STYPE_REASSOC_RESP :
16788c2ecf20Sopenharmony_ci			   IEEE80211_STYPE_ASSOC_RESP)),
16798c2ecf20Sopenharmony_ci			 body, (u8 *) pos - (u8 *) body,
16808c2ecf20Sopenharmony_ci			 hdr->addr2,
16818c2ecf20Sopenharmony_ci			 send_deauth ? 0 : local->ap->tx_callback_assoc);
16828c2ecf20Sopenharmony_ci
16838c2ecf20Sopenharmony_ci	if (sta) {
16848c2ecf20Sopenharmony_ci		if (resp == WLAN_STATUS_SUCCESS) {
16858c2ecf20Sopenharmony_ci			sta->last_rx = jiffies;
16868c2ecf20Sopenharmony_ci			/* STA will be marked associated from TX callback, if
16878c2ecf20Sopenharmony_ci			 * AssocResp is ACKed */
16888c2ecf20Sopenharmony_ci		}
16898c2ecf20Sopenharmony_ci		atomic_dec(&sta->users);
16908c2ecf20Sopenharmony_ci	}
16918c2ecf20Sopenharmony_ci
16928c2ecf20Sopenharmony_ci#if 0
16938c2ecf20Sopenharmony_ci	PDEBUG(DEBUG_AP, "%s: %pM %sassoc (len=%d "
16948c2ecf20Sopenharmony_ci	       "prev_ap=%pM) => %d(%d) (%s)\n",
16958c2ecf20Sopenharmony_ci	       dev->name,
16968c2ecf20Sopenharmony_ci	       hdr->addr2,
16978c2ecf20Sopenharmony_ci	       reassoc ? "re" : "", len,
16988c2ecf20Sopenharmony_ci	       prev_ap,
16998c2ecf20Sopenharmony_ci	       resp, send_deauth, txt);
17008c2ecf20Sopenharmony_ci#endif
17018c2ecf20Sopenharmony_ci}
17028c2ecf20Sopenharmony_ci
17038c2ecf20Sopenharmony_ci
17048c2ecf20Sopenharmony_ci/* Called only as a scheduled task for pending AP frames. */
17058c2ecf20Sopenharmony_cistatic void handle_deauth(local_info_t *local, struct sk_buff *skb,
17068c2ecf20Sopenharmony_ci			  struct hostap_80211_rx_status *rx_stats)
17078c2ecf20Sopenharmony_ci{
17088c2ecf20Sopenharmony_ci	struct net_device *dev = local->dev;
17098c2ecf20Sopenharmony_ci	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
17108c2ecf20Sopenharmony_ci	char *body = (char *) (skb->data + IEEE80211_MGMT_HDR_LEN);
17118c2ecf20Sopenharmony_ci	int len;
17128c2ecf20Sopenharmony_ci	u16 reason_code;
17138c2ecf20Sopenharmony_ci	__le16 *pos;
17148c2ecf20Sopenharmony_ci	struct sta_info *sta = NULL;
17158c2ecf20Sopenharmony_ci
17168c2ecf20Sopenharmony_ci	len = skb->len - IEEE80211_MGMT_HDR_LEN;
17178c2ecf20Sopenharmony_ci
17188c2ecf20Sopenharmony_ci	if (len < 2) {
17198c2ecf20Sopenharmony_ci		printk("handle_deauth - too short payload (len=%d)\n", len);
17208c2ecf20Sopenharmony_ci		return;
17218c2ecf20Sopenharmony_ci	}
17228c2ecf20Sopenharmony_ci
17238c2ecf20Sopenharmony_ci	pos = (__le16 *) body;
17248c2ecf20Sopenharmony_ci	reason_code = le16_to_cpu(*pos);
17258c2ecf20Sopenharmony_ci
17268c2ecf20Sopenharmony_ci	PDEBUG(DEBUG_AP, "%s: deauthentication: %pM len=%d, "
17278c2ecf20Sopenharmony_ci	       "reason_code=%d\n", dev->name, hdr->addr2,
17288c2ecf20Sopenharmony_ci	       len, reason_code);
17298c2ecf20Sopenharmony_ci
17308c2ecf20Sopenharmony_ci	spin_lock_bh(&local->ap->sta_table_lock);
17318c2ecf20Sopenharmony_ci	sta = ap_get_sta(local->ap, hdr->addr2);
17328c2ecf20Sopenharmony_ci	if (sta != NULL) {
17338c2ecf20Sopenharmony_ci		if ((sta->flags & WLAN_STA_ASSOC) && !sta->ap)
17348c2ecf20Sopenharmony_ci			hostap_event_expired_sta(local->dev, sta);
17358c2ecf20Sopenharmony_ci		sta->flags &= ~(WLAN_STA_AUTH | WLAN_STA_ASSOC);
17368c2ecf20Sopenharmony_ci	}
17378c2ecf20Sopenharmony_ci	spin_unlock_bh(&local->ap->sta_table_lock);
17388c2ecf20Sopenharmony_ci	if (sta == NULL) {
17398c2ecf20Sopenharmony_ci		printk("%s: deauthentication from %pM, "
17408c2ecf20Sopenharmony_ci	       "reason_code=%d, but STA not authenticated\n", dev->name,
17418c2ecf20Sopenharmony_ci		       hdr->addr2, reason_code);
17428c2ecf20Sopenharmony_ci	}
17438c2ecf20Sopenharmony_ci}
17448c2ecf20Sopenharmony_ci
17458c2ecf20Sopenharmony_ci
17468c2ecf20Sopenharmony_ci/* Called only as a scheduled task for pending AP frames. */
17478c2ecf20Sopenharmony_cistatic void handle_disassoc(local_info_t *local, struct sk_buff *skb,
17488c2ecf20Sopenharmony_ci			    struct hostap_80211_rx_status *rx_stats)
17498c2ecf20Sopenharmony_ci{
17508c2ecf20Sopenharmony_ci	struct net_device *dev = local->dev;
17518c2ecf20Sopenharmony_ci	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
17528c2ecf20Sopenharmony_ci	char *body = skb->data + IEEE80211_MGMT_HDR_LEN;
17538c2ecf20Sopenharmony_ci	int len;
17548c2ecf20Sopenharmony_ci	u16 reason_code;
17558c2ecf20Sopenharmony_ci	__le16 *pos;
17568c2ecf20Sopenharmony_ci	struct sta_info *sta = NULL;
17578c2ecf20Sopenharmony_ci
17588c2ecf20Sopenharmony_ci	len = skb->len - IEEE80211_MGMT_HDR_LEN;
17598c2ecf20Sopenharmony_ci
17608c2ecf20Sopenharmony_ci	if (len < 2) {
17618c2ecf20Sopenharmony_ci		printk("handle_disassoc - too short payload (len=%d)\n", len);
17628c2ecf20Sopenharmony_ci		return;
17638c2ecf20Sopenharmony_ci	}
17648c2ecf20Sopenharmony_ci
17658c2ecf20Sopenharmony_ci	pos = (__le16 *) body;
17668c2ecf20Sopenharmony_ci	reason_code = le16_to_cpu(*pos);
17678c2ecf20Sopenharmony_ci
17688c2ecf20Sopenharmony_ci	PDEBUG(DEBUG_AP, "%s: disassociation: %pM len=%d, "
17698c2ecf20Sopenharmony_ci	       "reason_code=%d\n", dev->name, hdr->addr2,
17708c2ecf20Sopenharmony_ci	       len, reason_code);
17718c2ecf20Sopenharmony_ci
17728c2ecf20Sopenharmony_ci	spin_lock_bh(&local->ap->sta_table_lock);
17738c2ecf20Sopenharmony_ci	sta = ap_get_sta(local->ap, hdr->addr2);
17748c2ecf20Sopenharmony_ci	if (sta != NULL) {
17758c2ecf20Sopenharmony_ci		if ((sta->flags & WLAN_STA_ASSOC) && !sta->ap)
17768c2ecf20Sopenharmony_ci			hostap_event_expired_sta(local->dev, sta);
17778c2ecf20Sopenharmony_ci		sta->flags &= ~WLAN_STA_ASSOC;
17788c2ecf20Sopenharmony_ci	}
17798c2ecf20Sopenharmony_ci	spin_unlock_bh(&local->ap->sta_table_lock);
17808c2ecf20Sopenharmony_ci	if (sta == NULL) {
17818c2ecf20Sopenharmony_ci		printk("%s: disassociation from %pM, "
17828c2ecf20Sopenharmony_ci		       "reason_code=%d, but STA not authenticated\n",
17838c2ecf20Sopenharmony_ci		       dev->name, hdr->addr2, reason_code);
17848c2ecf20Sopenharmony_ci	}
17858c2ecf20Sopenharmony_ci}
17868c2ecf20Sopenharmony_ci
17878c2ecf20Sopenharmony_ci
17888c2ecf20Sopenharmony_ci/* Called only as a scheduled task for pending AP frames. */
17898c2ecf20Sopenharmony_cistatic void ap_handle_data_nullfunc(local_info_t *local,
17908c2ecf20Sopenharmony_ci				    struct ieee80211_hdr *hdr)
17918c2ecf20Sopenharmony_ci{
17928c2ecf20Sopenharmony_ci	struct net_device *dev = local->dev;
17938c2ecf20Sopenharmony_ci
17948c2ecf20Sopenharmony_ci	/* some STA f/w's seem to require control::ACK frame for
17958c2ecf20Sopenharmony_ci	 * data::nullfunc, but at least Prism2 station f/w version 0.8.0 does
17968c2ecf20Sopenharmony_ci	 * not send this..
17978c2ecf20Sopenharmony_ci	 * send control::ACK for the data::nullfunc */
17988c2ecf20Sopenharmony_ci
17998c2ecf20Sopenharmony_ci	printk(KERN_DEBUG "Sending control::ACK for data::nullfunc\n");
18008c2ecf20Sopenharmony_ci	prism2_send_mgmt(dev, IEEE80211_FTYPE_CTL | IEEE80211_STYPE_ACK,
18018c2ecf20Sopenharmony_ci			 NULL, 0, hdr->addr2, 0);
18028c2ecf20Sopenharmony_ci}
18038c2ecf20Sopenharmony_ci
18048c2ecf20Sopenharmony_ci
18058c2ecf20Sopenharmony_ci/* Called only as a scheduled task for pending AP frames. */
18068c2ecf20Sopenharmony_cistatic void ap_handle_dropped_data(local_info_t *local,
18078c2ecf20Sopenharmony_ci				   struct ieee80211_hdr *hdr)
18088c2ecf20Sopenharmony_ci{
18098c2ecf20Sopenharmony_ci	struct net_device *dev = local->dev;
18108c2ecf20Sopenharmony_ci	struct sta_info *sta;
18118c2ecf20Sopenharmony_ci	__le16 reason;
18128c2ecf20Sopenharmony_ci
18138c2ecf20Sopenharmony_ci	spin_lock_bh(&local->ap->sta_table_lock);
18148c2ecf20Sopenharmony_ci	sta = ap_get_sta(local->ap, hdr->addr2);
18158c2ecf20Sopenharmony_ci	if (sta)
18168c2ecf20Sopenharmony_ci		atomic_inc(&sta->users);
18178c2ecf20Sopenharmony_ci	spin_unlock_bh(&local->ap->sta_table_lock);
18188c2ecf20Sopenharmony_ci
18198c2ecf20Sopenharmony_ci	if (sta != NULL && (sta->flags & WLAN_STA_ASSOC)) {
18208c2ecf20Sopenharmony_ci		PDEBUG(DEBUG_AP, "ap_handle_dropped_data: STA is now okay?\n");
18218c2ecf20Sopenharmony_ci		atomic_dec(&sta->users);
18228c2ecf20Sopenharmony_ci		return;
18238c2ecf20Sopenharmony_ci	}
18248c2ecf20Sopenharmony_ci
18258c2ecf20Sopenharmony_ci	reason = cpu_to_le16(WLAN_REASON_CLASS3_FRAME_FROM_NONASSOC_STA);
18268c2ecf20Sopenharmony_ci	prism2_send_mgmt(dev, IEEE80211_FTYPE_MGMT |
18278c2ecf20Sopenharmony_ci			 ((sta == NULL || !(sta->flags & WLAN_STA_ASSOC)) ?
18288c2ecf20Sopenharmony_ci			  IEEE80211_STYPE_DEAUTH : IEEE80211_STYPE_DISASSOC),
18298c2ecf20Sopenharmony_ci			 (char *) &reason, sizeof(reason), hdr->addr2, 0);
18308c2ecf20Sopenharmony_ci
18318c2ecf20Sopenharmony_ci	if (sta)
18328c2ecf20Sopenharmony_ci		atomic_dec(&sta->users);
18338c2ecf20Sopenharmony_ci}
18348c2ecf20Sopenharmony_ci
18358c2ecf20Sopenharmony_ci#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */
18368c2ecf20Sopenharmony_ci
18378c2ecf20Sopenharmony_ci
18388c2ecf20Sopenharmony_ci/* Called only as a scheduled task for pending AP frames. */
18398c2ecf20Sopenharmony_cistatic void pspoll_send_buffered(local_info_t *local, struct sta_info *sta,
18408c2ecf20Sopenharmony_ci				 struct sk_buff *skb)
18418c2ecf20Sopenharmony_ci{
18428c2ecf20Sopenharmony_ci	struct hostap_skb_tx_data *meta;
18438c2ecf20Sopenharmony_ci
18448c2ecf20Sopenharmony_ci	if (!(sta->flags & WLAN_STA_PS)) {
18458c2ecf20Sopenharmony_ci		/* Station has moved to non-PS mode, so send all buffered
18468c2ecf20Sopenharmony_ci		 * frames using normal device queue. */
18478c2ecf20Sopenharmony_ci		dev_queue_xmit(skb);
18488c2ecf20Sopenharmony_ci		return;
18498c2ecf20Sopenharmony_ci	}
18508c2ecf20Sopenharmony_ci
18518c2ecf20Sopenharmony_ci	/* add a flag for hostap_handle_sta_tx() to know that this skb should
18528c2ecf20Sopenharmony_ci	 * be passed through even though STA is using PS */
18538c2ecf20Sopenharmony_ci	meta = (struct hostap_skb_tx_data *) skb->cb;
18548c2ecf20Sopenharmony_ci	meta->flags |= HOSTAP_TX_FLAGS_BUFFERED_FRAME;
18558c2ecf20Sopenharmony_ci	if (!skb_queue_empty(&sta->tx_buf)) {
18568c2ecf20Sopenharmony_ci		/* indicate to STA that more frames follow */
18578c2ecf20Sopenharmony_ci		meta->flags |= HOSTAP_TX_FLAGS_ADD_MOREDATA;
18588c2ecf20Sopenharmony_ci	}
18598c2ecf20Sopenharmony_ci	dev_queue_xmit(skb);
18608c2ecf20Sopenharmony_ci}
18618c2ecf20Sopenharmony_ci
18628c2ecf20Sopenharmony_ci
18638c2ecf20Sopenharmony_ci/* Called only as a scheduled task for pending AP frames. */
18648c2ecf20Sopenharmony_cistatic void handle_pspoll(local_info_t *local,
18658c2ecf20Sopenharmony_ci			  struct ieee80211_hdr *hdr,
18668c2ecf20Sopenharmony_ci			  struct hostap_80211_rx_status *rx_stats)
18678c2ecf20Sopenharmony_ci{
18688c2ecf20Sopenharmony_ci	struct net_device *dev = local->dev;
18698c2ecf20Sopenharmony_ci	struct sta_info *sta;
18708c2ecf20Sopenharmony_ci	u16 aid;
18718c2ecf20Sopenharmony_ci	struct sk_buff *skb;
18728c2ecf20Sopenharmony_ci
18738c2ecf20Sopenharmony_ci	PDEBUG(DEBUG_PS2, "handle_pspoll: BSSID=%pM, TA=%pM PWRMGT=%d\n",
18748c2ecf20Sopenharmony_ci	       hdr->addr1, hdr->addr2, !!ieee80211_has_pm(hdr->frame_control));
18758c2ecf20Sopenharmony_ci
18768c2ecf20Sopenharmony_ci	if (!ether_addr_equal(hdr->addr1, dev->dev_addr)) {
18778c2ecf20Sopenharmony_ci		PDEBUG(DEBUG_AP,
18788c2ecf20Sopenharmony_ci		       "handle_pspoll - addr1(BSSID)=%pM not own MAC\n",
18798c2ecf20Sopenharmony_ci		       hdr->addr1);
18808c2ecf20Sopenharmony_ci		return;
18818c2ecf20Sopenharmony_ci	}
18828c2ecf20Sopenharmony_ci
18838c2ecf20Sopenharmony_ci	aid = le16_to_cpu(hdr->duration_id);
18848c2ecf20Sopenharmony_ci	if ((aid & (BIT(15) | BIT(14))) != (BIT(15) | BIT(14))) {
18858c2ecf20Sopenharmony_ci		PDEBUG(DEBUG_PS, "   PSPOLL and AID[15:14] not set\n");
18868c2ecf20Sopenharmony_ci		return;
18878c2ecf20Sopenharmony_ci	}
18888c2ecf20Sopenharmony_ci	aid &= ~(BIT(15) | BIT(14));
18898c2ecf20Sopenharmony_ci	if (aid == 0 || aid > MAX_AID_TABLE_SIZE) {
18908c2ecf20Sopenharmony_ci		PDEBUG(DEBUG_PS, "   invalid aid=%d\n", aid);
18918c2ecf20Sopenharmony_ci		return;
18928c2ecf20Sopenharmony_ci	}
18938c2ecf20Sopenharmony_ci	PDEBUG(DEBUG_PS2, "   aid=%d\n", aid);
18948c2ecf20Sopenharmony_ci
18958c2ecf20Sopenharmony_ci	spin_lock_bh(&local->ap->sta_table_lock);
18968c2ecf20Sopenharmony_ci	sta = ap_get_sta(local->ap, hdr->addr2);
18978c2ecf20Sopenharmony_ci	if (sta)
18988c2ecf20Sopenharmony_ci		atomic_inc(&sta->users);
18998c2ecf20Sopenharmony_ci	spin_unlock_bh(&local->ap->sta_table_lock);
19008c2ecf20Sopenharmony_ci
19018c2ecf20Sopenharmony_ci	if (sta == NULL) {
19028c2ecf20Sopenharmony_ci		PDEBUG(DEBUG_PS, "   STA not found\n");
19038c2ecf20Sopenharmony_ci		return;
19048c2ecf20Sopenharmony_ci	}
19058c2ecf20Sopenharmony_ci	if (sta->aid != aid) {
19068c2ecf20Sopenharmony_ci		PDEBUG(DEBUG_PS, "   received aid=%i does not match with "
19078c2ecf20Sopenharmony_ci		       "assoc.aid=%d\n", aid, sta->aid);
19088c2ecf20Sopenharmony_ci		return;
19098c2ecf20Sopenharmony_ci	}
19108c2ecf20Sopenharmony_ci
19118c2ecf20Sopenharmony_ci	/* FIX: todo:
19128c2ecf20Sopenharmony_ci	 * - add timeout for buffering (clear aid in TIM vector if buffer timed
19138c2ecf20Sopenharmony_ci	 *   out (expiry time must be longer than ListenInterval for
19148c2ecf20Sopenharmony_ci	 *   the corresponding STA; "8802-11: 11.2.1.9 AP aging function"
19158c2ecf20Sopenharmony_ci	 * - what to do, if buffered, pspolled, and sent frame is not ACKed by
19168c2ecf20Sopenharmony_ci	 *   sta; store buffer for later use and leave TIM aid bit set? use
19178c2ecf20Sopenharmony_ci	 *   TX event to check whether frame was ACKed?
19188c2ecf20Sopenharmony_ci	 */
19198c2ecf20Sopenharmony_ci
19208c2ecf20Sopenharmony_ci	while ((skb = skb_dequeue(&sta->tx_buf)) != NULL) {
19218c2ecf20Sopenharmony_ci		/* send buffered frame .. */
19228c2ecf20Sopenharmony_ci		PDEBUG(DEBUG_PS2, "Sending buffered frame to STA after PS POLL"
19238c2ecf20Sopenharmony_ci		       " (buffer_count=%d)\n", skb_queue_len(&sta->tx_buf));
19248c2ecf20Sopenharmony_ci
19258c2ecf20Sopenharmony_ci		pspoll_send_buffered(local, sta, skb);
19268c2ecf20Sopenharmony_ci
19278c2ecf20Sopenharmony_ci		if (sta->flags & WLAN_STA_PS) {
19288c2ecf20Sopenharmony_ci			/* send only one buffered packet per PS Poll */
19298c2ecf20Sopenharmony_ci			/* FIX: should ignore further PS Polls until the
19308c2ecf20Sopenharmony_ci			 * buffered packet that was just sent is acknowledged
19318c2ecf20Sopenharmony_ci			 * (Tx or TxExc event) */
19328c2ecf20Sopenharmony_ci			break;
19338c2ecf20Sopenharmony_ci		}
19348c2ecf20Sopenharmony_ci	}
19358c2ecf20Sopenharmony_ci
19368c2ecf20Sopenharmony_ci	if (skb_queue_empty(&sta->tx_buf)) {
19378c2ecf20Sopenharmony_ci		/* try to clear aid from TIM */
19388c2ecf20Sopenharmony_ci		if (!(sta->flags & WLAN_STA_TIM))
19398c2ecf20Sopenharmony_ci			PDEBUG(DEBUG_PS2,  "Re-unsetting TIM for aid %d\n",
19408c2ecf20Sopenharmony_ci			       aid);
19418c2ecf20Sopenharmony_ci		hostap_set_tim(local, aid, 0);
19428c2ecf20Sopenharmony_ci		sta->flags &= ~WLAN_STA_TIM;
19438c2ecf20Sopenharmony_ci	}
19448c2ecf20Sopenharmony_ci
19458c2ecf20Sopenharmony_ci	atomic_dec(&sta->users);
19468c2ecf20Sopenharmony_ci}
19478c2ecf20Sopenharmony_ci
19488c2ecf20Sopenharmony_ci
19498c2ecf20Sopenharmony_ci#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT
19508c2ecf20Sopenharmony_ci
19518c2ecf20Sopenharmony_cistatic void handle_wds_oper_queue(struct work_struct *work)
19528c2ecf20Sopenharmony_ci{
19538c2ecf20Sopenharmony_ci	struct ap_data *ap = container_of(work, struct ap_data,
19548c2ecf20Sopenharmony_ci					  wds_oper_queue);
19558c2ecf20Sopenharmony_ci	local_info_t *local = ap->local;
19568c2ecf20Sopenharmony_ci	struct wds_oper_data *entry, *prev;
19578c2ecf20Sopenharmony_ci
19588c2ecf20Sopenharmony_ci	spin_lock_bh(&local->lock);
19598c2ecf20Sopenharmony_ci	entry = local->ap->wds_oper_entries;
19608c2ecf20Sopenharmony_ci	local->ap->wds_oper_entries = NULL;
19618c2ecf20Sopenharmony_ci	spin_unlock_bh(&local->lock);
19628c2ecf20Sopenharmony_ci
19638c2ecf20Sopenharmony_ci	while (entry) {
19648c2ecf20Sopenharmony_ci		PDEBUG(DEBUG_AP, "%s: %s automatic WDS connection "
19658c2ecf20Sopenharmony_ci		       "to AP %pM\n",
19668c2ecf20Sopenharmony_ci		       local->dev->name,
19678c2ecf20Sopenharmony_ci		       entry->type == WDS_ADD ? "adding" : "removing",
19688c2ecf20Sopenharmony_ci		       entry->addr);
19698c2ecf20Sopenharmony_ci		if (entry->type == WDS_ADD)
19708c2ecf20Sopenharmony_ci			prism2_wds_add(local, entry->addr, 0);
19718c2ecf20Sopenharmony_ci		else if (entry->type == WDS_DEL)
19728c2ecf20Sopenharmony_ci			prism2_wds_del(local, entry->addr, 0, 1);
19738c2ecf20Sopenharmony_ci
19748c2ecf20Sopenharmony_ci		prev = entry;
19758c2ecf20Sopenharmony_ci		entry = entry->next;
19768c2ecf20Sopenharmony_ci		kfree(prev);
19778c2ecf20Sopenharmony_ci	}
19788c2ecf20Sopenharmony_ci}
19798c2ecf20Sopenharmony_ci
19808c2ecf20Sopenharmony_ci
19818c2ecf20Sopenharmony_ci/* Called only as a scheduled task for pending AP frames. */
19828c2ecf20Sopenharmony_cistatic void handle_beacon(local_info_t *local, struct sk_buff *skb,
19838c2ecf20Sopenharmony_ci			  struct hostap_80211_rx_status *rx_stats)
19848c2ecf20Sopenharmony_ci{
19858c2ecf20Sopenharmony_ci	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
19868c2ecf20Sopenharmony_ci	char *body = skb->data + IEEE80211_MGMT_HDR_LEN;
19878c2ecf20Sopenharmony_ci	int len, left;
19888c2ecf20Sopenharmony_ci	u16 beacon_int, capability;
19898c2ecf20Sopenharmony_ci	__le16 *pos;
19908c2ecf20Sopenharmony_ci	char *ssid = NULL;
19918c2ecf20Sopenharmony_ci	unsigned char *supp_rates = NULL;
19928c2ecf20Sopenharmony_ci	int ssid_len = 0, supp_rates_len = 0;
19938c2ecf20Sopenharmony_ci	struct sta_info *sta = NULL;
19948c2ecf20Sopenharmony_ci	int new_sta = 0, channel = -1;
19958c2ecf20Sopenharmony_ci
19968c2ecf20Sopenharmony_ci	len = skb->len - IEEE80211_MGMT_HDR_LEN;
19978c2ecf20Sopenharmony_ci
19988c2ecf20Sopenharmony_ci	if (len < 8 + 2 + 2) {
19998c2ecf20Sopenharmony_ci		printk(KERN_DEBUG "handle_beacon - too short payload "
20008c2ecf20Sopenharmony_ci		       "(len=%d)\n", len);
20018c2ecf20Sopenharmony_ci		return;
20028c2ecf20Sopenharmony_ci	}
20038c2ecf20Sopenharmony_ci
20048c2ecf20Sopenharmony_ci	pos = (__le16 *) body;
20058c2ecf20Sopenharmony_ci	left = len;
20068c2ecf20Sopenharmony_ci
20078c2ecf20Sopenharmony_ci	/* Timestamp (8 octets) */
20088c2ecf20Sopenharmony_ci	pos += 4; left -= 8;
20098c2ecf20Sopenharmony_ci	/* Beacon interval (2 octets) */
20108c2ecf20Sopenharmony_ci	beacon_int = le16_to_cpu(*pos);
20118c2ecf20Sopenharmony_ci	pos++; left -= 2;
20128c2ecf20Sopenharmony_ci	/* Capability information (2 octets) */
20138c2ecf20Sopenharmony_ci	capability = le16_to_cpu(*pos);
20148c2ecf20Sopenharmony_ci	pos++; left -= 2;
20158c2ecf20Sopenharmony_ci
20168c2ecf20Sopenharmony_ci	if (local->ap->ap_policy != AP_OTHER_AP_EVEN_IBSS &&
20178c2ecf20Sopenharmony_ci	    capability & WLAN_CAPABILITY_IBSS)
20188c2ecf20Sopenharmony_ci		return;
20198c2ecf20Sopenharmony_ci
20208c2ecf20Sopenharmony_ci	if (left >= 2) {
20218c2ecf20Sopenharmony_ci		unsigned int ileft;
20228c2ecf20Sopenharmony_ci		unsigned char *u = (unsigned char *) pos;
20238c2ecf20Sopenharmony_ci
20248c2ecf20Sopenharmony_ci		if (*u == WLAN_EID_SSID) {
20258c2ecf20Sopenharmony_ci			u++; left--;
20268c2ecf20Sopenharmony_ci			ileft = *u;
20278c2ecf20Sopenharmony_ci			u++; left--;
20288c2ecf20Sopenharmony_ci
20298c2ecf20Sopenharmony_ci			if (ileft > left || ileft > MAX_SSID_LEN) {
20308c2ecf20Sopenharmony_ci				PDEBUG(DEBUG_AP, "SSID: overflow\n");
20318c2ecf20Sopenharmony_ci				return;
20328c2ecf20Sopenharmony_ci			}
20338c2ecf20Sopenharmony_ci
20348c2ecf20Sopenharmony_ci			if (local->ap->ap_policy == AP_OTHER_AP_SAME_SSID &&
20358c2ecf20Sopenharmony_ci			    (ileft != strlen(local->essid) ||
20368c2ecf20Sopenharmony_ci			     memcmp(local->essid, u, ileft) != 0)) {
20378c2ecf20Sopenharmony_ci				/* not our SSID */
20388c2ecf20Sopenharmony_ci				return;
20398c2ecf20Sopenharmony_ci			}
20408c2ecf20Sopenharmony_ci
20418c2ecf20Sopenharmony_ci			ssid = u;
20428c2ecf20Sopenharmony_ci			ssid_len = ileft;
20438c2ecf20Sopenharmony_ci
20448c2ecf20Sopenharmony_ci			u += ileft;
20458c2ecf20Sopenharmony_ci			left -= ileft;
20468c2ecf20Sopenharmony_ci		}
20478c2ecf20Sopenharmony_ci
20488c2ecf20Sopenharmony_ci		if (*u == WLAN_EID_SUPP_RATES) {
20498c2ecf20Sopenharmony_ci			u++; left--;
20508c2ecf20Sopenharmony_ci			ileft = *u;
20518c2ecf20Sopenharmony_ci			u++; left--;
20528c2ecf20Sopenharmony_ci
20538c2ecf20Sopenharmony_ci			if (ileft > left || ileft == 0 || ileft > 8) {
20548c2ecf20Sopenharmony_ci				PDEBUG(DEBUG_AP, " - SUPP_RATES len error\n");
20558c2ecf20Sopenharmony_ci				return;
20568c2ecf20Sopenharmony_ci			}
20578c2ecf20Sopenharmony_ci
20588c2ecf20Sopenharmony_ci			supp_rates = u;
20598c2ecf20Sopenharmony_ci			supp_rates_len = ileft;
20608c2ecf20Sopenharmony_ci
20618c2ecf20Sopenharmony_ci			u += ileft;
20628c2ecf20Sopenharmony_ci			left -= ileft;
20638c2ecf20Sopenharmony_ci		}
20648c2ecf20Sopenharmony_ci
20658c2ecf20Sopenharmony_ci		if (*u == WLAN_EID_DS_PARAMS) {
20668c2ecf20Sopenharmony_ci			u++; left--;
20678c2ecf20Sopenharmony_ci			ileft = *u;
20688c2ecf20Sopenharmony_ci			u++; left--;
20698c2ecf20Sopenharmony_ci
20708c2ecf20Sopenharmony_ci			if (ileft > left || ileft != 1) {
20718c2ecf20Sopenharmony_ci				PDEBUG(DEBUG_AP, " - DS_PARAMS len error\n");
20728c2ecf20Sopenharmony_ci				return;
20738c2ecf20Sopenharmony_ci			}
20748c2ecf20Sopenharmony_ci
20758c2ecf20Sopenharmony_ci			channel = *u;
20768c2ecf20Sopenharmony_ci
20778c2ecf20Sopenharmony_ci			u += ileft;
20788c2ecf20Sopenharmony_ci			left -= ileft;
20798c2ecf20Sopenharmony_ci		}
20808c2ecf20Sopenharmony_ci	}
20818c2ecf20Sopenharmony_ci
20828c2ecf20Sopenharmony_ci	spin_lock_bh(&local->ap->sta_table_lock);
20838c2ecf20Sopenharmony_ci	sta = ap_get_sta(local->ap, hdr->addr2);
20848c2ecf20Sopenharmony_ci	if (sta != NULL)
20858c2ecf20Sopenharmony_ci		atomic_inc(&sta->users);
20868c2ecf20Sopenharmony_ci	spin_unlock_bh(&local->ap->sta_table_lock);
20878c2ecf20Sopenharmony_ci
20888c2ecf20Sopenharmony_ci	if (sta == NULL) {
20898c2ecf20Sopenharmony_ci		/* add new AP */
20908c2ecf20Sopenharmony_ci		new_sta = 1;
20918c2ecf20Sopenharmony_ci		sta = ap_add_sta(local->ap, hdr->addr2);
20928c2ecf20Sopenharmony_ci		if (sta == NULL) {
20938c2ecf20Sopenharmony_ci			printk(KERN_INFO "prism2: kmalloc failed for AP "
20948c2ecf20Sopenharmony_ci			       "data structure\n");
20958c2ecf20Sopenharmony_ci			return;
20968c2ecf20Sopenharmony_ci		}
20978c2ecf20Sopenharmony_ci		hostap_event_new_sta(local->dev, sta);
20988c2ecf20Sopenharmony_ci
20998c2ecf20Sopenharmony_ci		/* mark APs authentication and associated for pseudo ad-hoc
21008c2ecf20Sopenharmony_ci		 * style communication */
21018c2ecf20Sopenharmony_ci		sta->flags = WLAN_STA_AUTH | WLAN_STA_ASSOC;
21028c2ecf20Sopenharmony_ci
21038c2ecf20Sopenharmony_ci		if (local->ap->autom_ap_wds) {
21048c2ecf20Sopenharmony_ci			hostap_wds_link_oper(local, sta->addr, WDS_ADD);
21058c2ecf20Sopenharmony_ci		}
21068c2ecf20Sopenharmony_ci	}
21078c2ecf20Sopenharmony_ci
21088c2ecf20Sopenharmony_ci	sta->ap = 1;
21098c2ecf20Sopenharmony_ci	if (ssid) {
21108c2ecf20Sopenharmony_ci		sta->u.ap.ssid_len = ssid_len;
21118c2ecf20Sopenharmony_ci		memcpy(sta->u.ap.ssid, ssid, ssid_len);
21128c2ecf20Sopenharmony_ci		sta->u.ap.ssid[ssid_len] = '\0';
21138c2ecf20Sopenharmony_ci	} else {
21148c2ecf20Sopenharmony_ci		sta->u.ap.ssid_len = 0;
21158c2ecf20Sopenharmony_ci		sta->u.ap.ssid[0] = '\0';
21168c2ecf20Sopenharmony_ci	}
21178c2ecf20Sopenharmony_ci	sta->u.ap.channel = channel;
21188c2ecf20Sopenharmony_ci	sta->rx_packets++;
21198c2ecf20Sopenharmony_ci	sta->rx_bytes += len;
21208c2ecf20Sopenharmony_ci	sta->u.ap.last_beacon = sta->last_rx = jiffies;
21218c2ecf20Sopenharmony_ci	sta->capability = capability;
21228c2ecf20Sopenharmony_ci	sta->listen_interval = beacon_int;
21238c2ecf20Sopenharmony_ci
21248c2ecf20Sopenharmony_ci	atomic_dec(&sta->users);
21258c2ecf20Sopenharmony_ci
21268c2ecf20Sopenharmony_ci	if (new_sta) {
21278c2ecf20Sopenharmony_ci		memset(sta->supported_rates, 0, sizeof(sta->supported_rates));
21288c2ecf20Sopenharmony_ci		memcpy(sta->supported_rates, supp_rates, supp_rates_len);
21298c2ecf20Sopenharmony_ci		prism2_check_tx_rates(sta);
21308c2ecf20Sopenharmony_ci	}
21318c2ecf20Sopenharmony_ci}
21328c2ecf20Sopenharmony_ci
21338c2ecf20Sopenharmony_ci#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */
21348c2ecf20Sopenharmony_ci
21358c2ecf20Sopenharmony_ci
21368c2ecf20Sopenharmony_ci/* Called only as a tasklet. */
21378c2ecf20Sopenharmony_cistatic void handle_ap_item(local_info_t *local, struct sk_buff *skb,
21388c2ecf20Sopenharmony_ci			   struct hostap_80211_rx_status *rx_stats)
21398c2ecf20Sopenharmony_ci{
21408c2ecf20Sopenharmony_ci#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT
21418c2ecf20Sopenharmony_ci	struct net_device *dev = local->dev;
21428c2ecf20Sopenharmony_ci#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */
21438c2ecf20Sopenharmony_ci	u16 fc, type, stype;
21448c2ecf20Sopenharmony_ci	struct ieee80211_hdr *hdr;
21458c2ecf20Sopenharmony_ci
21468c2ecf20Sopenharmony_ci	/* FIX: should give skb->len to handler functions and check that the
21478c2ecf20Sopenharmony_ci	 * buffer is long enough */
21488c2ecf20Sopenharmony_ci	hdr = (struct ieee80211_hdr *) skb->data;
21498c2ecf20Sopenharmony_ci	fc = le16_to_cpu(hdr->frame_control);
21508c2ecf20Sopenharmony_ci	type = fc & IEEE80211_FCTL_FTYPE;
21518c2ecf20Sopenharmony_ci	stype = fc & IEEE80211_FCTL_STYPE;
21528c2ecf20Sopenharmony_ci
21538c2ecf20Sopenharmony_ci#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT
21548c2ecf20Sopenharmony_ci	if (!local->hostapd && type == IEEE80211_FTYPE_DATA) {
21558c2ecf20Sopenharmony_ci		PDEBUG(DEBUG_AP, "handle_ap_item - data frame\n");
21568c2ecf20Sopenharmony_ci
21578c2ecf20Sopenharmony_ci		if (!(fc & IEEE80211_FCTL_TODS) ||
21588c2ecf20Sopenharmony_ci		    (fc & IEEE80211_FCTL_FROMDS)) {
21598c2ecf20Sopenharmony_ci			if (stype == IEEE80211_STYPE_NULLFUNC) {
21608c2ecf20Sopenharmony_ci				/* no ToDS nullfunc seems to be used to check
21618c2ecf20Sopenharmony_ci				 * AP association; so send reject message to
21628c2ecf20Sopenharmony_ci				 * speed up re-association */
21638c2ecf20Sopenharmony_ci				ap_handle_dropped_data(local, hdr);
21648c2ecf20Sopenharmony_ci				goto done;
21658c2ecf20Sopenharmony_ci			}
21668c2ecf20Sopenharmony_ci			PDEBUG(DEBUG_AP, "   not ToDS frame (fc=0x%04x)\n",
21678c2ecf20Sopenharmony_ci			       fc);
21688c2ecf20Sopenharmony_ci			goto done;
21698c2ecf20Sopenharmony_ci		}
21708c2ecf20Sopenharmony_ci
21718c2ecf20Sopenharmony_ci		if (!ether_addr_equal(hdr->addr1, dev->dev_addr)) {
21728c2ecf20Sopenharmony_ci			PDEBUG(DEBUG_AP, "handle_ap_item - addr1(BSSID)=%pM"
21738c2ecf20Sopenharmony_ci			       " not own MAC\n", hdr->addr1);
21748c2ecf20Sopenharmony_ci			goto done;
21758c2ecf20Sopenharmony_ci		}
21768c2ecf20Sopenharmony_ci
21778c2ecf20Sopenharmony_ci		if (local->ap->nullfunc_ack &&
21788c2ecf20Sopenharmony_ci		    stype == IEEE80211_STYPE_NULLFUNC)
21798c2ecf20Sopenharmony_ci			ap_handle_data_nullfunc(local, hdr);
21808c2ecf20Sopenharmony_ci		else
21818c2ecf20Sopenharmony_ci			ap_handle_dropped_data(local, hdr);
21828c2ecf20Sopenharmony_ci		goto done;
21838c2ecf20Sopenharmony_ci	}
21848c2ecf20Sopenharmony_ci
21858c2ecf20Sopenharmony_ci	if (type == IEEE80211_FTYPE_MGMT && stype == IEEE80211_STYPE_BEACON) {
21868c2ecf20Sopenharmony_ci		handle_beacon(local, skb, rx_stats);
21878c2ecf20Sopenharmony_ci		goto done;
21888c2ecf20Sopenharmony_ci	}
21898c2ecf20Sopenharmony_ci#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */
21908c2ecf20Sopenharmony_ci
21918c2ecf20Sopenharmony_ci	if (type == IEEE80211_FTYPE_CTL && stype == IEEE80211_STYPE_PSPOLL) {
21928c2ecf20Sopenharmony_ci		handle_pspoll(local, hdr, rx_stats);
21938c2ecf20Sopenharmony_ci		goto done;
21948c2ecf20Sopenharmony_ci	}
21958c2ecf20Sopenharmony_ci
21968c2ecf20Sopenharmony_ci	if (local->hostapd) {
21978c2ecf20Sopenharmony_ci		PDEBUG(DEBUG_AP, "Unknown frame in AP queue: type=0x%02x "
21988c2ecf20Sopenharmony_ci		       "subtype=0x%02x\n", type, stype);
21998c2ecf20Sopenharmony_ci		goto done;
22008c2ecf20Sopenharmony_ci	}
22018c2ecf20Sopenharmony_ci
22028c2ecf20Sopenharmony_ci#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT
22038c2ecf20Sopenharmony_ci	if (type != IEEE80211_FTYPE_MGMT) {
22048c2ecf20Sopenharmony_ci		PDEBUG(DEBUG_AP, "handle_ap_item - not a management frame?\n");
22058c2ecf20Sopenharmony_ci		goto done;
22068c2ecf20Sopenharmony_ci	}
22078c2ecf20Sopenharmony_ci
22088c2ecf20Sopenharmony_ci	if (!ether_addr_equal(hdr->addr1, dev->dev_addr)) {
22098c2ecf20Sopenharmony_ci		PDEBUG(DEBUG_AP, "handle_ap_item - addr1(DA)=%pM"
22108c2ecf20Sopenharmony_ci		       " not own MAC\n", hdr->addr1);
22118c2ecf20Sopenharmony_ci		goto done;
22128c2ecf20Sopenharmony_ci	}
22138c2ecf20Sopenharmony_ci
22148c2ecf20Sopenharmony_ci	if (!ether_addr_equal(hdr->addr3, dev->dev_addr)) {
22158c2ecf20Sopenharmony_ci		PDEBUG(DEBUG_AP, "handle_ap_item - addr3(BSSID)=%pM"
22168c2ecf20Sopenharmony_ci		       " not own MAC\n", hdr->addr3);
22178c2ecf20Sopenharmony_ci		goto done;
22188c2ecf20Sopenharmony_ci	}
22198c2ecf20Sopenharmony_ci
22208c2ecf20Sopenharmony_ci	switch (stype) {
22218c2ecf20Sopenharmony_ci	case IEEE80211_STYPE_ASSOC_REQ:
22228c2ecf20Sopenharmony_ci		handle_assoc(local, skb, rx_stats, 0);
22238c2ecf20Sopenharmony_ci		break;
22248c2ecf20Sopenharmony_ci	case IEEE80211_STYPE_ASSOC_RESP:
22258c2ecf20Sopenharmony_ci		PDEBUG(DEBUG_AP, "==> ASSOC RESP (ignored)\n");
22268c2ecf20Sopenharmony_ci		break;
22278c2ecf20Sopenharmony_ci	case IEEE80211_STYPE_REASSOC_REQ:
22288c2ecf20Sopenharmony_ci		handle_assoc(local, skb, rx_stats, 1);
22298c2ecf20Sopenharmony_ci		break;
22308c2ecf20Sopenharmony_ci	case IEEE80211_STYPE_REASSOC_RESP:
22318c2ecf20Sopenharmony_ci		PDEBUG(DEBUG_AP, "==> REASSOC RESP (ignored)\n");
22328c2ecf20Sopenharmony_ci		break;
22338c2ecf20Sopenharmony_ci	case IEEE80211_STYPE_ATIM:
22348c2ecf20Sopenharmony_ci		PDEBUG(DEBUG_AP, "==> ATIM (ignored)\n");
22358c2ecf20Sopenharmony_ci		break;
22368c2ecf20Sopenharmony_ci	case IEEE80211_STYPE_DISASSOC:
22378c2ecf20Sopenharmony_ci		handle_disassoc(local, skb, rx_stats);
22388c2ecf20Sopenharmony_ci		break;
22398c2ecf20Sopenharmony_ci	case IEEE80211_STYPE_AUTH:
22408c2ecf20Sopenharmony_ci		handle_authen(local, skb, rx_stats);
22418c2ecf20Sopenharmony_ci		break;
22428c2ecf20Sopenharmony_ci	case IEEE80211_STYPE_DEAUTH:
22438c2ecf20Sopenharmony_ci		handle_deauth(local, skb, rx_stats);
22448c2ecf20Sopenharmony_ci		break;
22458c2ecf20Sopenharmony_ci	default:
22468c2ecf20Sopenharmony_ci		PDEBUG(DEBUG_AP, "Unknown mgmt frame subtype 0x%02x\n",
22478c2ecf20Sopenharmony_ci		       stype >> 4);
22488c2ecf20Sopenharmony_ci		break;
22498c2ecf20Sopenharmony_ci	}
22508c2ecf20Sopenharmony_ci#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */
22518c2ecf20Sopenharmony_ci
22528c2ecf20Sopenharmony_ci done:
22538c2ecf20Sopenharmony_ci	dev_kfree_skb(skb);
22548c2ecf20Sopenharmony_ci}
22558c2ecf20Sopenharmony_ci
22568c2ecf20Sopenharmony_ci
22578c2ecf20Sopenharmony_ci/* Called only as a tasklet (software IRQ) */
22588c2ecf20Sopenharmony_civoid hostap_rx(struct net_device *dev, struct sk_buff *skb,
22598c2ecf20Sopenharmony_ci	       struct hostap_80211_rx_status *rx_stats)
22608c2ecf20Sopenharmony_ci{
22618c2ecf20Sopenharmony_ci	struct hostap_interface *iface;
22628c2ecf20Sopenharmony_ci	local_info_t *local;
22638c2ecf20Sopenharmony_ci	struct ieee80211_hdr *hdr;
22648c2ecf20Sopenharmony_ci
22658c2ecf20Sopenharmony_ci	iface = netdev_priv(dev);
22668c2ecf20Sopenharmony_ci	local = iface->local;
22678c2ecf20Sopenharmony_ci
22688c2ecf20Sopenharmony_ci	if (skb->len < 16)
22698c2ecf20Sopenharmony_ci		goto drop;
22708c2ecf20Sopenharmony_ci
22718c2ecf20Sopenharmony_ci	dev->stats.rx_packets++;
22728c2ecf20Sopenharmony_ci
22738c2ecf20Sopenharmony_ci	hdr = (struct ieee80211_hdr *) skb->data;
22748c2ecf20Sopenharmony_ci
22758c2ecf20Sopenharmony_ci	if (local->ap->ap_policy == AP_OTHER_AP_SKIP_ALL &&
22768c2ecf20Sopenharmony_ci	    ieee80211_is_beacon(hdr->frame_control))
22778c2ecf20Sopenharmony_ci		goto drop;
22788c2ecf20Sopenharmony_ci
22798c2ecf20Sopenharmony_ci	skb->protocol = cpu_to_be16(ETH_P_HOSTAP);
22808c2ecf20Sopenharmony_ci	handle_ap_item(local, skb, rx_stats);
22818c2ecf20Sopenharmony_ci	return;
22828c2ecf20Sopenharmony_ci
22838c2ecf20Sopenharmony_ci drop:
22848c2ecf20Sopenharmony_ci	dev_kfree_skb(skb);
22858c2ecf20Sopenharmony_ci}
22868c2ecf20Sopenharmony_ci
22878c2ecf20Sopenharmony_ci
22888c2ecf20Sopenharmony_ci/* Called only as a tasklet (software IRQ) */
22898c2ecf20Sopenharmony_cistatic void schedule_packet_send(local_info_t *local, struct sta_info *sta)
22908c2ecf20Sopenharmony_ci{
22918c2ecf20Sopenharmony_ci	struct sk_buff *skb;
22928c2ecf20Sopenharmony_ci	struct ieee80211_hdr *hdr;
22938c2ecf20Sopenharmony_ci	struct hostap_80211_rx_status rx_stats;
22948c2ecf20Sopenharmony_ci
22958c2ecf20Sopenharmony_ci	if (skb_queue_empty(&sta->tx_buf))
22968c2ecf20Sopenharmony_ci		return;
22978c2ecf20Sopenharmony_ci
22988c2ecf20Sopenharmony_ci	skb = dev_alloc_skb(16);
22998c2ecf20Sopenharmony_ci	if (skb == NULL) {
23008c2ecf20Sopenharmony_ci		printk(KERN_DEBUG "%s: schedule_packet_send: skb alloc "
23018c2ecf20Sopenharmony_ci		       "failed\n", local->dev->name);
23028c2ecf20Sopenharmony_ci		return;
23038c2ecf20Sopenharmony_ci	}
23048c2ecf20Sopenharmony_ci
23058c2ecf20Sopenharmony_ci	hdr = skb_put(skb, 16);
23068c2ecf20Sopenharmony_ci
23078c2ecf20Sopenharmony_ci	/* Generate a fake pspoll frame to start packet delivery */
23088c2ecf20Sopenharmony_ci	hdr->frame_control = cpu_to_le16(
23098c2ecf20Sopenharmony_ci		IEEE80211_FTYPE_CTL | IEEE80211_STYPE_PSPOLL);
23108c2ecf20Sopenharmony_ci	memcpy(hdr->addr1, local->dev->dev_addr, ETH_ALEN);
23118c2ecf20Sopenharmony_ci	memcpy(hdr->addr2, sta->addr, ETH_ALEN);
23128c2ecf20Sopenharmony_ci	hdr->duration_id = cpu_to_le16(sta->aid | BIT(15) | BIT(14));
23138c2ecf20Sopenharmony_ci
23148c2ecf20Sopenharmony_ci	PDEBUG(DEBUG_PS2,
23158c2ecf20Sopenharmony_ci	       "%s: Scheduling buffered packet delivery for STA %pM\n",
23168c2ecf20Sopenharmony_ci	       local->dev->name, sta->addr);
23178c2ecf20Sopenharmony_ci
23188c2ecf20Sopenharmony_ci	skb->dev = local->dev;
23198c2ecf20Sopenharmony_ci
23208c2ecf20Sopenharmony_ci	memset(&rx_stats, 0, sizeof(rx_stats));
23218c2ecf20Sopenharmony_ci	hostap_rx(local->dev, skb, &rx_stats);
23228c2ecf20Sopenharmony_ci}
23238c2ecf20Sopenharmony_ci
23248c2ecf20Sopenharmony_ci
23258c2ecf20Sopenharmony_ciint prism2_ap_get_sta_qual(local_info_t *local, struct sockaddr addr[],
23268c2ecf20Sopenharmony_ci			   struct iw_quality qual[], int buf_size,
23278c2ecf20Sopenharmony_ci			   int aplist)
23288c2ecf20Sopenharmony_ci{
23298c2ecf20Sopenharmony_ci	struct ap_data *ap = local->ap;
23308c2ecf20Sopenharmony_ci	struct list_head *ptr;
23318c2ecf20Sopenharmony_ci	int count = 0;
23328c2ecf20Sopenharmony_ci
23338c2ecf20Sopenharmony_ci	spin_lock_bh(&ap->sta_table_lock);
23348c2ecf20Sopenharmony_ci
23358c2ecf20Sopenharmony_ci	for (ptr = ap->sta_list.next; ptr != NULL && ptr != &ap->sta_list;
23368c2ecf20Sopenharmony_ci	     ptr = ptr->next) {
23378c2ecf20Sopenharmony_ci		struct sta_info *sta = (struct sta_info *) ptr;
23388c2ecf20Sopenharmony_ci
23398c2ecf20Sopenharmony_ci		if (aplist && !sta->ap)
23408c2ecf20Sopenharmony_ci			continue;
23418c2ecf20Sopenharmony_ci		addr[count].sa_family = ARPHRD_ETHER;
23428c2ecf20Sopenharmony_ci		memcpy(addr[count].sa_data, sta->addr, ETH_ALEN);
23438c2ecf20Sopenharmony_ci		if (sta->last_rx_silence == 0)
23448c2ecf20Sopenharmony_ci			qual[count].qual = sta->last_rx_signal < 27 ?
23458c2ecf20Sopenharmony_ci				0 : (sta->last_rx_signal - 27) * 92 / 127;
23468c2ecf20Sopenharmony_ci		else
23478c2ecf20Sopenharmony_ci			qual[count].qual = sta->last_rx_signal -
23488c2ecf20Sopenharmony_ci				sta->last_rx_silence - 35;
23498c2ecf20Sopenharmony_ci		qual[count].level = HFA384X_LEVEL_TO_dBm(sta->last_rx_signal);
23508c2ecf20Sopenharmony_ci		qual[count].noise = HFA384X_LEVEL_TO_dBm(sta->last_rx_silence);
23518c2ecf20Sopenharmony_ci		qual[count].updated = sta->last_rx_updated;
23528c2ecf20Sopenharmony_ci
23538c2ecf20Sopenharmony_ci		sta->last_rx_updated = IW_QUAL_DBM;
23548c2ecf20Sopenharmony_ci
23558c2ecf20Sopenharmony_ci		count++;
23568c2ecf20Sopenharmony_ci		if (count >= buf_size)
23578c2ecf20Sopenharmony_ci			break;
23588c2ecf20Sopenharmony_ci	}
23598c2ecf20Sopenharmony_ci	spin_unlock_bh(&ap->sta_table_lock);
23608c2ecf20Sopenharmony_ci
23618c2ecf20Sopenharmony_ci	return count;
23628c2ecf20Sopenharmony_ci}
23638c2ecf20Sopenharmony_ci
23648c2ecf20Sopenharmony_ci
23658c2ecf20Sopenharmony_ci/* Translate our list of Access Points & Stations to a card independent
23668c2ecf20Sopenharmony_ci * format that the Wireless Tools will understand - Jean II */
23678c2ecf20Sopenharmony_ciint prism2_ap_translate_scan(struct net_device *dev,
23688c2ecf20Sopenharmony_ci			     struct iw_request_info *info, char *buffer)
23698c2ecf20Sopenharmony_ci{
23708c2ecf20Sopenharmony_ci	struct hostap_interface *iface;
23718c2ecf20Sopenharmony_ci	local_info_t *local;
23728c2ecf20Sopenharmony_ci	struct ap_data *ap;
23738c2ecf20Sopenharmony_ci	struct list_head *ptr;
23748c2ecf20Sopenharmony_ci	struct iw_event iwe;
23758c2ecf20Sopenharmony_ci	char *current_ev = buffer;
23768c2ecf20Sopenharmony_ci	char *end_buf = buffer + IW_SCAN_MAX_DATA;
23778c2ecf20Sopenharmony_ci#if !defined(PRISM2_NO_KERNEL_IEEE80211_MGMT)
23788c2ecf20Sopenharmony_ci	char buf[64];
23798c2ecf20Sopenharmony_ci#endif
23808c2ecf20Sopenharmony_ci
23818c2ecf20Sopenharmony_ci	iface = netdev_priv(dev);
23828c2ecf20Sopenharmony_ci	local = iface->local;
23838c2ecf20Sopenharmony_ci	ap = local->ap;
23848c2ecf20Sopenharmony_ci
23858c2ecf20Sopenharmony_ci	spin_lock_bh(&ap->sta_table_lock);
23868c2ecf20Sopenharmony_ci
23878c2ecf20Sopenharmony_ci	for (ptr = ap->sta_list.next; ptr != NULL && ptr != &ap->sta_list;
23888c2ecf20Sopenharmony_ci	     ptr = ptr->next) {
23898c2ecf20Sopenharmony_ci		struct sta_info *sta = (struct sta_info *) ptr;
23908c2ecf20Sopenharmony_ci
23918c2ecf20Sopenharmony_ci		/* First entry *MUST* be the AP MAC address */
23928c2ecf20Sopenharmony_ci		memset(&iwe, 0, sizeof(iwe));
23938c2ecf20Sopenharmony_ci		iwe.cmd = SIOCGIWAP;
23948c2ecf20Sopenharmony_ci		iwe.u.ap_addr.sa_family = ARPHRD_ETHER;
23958c2ecf20Sopenharmony_ci		memcpy(iwe.u.ap_addr.sa_data, sta->addr, ETH_ALEN);
23968c2ecf20Sopenharmony_ci		iwe.len = IW_EV_ADDR_LEN;
23978c2ecf20Sopenharmony_ci		current_ev = iwe_stream_add_event(info, current_ev, end_buf,
23988c2ecf20Sopenharmony_ci						  &iwe, IW_EV_ADDR_LEN);
23998c2ecf20Sopenharmony_ci
24008c2ecf20Sopenharmony_ci		/* Use the mode to indicate if it's a station or
24018c2ecf20Sopenharmony_ci		 * an Access Point */
24028c2ecf20Sopenharmony_ci		memset(&iwe, 0, sizeof(iwe));
24038c2ecf20Sopenharmony_ci		iwe.cmd = SIOCGIWMODE;
24048c2ecf20Sopenharmony_ci		if (sta->ap)
24058c2ecf20Sopenharmony_ci			iwe.u.mode = IW_MODE_MASTER;
24068c2ecf20Sopenharmony_ci		else
24078c2ecf20Sopenharmony_ci			iwe.u.mode = IW_MODE_INFRA;
24088c2ecf20Sopenharmony_ci		iwe.len = IW_EV_UINT_LEN;
24098c2ecf20Sopenharmony_ci		current_ev = iwe_stream_add_event(info, current_ev, end_buf,
24108c2ecf20Sopenharmony_ci						  &iwe, IW_EV_UINT_LEN);
24118c2ecf20Sopenharmony_ci
24128c2ecf20Sopenharmony_ci		/* Some quality */
24138c2ecf20Sopenharmony_ci		memset(&iwe, 0, sizeof(iwe));
24148c2ecf20Sopenharmony_ci		iwe.cmd = IWEVQUAL;
24158c2ecf20Sopenharmony_ci		if (sta->last_rx_silence == 0)
24168c2ecf20Sopenharmony_ci			iwe.u.qual.qual = sta->last_rx_signal < 27 ?
24178c2ecf20Sopenharmony_ci				0 : (sta->last_rx_signal - 27) * 92 / 127;
24188c2ecf20Sopenharmony_ci		else
24198c2ecf20Sopenharmony_ci			iwe.u.qual.qual = sta->last_rx_signal -
24208c2ecf20Sopenharmony_ci				sta->last_rx_silence - 35;
24218c2ecf20Sopenharmony_ci		iwe.u.qual.level = HFA384X_LEVEL_TO_dBm(sta->last_rx_signal);
24228c2ecf20Sopenharmony_ci		iwe.u.qual.noise = HFA384X_LEVEL_TO_dBm(sta->last_rx_silence);
24238c2ecf20Sopenharmony_ci		iwe.u.qual.updated = sta->last_rx_updated;
24248c2ecf20Sopenharmony_ci		iwe.len = IW_EV_QUAL_LEN;
24258c2ecf20Sopenharmony_ci		current_ev = iwe_stream_add_event(info, current_ev, end_buf,
24268c2ecf20Sopenharmony_ci						  &iwe, IW_EV_QUAL_LEN);
24278c2ecf20Sopenharmony_ci
24288c2ecf20Sopenharmony_ci#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT
24298c2ecf20Sopenharmony_ci		if (sta->ap) {
24308c2ecf20Sopenharmony_ci			memset(&iwe, 0, sizeof(iwe));
24318c2ecf20Sopenharmony_ci			iwe.cmd = SIOCGIWESSID;
24328c2ecf20Sopenharmony_ci			iwe.u.data.length = sta->u.ap.ssid_len;
24338c2ecf20Sopenharmony_ci			iwe.u.data.flags = 1;
24348c2ecf20Sopenharmony_ci			current_ev = iwe_stream_add_point(info, current_ev,
24358c2ecf20Sopenharmony_ci							  end_buf, &iwe,
24368c2ecf20Sopenharmony_ci							  sta->u.ap.ssid);
24378c2ecf20Sopenharmony_ci
24388c2ecf20Sopenharmony_ci			memset(&iwe, 0, sizeof(iwe));
24398c2ecf20Sopenharmony_ci			iwe.cmd = SIOCGIWENCODE;
24408c2ecf20Sopenharmony_ci			if (sta->capability & WLAN_CAPABILITY_PRIVACY)
24418c2ecf20Sopenharmony_ci				iwe.u.data.flags =
24428c2ecf20Sopenharmony_ci					IW_ENCODE_ENABLED | IW_ENCODE_NOKEY;
24438c2ecf20Sopenharmony_ci			else
24448c2ecf20Sopenharmony_ci				iwe.u.data.flags = IW_ENCODE_DISABLED;
24458c2ecf20Sopenharmony_ci			current_ev = iwe_stream_add_point(info, current_ev,
24468c2ecf20Sopenharmony_ci							  end_buf, &iwe,
24478c2ecf20Sopenharmony_ci							  sta->u.ap.ssid);
24488c2ecf20Sopenharmony_ci
24498c2ecf20Sopenharmony_ci			if (sta->u.ap.channel > 0 &&
24508c2ecf20Sopenharmony_ci			    sta->u.ap.channel <= FREQ_COUNT) {
24518c2ecf20Sopenharmony_ci				memset(&iwe, 0, sizeof(iwe));
24528c2ecf20Sopenharmony_ci				iwe.cmd = SIOCGIWFREQ;
24538c2ecf20Sopenharmony_ci				iwe.u.freq.m = freq_list[sta->u.ap.channel - 1]
24548c2ecf20Sopenharmony_ci					* 100000;
24558c2ecf20Sopenharmony_ci				iwe.u.freq.e = 1;
24568c2ecf20Sopenharmony_ci				current_ev = iwe_stream_add_event(
24578c2ecf20Sopenharmony_ci					info, current_ev, end_buf, &iwe,
24588c2ecf20Sopenharmony_ci					IW_EV_FREQ_LEN);
24598c2ecf20Sopenharmony_ci			}
24608c2ecf20Sopenharmony_ci
24618c2ecf20Sopenharmony_ci			memset(&iwe, 0, sizeof(iwe));
24628c2ecf20Sopenharmony_ci			iwe.cmd = IWEVCUSTOM;
24638c2ecf20Sopenharmony_ci			sprintf(buf, "beacon_interval=%d",
24648c2ecf20Sopenharmony_ci				sta->listen_interval);
24658c2ecf20Sopenharmony_ci			iwe.u.data.length = strlen(buf);
24668c2ecf20Sopenharmony_ci			current_ev = iwe_stream_add_point(info, current_ev,
24678c2ecf20Sopenharmony_ci							  end_buf, &iwe, buf);
24688c2ecf20Sopenharmony_ci		}
24698c2ecf20Sopenharmony_ci#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */
24708c2ecf20Sopenharmony_ci
24718c2ecf20Sopenharmony_ci		sta->last_rx_updated = IW_QUAL_DBM;
24728c2ecf20Sopenharmony_ci
24738c2ecf20Sopenharmony_ci		/* To be continued, we should make good use of IWEVCUSTOM */
24748c2ecf20Sopenharmony_ci	}
24758c2ecf20Sopenharmony_ci
24768c2ecf20Sopenharmony_ci	spin_unlock_bh(&ap->sta_table_lock);
24778c2ecf20Sopenharmony_ci
24788c2ecf20Sopenharmony_ci	return current_ev - buffer;
24798c2ecf20Sopenharmony_ci}
24808c2ecf20Sopenharmony_ci
24818c2ecf20Sopenharmony_ci
24828c2ecf20Sopenharmony_cistatic int prism2_hostapd_add_sta(struct ap_data *ap,
24838c2ecf20Sopenharmony_ci				  struct prism2_hostapd_param *param)
24848c2ecf20Sopenharmony_ci{
24858c2ecf20Sopenharmony_ci	struct sta_info *sta;
24868c2ecf20Sopenharmony_ci
24878c2ecf20Sopenharmony_ci	spin_lock_bh(&ap->sta_table_lock);
24888c2ecf20Sopenharmony_ci	sta = ap_get_sta(ap, param->sta_addr);
24898c2ecf20Sopenharmony_ci	if (sta)
24908c2ecf20Sopenharmony_ci		atomic_inc(&sta->users);
24918c2ecf20Sopenharmony_ci	spin_unlock_bh(&ap->sta_table_lock);
24928c2ecf20Sopenharmony_ci
24938c2ecf20Sopenharmony_ci	if (sta == NULL) {
24948c2ecf20Sopenharmony_ci		sta = ap_add_sta(ap, param->sta_addr);
24958c2ecf20Sopenharmony_ci		if (sta == NULL)
24968c2ecf20Sopenharmony_ci			return -1;
24978c2ecf20Sopenharmony_ci	}
24988c2ecf20Sopenharmony_ci
24998c2ecf20Sopenharmony_ci	if (!(sta->flags & WLAN_STA_ASSOC) && !sta->ap && sta->local)
25008c2ecf20Sopenharmony_ci		hostap_event_new_sta(sta->local->dev, sta);
25018c2ecf20Sopenharmony_ci
25028c2ecf20Sopenharmony_ci	sta->flags |= WLAN_STA_AUTH | WLAN_STA_ASSOC;
25038c2ecf20Sopenharmony_ci	sta->last_rx = jiffies;
25048c2ecf20Sopenharmony_ci	sta->aid = param->u.add_sta.aid;
25058c2ecf20Sopenharmony_ci	sta->capability = param->u.add_sta.capability;
25068c2ecf20Sopenharmony_ci	sta->tx_supp_rates = param->u.add_sta.tx_supp_rates;
25078c2ecf20Sopenharmony_ci	if (sta->tx_supp_rates & WLAN_RATE_1M)
25088c2ecf20Sopenharmony_ci		sta->supported_rates[0] = 2;
25098c2ecf20Sopenharmony_ci	if (sta->tx_supp_rates & WLAN_RATE_2M)
25108c2ecf20Sopenharmony_ci		sta->supported_rates[1] = 4;
25118c2ecf20Sopenharmony_ci	if (sta->tx_supp_rates & WLAN_RATE_5M5)
25128c2ecf20Sopenharmony_ci		sta->supported_rates[2] = 11;
25138c2ecf20Sopenharmony_ci	if (sta->tx_supp_rates & WLAN_RATE_11M)
25148c2ecf20Sopenharmony_ci		sta->supported_rates[3] = 22;
25158c2ecf20Sopenharmony_ci	prism2_check_tx_rates(sta);
25168c2ecf20Sopenharmony_ci	atomic_dec(&sta->users);
25178c2ecf20Sopenharmony_ci	return 0;
25188c2ecf20Sopenharmony_ci}
25198c2ecf20Sopenharmony_ci
25208c2ecf20Sopenharmony_ci
25218c2ecf20Sopenharmony_cistatic int prism2_hostapd_remove_sta(struct ap_data *ap,
25228c2ecf20Sopenharmony_ci				     struct prism2_hostapd_param *param)
25238c2ecf20Sopenharmony_ci{
25248c2ecf20Sopenharmony_ci	struct sta_info *sta;
25258c2ecf20Sopenharmony_ci
25268c2ecf20Sopenharmony_ci	spin_lock_bh(&ap->sta_table_lock);
25278c2ecf20Sopenharmony_ci	sta = ap_get_sta(ap, param->sta_addr);
25288c2ecf20Sopenharmony_ci	if (sta) {
25298c2ecf20Sopenharmony_ci		ap_sta_hash_del(ap, sta);
25308c2ecf20Sopenharmony_ci		list_del(&sta->list);
25318c2ecf20Sopenharmony_ci	}
25328c2ecf20Sopenharmony_ci	spin_unlock_bh(&ap->sta_table_lock);
25338c2ecf20Sopenharmony_ci
25348c2ecf20Sopenharmony_ci	if (!sta)
25358c2ecf20Sopenharmony_ci		return -ENOENT;
25368c2ecf20Sopenharmony_ci
25378c2ecf20Sopenharmony_ci	if ((sta->flags & WLAN_STA_ASSOC) && !sta->ap && sta->local)
25388c2ecf20Sopenharmony_ci		hostap_event_expired_sta(sta->local->dev, sta);
25398c2ecf20Sopenharmony_ci	ap_free_sta(ap, sta);
25408c2ecf20Sopenharmony_ci
25418c2ecf20Sopenharmony_ci	return 0;
25428c2ecf20Sopenharmony_ci}
25438c2ecf20Sopenharmony_ci
25448c2ecf20Sopenharmony_ci
25458c2ecf20Sopenharmony_cistatic int prism2_hostapd_get_info_sta(struct ap_data *ap,
25468c2ecf20Sopenharmony_ci				       struct prism2_hostapd_param *param)
25478c2ecf20Sopenharmony_ci{
25488c2ecf20Sopenharmony_ci	struct sta_info *sta;
25498c2ecf20Sopenharmony_ci
25508c2ecf20Sopenharmony_ci	spin_lock_bh(&ap->sta_table_lock);
25518c2ecf20Sopenharmony_ci	sta = ap_get_sta(ap, param->sta_addr);
25528c2ecf20Sopenharmony_ci	if (sta)
25538c2ecf20Sopenharmony_ci		atomic_inc(&sta->users);
25548c2ecf20Sopenharmony_ci	spin_unlock_bh(&ap->sta_table_lock);
25558c2ecf20Sopenharmony_ci
25568c2ecf20Sopenharmony_ci	if (!sta)
25578c2ecf20Sopenharmony_ci		return -ENOENT;
25588c2ecf20Sopenharmony_ci
25598c2ecf20Sopenharmony_ci	param->u.get_info_sta.inactive_sec = (jiffies - sta->last_rx) / HZ;
25608c2ecf20Sopenharmony_ci
25618c2ecf20Sopenharmony_ci	atomic_dec(&sta->users);
25628c2ecf20Sopenharmony_ci
25638c2ecf20Sopenharmony_ci	return 1;
25648c2ecf20Sopenharmony_ci}
25658c2ecf20Sopenharmony_ci
25668c2ecf20Sopenharmony_ci
25678c2ecf20Sopenharmony_cistatic int prism2_hostapd_set_flags_sta(struct ap_data *ap,
25688c2ecf20Sopenharmony_ci					struct prism2_hostapd_param *param)
25698c2ecf20Sopenharmony_ci{
25708c2ecf20Sopenharmony_ci	struct sta_info *sta;
25718c2ecf20Sopenharmony_ci
25728c2ecf20Sopenharmony_ci	spin_lock_bh(&ap->sta_table_lock);
25738c2ecf20Sopenharmony_ci	sta = ap_get_sta(ap, param->sta_addr);
25748c2ecf20Sopenharmony_ci	if (sta) {
25758c2ecf20Sopenharmony_ci		sta->flags |= param->u.set_flags_sta.flags_or;
25768c2ecf20Sopenharmony_ci		sta->flags &= param->u.set_flags_sta.flags_and;
25778c2ecf20Sopenharmony_ci	}
25788c2ecf20Sopenharmony_ci	spin_unlock_bh(&ap->sta_table_lock);
25798c2ecf20Sopenharmony_ci
25808c2ecf20Sopenharmony_ci	if (!sta)
25818c2ecf20Sopenharmony_ci		return -ENOENT;
25828c2ecf20Sopenharmony_ci
25838c2ecf20Sopenharmony_ci	return 0;
25848c2ecf20Sopenharmony_ci}
25858c2ecf20Sopenharmony_ci
25868c2ecf20Sopenharmony_ci
25878c2ecf20Sopenharmony_cistatic int prism2_hostapd_sta_clear_stats(struct ap_data *ap,
25888c2ecf20Sopenharmony_ci					  struct prism2_hostapd_param *param)
25898c2ecf20Sopenharmony_ci{
25908c2ecf20Sopenharmony_ci	struct sta_info *sta;
25918c2ecf20Sopenharmony_ci	int rate;
25928c2ecf20Sopenharmony_ci
25938c2ecf20Sopenharmony_ci	spin_lock_bh(&ap->sta_table_lock);
25948c2ecf20Sopenharmony_ci	sta = ap_get_sta(ap, param->sta_addr);
25958c2ecf20Sopenharmony_ci	if (sta) {
25968c2ecf20Sopenharmony_ci		sta->rx_packets = sta->tx_packets = 0;
25978c2ecf20Sopenharmony_ci		sta->rx_bytes = sta->tx_bytes = 0;
25988c2ecf20Sopenharmony_ci		for (rate = 0; rate < WLAN_RATE_COUNT; rate++) {
25998c2ecf20Sopenharmony_ci			sta->tx_count[rate] = 0;
26008c2ecf20Sopenharmony_ci			sta->rx_count[rate] = 0;
26018c2ecf20Sopenharmony_ci		}
26028c2ecf20Sopenharmony_ci	}
26038c2ecf20Sopenharmony_ci	spin_unlock_bh(&ap->sta_table_lock);
26048c2ecf20Sopenharmony_ci
26058c2ecf20Sopenharmony_ci	if (!sta)
26068c2ecf20Sopenharmony_ci		return -ENOENT;
26078c2ecf20Sopenharmony_ci
26088c2ecf20Sopenharmony_ci	return 0;
26098c2ecf20Sopenharmony_ci}
26108c2ecf20Sopenharmony_ci
26118c2ecf20Sopenharmony_ci
26128c2ecf20Sopenharmony_ciint prism2_hostapd(struct ap_data *ap, struct prism2_hostapd_param *param)
26138c2ecf20Sopenharmony_ci{
26148c2ecf20Sopenharmony_ci	switch (param->cmd) {
26158c2ecf20Sopenharmony_ci	case PRISM2_HOSTAPD_FLUSH:
26168c2ecf20Sopenharmony_ci		ap_control_kickall(ap);
26178c2ecf20Sopenharmony_ci		return 0;
26188c2ecf20Sopenharmony_ci	case PRISM2_HOSTAPD_ADD_STA:
26198c2ecf20Sopenharmony_ci		return prism2_hostapd_add_sta(ap, param);
26208c2ecf20Sopenharmony_ci	case PRISM2_HOSTAPD_REMOVE_STA:
26218c2ecf20Sopenharmony_ci		return prism2_hostapd_remove_sta(ap, param);
26228c2ecf20Sopenharmony_ci	case PRISM2_HOSTAPD_GET_INFO_STA:
26238c2ecf20Sopenharmony_ci		return prism2_hostapd_get_info_sta(ap, param);
26248c2ecf20Sopenharmony_ci	case PRISM2_HOSTAPD_SET_FLAGS_STA:
26258c2ecf20Sopenharmony_ci		return prism2_hostapd_set_flags_sta(ap, param);
26268c2ecf20Sopenharmony_ci	case PRISM2_HOSTAPD_STA_CLEAR_STATS:
26278c2ecf20Sopenharmony_ci		return prism2_hostapd_sta_clear_stats(ap, param);
26288c2ecf20Sopenharmony_ci	default:
26298c2ecf20Sopenharmony_ci		printk(KERN_WARNING "prism2_hostapd: unknown cmd=%d\n",
26308c2ecf20Sopenharmony_ci		       param->cmd);
26318c2ecf20Sopenharmony_ci		return -EOPNOTSUPP;
26328c2ecf20Sopenharmony_ci	}
26338c2ecf20Sopenharmony_ci}
26348c2ecf20Sopenharmony_ci
26358c2ecf20Sopenharmony_ci
26368c2ecf20Sopenharmony_ci/* Update station info for host-based TX rate control and return current
26378c2ecf20Sopenharmony_ci * TX rate */
26388c2ecf20Sopenharmony_cistatic int ap_update_sta_tx_rate(struct sta_info *sta, struct net_device *dev)
26398c2ecf20Sopenharmony_ci{
26408c2ecf20Sopenharmony_ci	int ret = sta->tx_rate;
26418c2ecf20Sopenharmony_ci	struct hostap_interface *iface;
26428c2ecf20Sopenharmony_ci	local_info_t *local;
26438c2ecf20Sopenharmony_ci
26448c2ecf20Sopenharmony_ci	iface = netdev_priv(dev);
26458c2ecf20Sopenharmony_ci	local = iface->local;
26468c2ecf20Sopenharmony_ci
26478c2ecf20Sopenharmony_ci	sta->tx_count[sta->tx_rate_idx]++;
26488c2ecf20Sopenharmony_ci	sta->tx_since_last_failure++;
26498c2ecf20Sopenharmony_ci	sta->tx_consecutive_exc = 0;
26508c2ecf20Sopenharmony_ci	if (sta->tx_since_last_failure >= WLAN_RATE_UPDATE_COUNT &&
26518c2ecf20Sopenharmony_ci	    sta->tx_rate_idx < sta->tx_max_rate) {
26528c2ecf20Sopenharmony_ci		/* use next higher rate */
26538c2ecf20Sopenharmony_ci		int old_rate, new_rate;
26548c2ecf20Sopenharmony_ci		old_rate = new_rate = sta->tx_rate_idx;
26558c2ecf20Sopenharmony_ci		while (new_rate < sta->tx_max_rate) {
26568c2ecf20Sopenharmony_ci			new_rate++;
26578c2ecf20Sopenharmony_ci			if (ap_tx_rate_ok(new_rate, sta, local)) {
26588c2ecf20Sopenharmony_ci				sta->tx_rate_idx = new_rate;
26598c2ecf20Sopenharmony_ci				break;
26608c2ecf20Sopenharmony_ci			}
26618c2ecf20Sopenharmony_ci		}
26628c2ecf20Sopenharmony_ci		if (old_rate != sta->tx_rate_idx) {
26638c2ecf20Sopenharmony_ci			switch (sta->tx_rate_idx) {
26648c2ecf20Sopenharmony_ci			case 0: sta->tx_rate = 10; break;
26658c2ecf20Sopenharmony_ci			case 1: sta->tx_rate = 20; break;
26668c2ecf20Sopenharmony_ci			case 2: sta->tx_rate = 55; break;
26678c2ecf20Sopenharmony_ci			case 3: sta->tx_rate = 110; break;
26688c2ecf20Sopenharmony_ci			default: sta->tx_rate = 0; break;
26698c2ecf20Sopenharmony_ci			}
26708c2ecf20Sopenharmony_ci			PDEBUG(DEBUG_AP, "%s: STA %pM TX rate raised to %d\n",
26718c2ecf20Sopenharmony_ci			       dev->name, sta->addr, sta->tx_rate);
26728c2ecf20Sopenharmony_ci		}
26738c2ecf20Sopenharmony_ci		sta->tx_since_last_failure = 0;
26748c2ecf20Sopenharmony_ci	}
26758c2ecf20Sopenharmony_ci
26768c2ecf20Sopenharmony_ci	return ret;
26778c2ecf20Sopenharmony_ci}
26788c2ecf20Sopenharmony_ci
26798c2ecf20Sopenharmony_ci
26808c2ecf20Sopenharmony_ci/* Called only from software IRQ. Called for each TX frame prior possible
26818c2ecf20Sopenharmony_ci * encryption and transmit. */
26828c2ecf20Sopenharmony_ciap_tx_ret hostap_handle_sta_tx(local_info_t *local, struct hostap_tx_data *tx)
26838c2ecf20Sopenharmony_ci{
26848c2ecf20Sopenharmony_ci	struct sta_info *sta = NULL;
26858c2ecf20Sopenharmony_ci	struct sk_buff *skb = tx->skb;
26868c2ecf20Sopenharmony_ci	int set_tim, ret;
26878c2ecf20Sopenharmony_ci	struct ieee80211_hdr *hdr;
26888c2ecf20Sopenharmony_ci	struct hostap_skb_tx_data *meta;
26898c2ecf20Sopenharmony_ci
26908c2ecf20Sopenharmony_ci	meta = (struct hostap_skb_tx_data *) skb->cb;
26918c2ecf20Sopenharmony_ci	ret = AP_TX_CONTINUE;
26928c2ecf20Sopenharmony_ci	if (local->ap == NULL || skb->len < 10 ||
26938c2ecf20Sopenharmony_ci	    meta->iface->type == HOSTAP_INTERFACE_STA)
26948c2ecf20Sopenharmony_ci		goto out;
26958c2ecf20Sopenharmony_ci
26968c2ecf20Sopenharmony_ci	hdr = (struct ieee80211_hdr *) skb->data;
26978c2ecf20Sopenharmony_ci
26988c2ecf20Sopenharmony_ci	if (hdr->addr1[0] & 0x01) {
26998c2ecf20Sopenharmony_ci		/* broadcast/multicast frame - no AP related processing */
27008c2ecf20Sopenharmony_ci		if (local->ap->num_sta <= 0)
27018c2ecf20Sopenharmony_ci			ret = AP_TX_DROP;
27028c2ecf20Sopenharmony_ci		goto out;
27038c2ecf20Sopenharmony_ci	}
27048c2ecf20Sopenharmony_ci
27058c2ecf20Sopenharmony_ci	/* unicast packet - check whether destination STA is associated */
27068c2ecf20Sopenharmony_ci	spin_lock(&local->ap->sta_table_lock);
27078c2ecf20Sopenharmony_ci	sta = ap_get_sta(local->ap, hdr->addr1);
27088c2ecf20Sopenharmony_ci	if (sta)
27098c2ecf20Sopenharmony_ci		atomic_inc(&sta->users);
27108c2ecf20Sopenharmony_ci	spin_unlock(&local->ap->sta_table_lock);
27118c2ecf20Sopenharmony_ci
27128c2ecf20Sopenharmony_ci	if (local->iw_mode == IW_MODE_MASTER && sta == NULL &&
27138c2ecf20Sopenharmony_ci	    !(meta->flags & HOSTAP_TX_FLAGS_WDS) &&
27148c2ecf20Sopenharmony_ci	    meta->iface->type != HOSTAP_INTERFACE_MASTER &&
27158c2ecf20Sopenharmony_ci	    meta->iface->type != HOSTAP_INTERFACE_AP) {
27168c2ecf20Sopenharmony_ci#if 0
27178c2ecf20Sopenharmony_ci		/* This can happen, e.g., when wlan0 is added to a bridge and
27188c2ecf20Sopenharmony_ci		 * bridging code does not know which port is the correct target
27198c2ecf20Sopenharmony_ci		 * for a unicast frame. In this case, the packet is send to all
27208c2ecf20Sopenharmony_ci		 * ports of the bridge. Since this is a valid scenario, do not
27218c2ecf20Sopenharmony_ci		 * print out any errors here. */
27228c2ecf20Sopenharmony_ci		if (net_ratelimit()) {
27238c2ecf20Sopenharmony_ci			printk(KERN_DEBUG "AP: drop packet to non-associated "
27248c2ecf20Sopenharmony_ci			       "STA %pM\n", hdr->addr1);
27258c2ecf20Sopenharmony_ci		}
27268c2ecf20Sopenharmony_ci#endif
27278c2ecf20Sopenharmony_ci		local->ap->tx_drop_nonassoc++;
27288c2ecf20Sopenharmony_ci		ret = AP_TX_DROP;
27298c2ecf20Sopenharmony_ci		goto out;
27308c2ecf20Sopenharmony_ci	}
27318c2ecf20Sopenharmony_ci
27328c2ecf20Sopenharmony_ci	if (sta == NULL)
27338c2ecf20Sopenharmony_ci		goto out;
27348c2ecf20Sopenharmony_ci
27358c2ecf20Sopenharmony_ci	if (!(sta->flags & WLAN_STA_AUTHORIZED))
27368c2ecf20Sopenharmony_ci		ret = AP_TX_CONTINUE_NOT_AUTHORIZED;
27378c2ecf20Sopenharmony_ci
27388c2ecf20Sopenharmony_ci	/* Set tx_rate if using host-based TX rate control */
27398c2ecf20Sopenharmony_ci	if (!local->fw_tx_rate_control)
27408c2ecf20Sopenharmony_ci		local->ap->last_tx_rate = meta->rate =
27418c2ecf20Sopenharmony_ci			ap_update_sta_tx_rate(sta, local->dev);
27428c2ecf20Sopenharmony_ci
27438c2ecf20Sopenharmony_ci	if (local->iw_mode != IW_MODE_MASTER)
27448c2ecf20Sopenharmony_ci		goto out;
27458c2ecf20Sopenharmony_ci
27468c2ecf20Sopenharmony_ci	if (!(sta->flags & WLAN_STA_PS))
27478c2ecf20Sopenharmony_ci		goto out;
27488c2ecf20Sopenharmony_ci
27498c2ecf20Sopenharmony_ci	if (meta->flags & HOSTAP_TX_FLAGS_ADD_MOREDATA) {
27508c2ecf20Sopenharmony_ci		/* indicate to STA that more frames follow */
27518c2ecf20Sopenharmony_ci		hdr->frame_control |=
27528c2ecf20Sopenharmony_ci			cpu_to_le16(IEEE80211_FCTL_MOREDATA);
27538c2ecf20Sopenharmony_ci	}
27548c2ecf20Sopenharmony_ci
27558c2ecf20Sopenharmony_ci	if (meta->flags & HOSTAP_TX_FLAGS_BUFFERED_FRAME) {
27568c2ecf20Sopenharmony_ci		/* packet was already buffered and now send due to
27578c2ecf20Sopenharmony_ci		 * PS poll, so do not rebuffer it */
27588c2ecf20Sopenharmony_ci		goto out;
27598c2ecf20Sopenharmony_ci	}
27608c2ecf20Sopenharmony_ci
27618c2ecf20Sopenharmony_ci	if (skb_queue_len(&sta->tx_buf) >= STA_MAX_TX_BUFFER) {
27628c2ecf20Sopenharmony_ci		PDEBUG(DEBUG_PS, "%s: No more space in STA (%pM)'s"
27638c2ecf20Sopenharmony_ci		       "PS mode buffer\n",
27648c2ecf20Sopenharmony_ci		       local->dev->name, sta->addr);
27658c2ecf20Sopenharmony_ci		/* Make sure that TIM is set for the station (it might not be
27668c2ecf20Sopenharmony_ci		 * after AP wlan hw reset). */
27678c2ecf20Sopenharmony_ci		/* FIX: should fix hw reset to restore bits based on STA
27688c2ecf20Sopenharmony_ci		 * buffer state.. */
27698c2ecf20Sopenharmony_ci		hostap_set_tim(local, sta->aid, 1);
27708c2ecf20Sopenharmony_ci		sta->flags |= WLAN_STA_TIM;
27718c2ecf20Sopenharmony_ci		ret = AP_TX_DROP;
27728c2ecf20Sopenharmony_ci		goto out;
27738c2ecf20Sopenharmony_ci	}
27748c2ecf20Sopenharmony_ci
27758c2ecf20Sopenharmony_ci	/* STA in PS mode, buffer frame for later delivery */
27768c2ecf20Sopenharmony_ci	set_tim = skb_queue_empty(&sta->tx_buf);
27778c2ecf20Sopenharmony_ci	skb_queue_tail(&sta->tx_buf, skb);
27788c2ecf20Sopenharmony_ci	/* FIX: could save RX time to skb and expire buffered frames after
27798c2ecf20Sopenharmony_ci	 * some time if STA does not poll for them */
27808c2ecf20Sopenharmony_ci
27818c2ecf20Sopenharmony_ci	if (set_tim) {
27828c2ecf20Sopenharmony_ci		if (sta->flags & WLAN_STA_TIM)
27838c2ecf20Sopenharmony_ci			PDEBUG(DEBUG_PS2, "Re-setting TIM for aid %d\n",
27848c2ecf20Sopenharmony_ci			       sta->aid);
27858c2ecf20Sopenharmony_ci		hostap_set_tim(local, sta->aid, 1);
27868c2ecf20Sopenharmony_ci		sta->flags |= WLAN_STA_TIM;
27878c2ecf20Sopenharmony_ci	}
27888c2ecf20Sopenharmony_ci
27898c2ecf20Sopenharmony_ci	ret = AP_TX_BUFFERED;
27908c2ecf20Sopenharmony_ci
27918c2ecf20Sopenharmony_ci out:
27928c2ecf20Sopenharmony_ci	if (sta != NULL) {
27938c2ecf20Sopenharmony_ci		if (ret == AP_TX_CONTINUE ||
27948c2ecf20Sopenharmony_ci		    ret == AP_TX_CONTINUE_NOT_AUTHORIZED) {
27958c2ecf20Sopenharmony_ci			sta->tx_packets++;
27968c2ecf20Sopenharmony_ci			sta->tx_bytes += skb->len;
27978c2ecf20Sopenharmony_ci			sta->last_tx = jiffies;
27988c2ecf20Sopenharmony_ci		}
27998c2ecf20Sopenharmony_ci
28008c2ecf20Sopenharmony_ci		if ((ret == AP_TX_CONTINUE ||
28018c2ecf20Sopenharmony_ci		     ret == AP_TX_CONTINUE_NOT_AUTHORIZED) &&
28028c2ecf20Sopenharmony_ci		    sta->crypt && tx->host_encrypt) {
28038c2ecf20Sopenharmony_ci			tx->crypt = sta->crypt;
28048c2ecf20Sopenharmony_ci			tx->sta_ptr = sta; /* hostap_handle_sta_release() will
28058c2ecf20Sopenharmony_ci					    * be called to release sta info
28068c2ecf20Sopenharmony_ci					    * later */
28078c2ecf20Sopenharmony_ci		} else
28088c2ecf20Sopenharmony_ci			atomic_dec(&sta->users);
28098c2ecf20Sopenharmony_ci	}
28108c2ecf20Sopenharmony_ci
28118c2ecf20Sopenharmony_ci	return ret;
28128c2ecf20Sopenharmony_ci}
28138c2ecf20Sopenharmony_ci
28148c2ecf20Sopenharmony_ci
28158c2ecf20Sopenharmony_civoid hostap_handle_sta_release(void *ptr)
28168c2ecf20Sopenharmony_ci{
28178c2ecf20Sopenharmony_ci	struct sta_info *sta = ptr;
28188c2ecf20Sopenharmony_ci	atomic_dec(&sta->users);
28198c2ecf20Sopenharmony_ci}
28208c2ecf20Sopenharmony_ci
28218c2ecf20Sopenharmony_ci
28228c2ecf20Sopenharmony_ci/* Called only as a tasklet (software IRQ) */
28238c2ecf20Sopenharmony_civoid hostap_handle_sta_tx_exc(local_info_t *local, struct sk_buff *skb)
28248c2ecf20Sopenharmony_ci{
28258c2ecf20Sopenharmony_ci	struct sta_info *sta;
28268c2ecf20Sopenharmony_ci	struct ieee80211_hdr *hdr;
28278c2ecf20Sopenharmony_ci	struct hostap_skb_tx_data *meta;
28288c2ecf20Sopenharmony_ci
28298c2ecf20Sopenharmony_ci	hdr = (struct ieee80211_hdr *) skb->data;
28308c2ecf20Sopenharmony_ci	meta = (struct hostap_skb_tx_data *) skb->cb;
28318c2ecf20Sopenharmony_ci
28328c2ecf20Sopenharmony_ci	spin_lock(&local->ap->sta_table_lock);
28338c2ecf20Sopenharmony_ci	sta = ap_get_sta(local->ap, hdr->addr1);
28348c2ecf20Sopenharmony_ci	if (!sta) {
28358c2ecf20Sopenharmony_ci		spin_unlock(&local->ap->sta_table_lock);
28368c2ecf20Sopenharmony_ci		PDEBUG(DEBUG_AP, "%s: Could not find STA %pM"
28378c2ecf20Sopenharmony_ci		       " for this TX error (@%lu)\n",
28388c2ecf20Sopenharmony_ci		       local->dev->name, hdr->addr1, jiffies);
28398c2ecf20Sopenharmony_ci		return;
28408c2ecf20Sopenharmony_ci	}
28418c2ecf20Sopenharmony_ci
28428c2ecf20Sopenharmony_ci	sta->tx_since_last_failure = 0;
28438c2ecf20Sopenharmony_ci	sta->tx_consecutive_exc++;
28448c2ecf20Sopenharmony_ci
28458c2ecf20Sopenharmony_ci	if (sta->tx_consecutive_exc >= WLAN_RATE_DECREASE_THRESHOLD &&
28468c2ecf20Sopenharmony_ci	    sta->tx_rate_idx > 0 && meta->rate <= sta->tx_rate) {
28478c2ecf20Sopenharmony_ci		/* use next lower rate */
28488c2ecf20Sopenharmony_ci		int old, rate;
28498c2ecf20Sopenharmony_ci		old = rate = sta->tx_rate_idx;
28508c2ecf20Sopenharmony_ci		while (rate > 0) {
28518c2ecf20Sopenharmony_ci			rate--;
28528c2ecf20Sopenharmony_ci			if (ap_tx_rate_ok(rate, sta, local)) {
28538c2ecf20Sopenharmony_ci				sta->tx_rate_idx = rate;
28548c2ecf20Sopenharmony_ci				break;
28558c2ecf20Sopenharmony_ci			}
28568c2ecf20Sopenharmony_ci		}
28578c2ecf20Sopenharmony_ci		if (old != sta->tx_rate_idx) {
28588c2ecf20Sopenharmony_ci			switch (sta->tx_rate_idx) {
28598c2ecf20Sopenharmony_ci			case 0: sta->tx_rate = 10; break;
28608c2ecf20Sopenharmony_ci			case 1: sta->tx_rate = 20; break;
28618c2ecf20Sopenharmony_ci			case 2: sta->tx_rate = 55; break;
28628c2ecf20Sopenharmony_ci			case 3: sta->tx_rate = 110; break;
28638c2ecf20Sopenharmony_ci			default: sta->tx_rate = 0; break;
28648c2ecf20Sopenharmony_ci			}
28658c2ecf20Sopenharmony_ci			PDEBUG(DEBUG_AP,
28668c2ecf20Sopenharmony_ci			       "%s: STA %pM TX rate lowered to %d\n",
28678c2ecf20Sopenharmony_ci			       local->dev->name, sta->addr, sta->tx_rate);
28688c2ecf20Sopenharmony_ci		}
28698c2ecf20Sopenharmony_ci		sta->tx_consecutive_exc = 0;
28708c2ecf20Sopenharmony_ci	}
28718c2ecf20Sopenharmony_ci	spin_unlock(&local->ap->sta_table_lock);
28728c2ecf20Sopenharmony_ci}
28738c2ecf20Sopenharmony_ci
28748c2ecf20Sopenharmony_ci
28758c2ecf20Sopenharmony_cistatic void hostap_update_sta_ps2(local_info_t *local, struct sta_info *sta,
28768c2ecf20Sopenharmony_ci				  int pwrmgt, int type, int stype)
28778c2ecf20Sopenharmony_ci{
28788c2ecf20Sopenharmony_ci	if (pwrmgt && !(sta->flags & WLAN_STA_PS)) {
28798c2ecf20Sopenharmony_ci		sta->flags |= WLAN_STA_PS;
28808c2ecf20Sopenharmony_ci		PDEBUG(DEBUG_PS2, "STA %pM changed to use PS "
28818c2ecf20Sopenharmony_ci		       "mode (type=0x%02X, stype=0x%02X)\n",
28828c2ecf20Sopenharmony_ci		       sta->addr, type >> 2, stype >> 4);
28838c2ecf20Sopenharmony_ci	} else if (!pwrmgt && (sta->flags & WLAN_STA_PS)) {
28848c2ecf20Sopenharmony_ci		sta->flags &= ~WLAN_STA_PS;
28858c2ecf20Sopenharmony_ci		PDEBUG(DEBUG_PS2, "STA %pM changed to not use "
28868c2ecf20Sopenharmony_ci		       "PS mode (type=0x%02X, stype=0x%02X)\n",
28878c2ecf20Sopenharmony_ci		       sta->addr, type >> 2, stype >> 4);
28888c2ecf20Sopenharmony_ci		if (type != IEEE80211_FTYPE_CTL ||
28898c2ecf20Sopenharmony_ci		    stype != IEEE80211_STYPE_PSPOLL)
28908c2ecf20Sopenharmony_ci			schedule_packet_send(local, sta);
28918c2ecf20Sopenharmony_ci	}
28928c2ecf20Sopenharmony_ci}
28938c2ecf20Sopenharmony_ci
28948c2ecf20Sopenharmony_ci
28958c2ecf20Sopenharmony_ci/* Called only as a tasklet (software IRQ). Called for each RX frame to update
28968c2ecf20Sopenharmony_ci * STA power saving state. pwrmgt is a flag from 802.11 frame_control field. */
28978c2ecf20Sopenharmony_ciint hostap_update_sta_ps(local_info_t *local, struct ieee80211_hdr *hdr)
28988c2ecf20Sopenharmony_ci{
28998c2ecf20Sopenharmony_ci	struct sta_info *sta;
29008c2ecf20Sopenharmony_ci	u16 fc;
29018c2ecf20Sopenharmony_ci
29028c2ecf20Sopenharmony_ci	spin_lock(&local->ap->sta_table_lock);
29038c2ecf20Sopenharmony_ci	sta = ap_get_sta(local->ap, hdr->addr2);
29048c2ecf20Sopenharmony_ci	if (sta)
29058c2ecf20Sopenharmony_ci		atomic_inc(&sta->users);
29068c2ecf20Sopenharmony_ci	spin_unlock(&local->ap->sta_table_lock);
29078c2ecf20Sopenharmony_ci
29088c2ecf20Sopenharmony_ci	if (!sta)
29098c2ecf20Sopenharmony_ci		return -1;
29108c2ecf20Sopenharmony_ci
29118c2ecf20Sopenharmony_ci	fc = le16_to_cpu(hdr->frame_control);
29128c2ecf20Sopenharmony_ci	hostap_update_sta_ps2(local, sta, fc & IEEE80211_FCTL_PM,
29138c2ecf20Sopenharmony_ci			      fc & IEEE80211_FCTL_FTYPE,
29148c2ecf20Sopenharmony_ci			      fc & IEEE80211_FCTL_STYPE);
29158c2ecf20Sopenharmony_ci
29168c2ecf20Sopenharmony_ci	atomic_dec(&sta->users);
29178c2ecf20Sopenharmony_ci	return 0;
29188c2ecf20Sopenharmony_ci}
29198c2ecf20Sopenharmony_ci
29208c2ecf20Sopenharmony_ci
29218c2ecf20Sopenharmony_ci/* Called only as a tasklet (software IRQ). Called for each RX frame after
29228c2ecf20Sopenharmony_ci * getting RX header and payload from hardware. */
29238c2ecf20Sopenharmony_ciap_rx_ret hostap_handle_sta_rx(local_info_t *local, struct net_device *dev,
29248c2ecf20Sopenharmony_ci			       struct sk_buff *skb,
29258c2ecf20Sopenharmony_ci			       struct hostap_80211_rx_status *rx_stats,
29268c2ecf20Sopenharmony_ci			       int wds)
29278c2ecf20Sopenharmony_ci{
29288c2ecf20Sopenharmony_ci	int ret;
29298c2ecf20Sopenharmony_ci	struct sta_info *sta;
29308c2ecf20Sopenharmony_ci	u16 fc, type, stype;
29318c2ecf20Sopenharmony_ci	struct ieee80211_hdr *hdr;
29328c2ecf20Sopenharmony_ci
29338c2ecf20Sopenharmony_ci	if (local->ap == NULL)
29348c2ecf20Sopenharmony_ci		return AP_RX_CONTINUE;
29358c2ecf20Sopenharmony_ci
29368c2ecf20Sopenharmony_ci	hdr = (struct ieee80211_hdr *) skb->data;
29378c2ecf20Sopenharmony_ci
29388c2ecf20Sopenharmony_ci	fc = le16_to_cpu(hdr->frame_control);
29398c2ecf20Sopenharmony_ci	type = fc & IEEE80211_FCTL_FTYPE;
29408c2ecf20Sopenharmony_ci	stype = fc & IEEE80211_FCTL_STYPE;
29418c2ecf20Sopenharmony_ci
29428c2ecf20Sopenharmony_ci	spin_lock(&local->ap->sta_table_lock);
29438c2ecf20Sopenharmony_ci	sta = ap_get_sta(local->ap, hdr->addr2);
29448c2ecf20Sopenharmony_ci	if (sta)
29458c2ecf20Sopenharmony_ci		atomic_inc(&sta->users);
29468c2ecf20Sopenharmony_ci	spin_unlock(&local->ap->sta_table_lock);
29478c2ecf20Sopenharmony_ci
29488c2ecf20Sopenharmony_ci	if (sta && !(sta->flags & WLAN_STA_AUTHORIZED))
29498c2ecf20Sopenharmony_ci		ret = AP_RX_CONTINUE_NOT_AUTHORIZED;
29508c2ecf20Sopenharmony_ci	else
29518c2ecf20Sopenharmony_ci		ret = AP_RX_CONTINUE;
29528c2ecf20Sopenharmony_ci
29538c2ecf20Sopenharmony_ci
29548c2ecf20Sopenharmony_ci	if (fc & IEEE80211_FCTL_TODS) {
29558c2ecf20Sopenharmony_ci		if (!wds && (sta == NULL || !(sta->flags & WLAN_STA_ASSOC))) {
29568c2ecf20Sopenharmony_ci			if (local->hostapd) {
29578c2ecf20Sopenharmony_ci				prism2_rx_80211(local->apdev, skb, rx_stats,
29588c2ecf20Sopenharmony_ci						PRISM2_RX_NON_ASSOC);
29598c2ecf20Sopenharmony_ci#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT
29608c2ecf20Sopenharmony_ci			} else {
29618c2ecf20Sopenharmony_ci				printk(KERN_DEBUG "%s: dropped received packet"
29628c2ecf20Sopenharmony_ci				       " from non-associated STA %pM"
29638c2ecf20Sopenharmony_ci				       " (type=0x%02x, subtype=0x%02x)\n",
29648c2ecf20Sopenharmony_ci				       dev->name, hdr->addr2,
29658c2ecf20Sopenharmony_ci				       type >> 2, stype >> 4);
29668c2ecf20Sopenharmony_ci				hostap_rx(dev, skb, rx_stats);
29678c2ecf20Sopenharmony_ci#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */
29688c2ecf20Sopenharmony_ci			}
29698c2ecf20Sopenharmony_ci			ret = AP_RX_EXIT;
29708c2ecf20Sopenharmony_ci			goto out;
29718c2ecf20Sopenharmony_ci		}
29728c2ecf20Sopenharmony_ci	} else if (fc & IEEE80211_FCTL_FROMDS) {
29738c2ecf20Sopenharmony_ci		if (!wds) {
29748c2ecf20Sopenharmony_ci			/* FromDS frame - not for us; probably
29758c2ecf20Sopenharmony_ci			 * broadcast/multicast in another BSS - drop */
29768c2ecf20Sopenharmony_ci			if (ether_addr_equal(hdr->addr1, dev->dev_addr)) {
29778c2ecf20Sopenharmony_ci				printk(KERN_DEBUG "Odd.. FromDS packet "
29788c2ecf20Sopenharmony_ci				       "received with own BSSID\n");
29798c2ecf20Sopenharmony_ci				hostap_dump_rx_80211(dev->name, skb, rx_stats);
29808c2ecf20Sopenharmony_ci			}
29818c2ecf20Sopenharmony_ci			ret = AP_RX_DROP;
29828c2ecf20Sopenharmony_ci			goto out;
29838c2ecf20Sopenharmony_ci		}
29848c2ecf20Sopenharmony_ci	} else if (stype == IEEE80211_STYPE_NULLFUNC && sta == NULL &&
29858c2ecf20Sopenharmony_ci		   ether_addr_equal(hdr->addr1, dev->dev_addr)) {
29868c2ecf20Sopenharmony_ci
29878c2ecf20Sopenharmony_ci		if (local->hostapd) {
29888c2ecf20Sopenharmony_ci			prism2_rx_80211(local->apdev, skb, rx_stats,
29898c2ecf20Sopenharmony_ci					PRISM2_RX_NON_ASSOC);
29908c2ecf20Sopenharmony_ci#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT
29918c2ecf20Sopenharmony_ci		} else {
29928c2ecf20Sopenharmony_ci			/* At least Lucent f/w seems to send data::nullfunc
29938c2ecf20Sopenharmony_ci			 * frames with no ToDS flag when the current AP returns
29948c2ecf20Sopenharmony_ci			 * after being unavailable for some time. Speed up
29958c2ecf20Sopenharmony_ci			 * re-association by informing the station about it not
29968c2ecf20Sopenharmony_ci			 * being associated. */
29978c2ecf20Sopenharmony_ci			printk(KERN_DEBUG "%s: rejected received nullfunc frame"
29988c2ecf20Sopenharmony_ci			       " without ToDS from not associated STA %pM\n",
29998c2ecf20Sopenharmony_ci			       dev->name, hdr->addr2);
30008c2ecf20Sopenharmony_ci			hostap_rx(dev, skb, rx_stats);
30018c2ecf20Sopenharmony_ci#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */
30028c2ecf20Sopenharmony_ci		}
30038c2ecf20Sopenharmony_ci		ret = AP_RX_EXIT;
30048c2ecf20Sopenharmony_ci		goto out;
30058c2ecf20Sopenharmony_ci	} else if (stype == IEEE80211_STYPE_NULLFUNC) {
30068c2ecf20Sopenharmony_ci		/* At least Lucent cards seem to send periodic nullfunc
30078c2ecf20Sopenharmony_ci		 * frames with ToDS. Let these through to update SQ
30088c2ecf20Sopenharmony_ci		 * stats and PS state. Nullfunc frames do not contain
30098c2ecf20Sopenharmony_ci		 * any data and they will be dropped below. */
30108c2ecf20Sopenharmony_ci	} else {
30118c2ecf20Sopenharmony_ci		/* If BSSID (Addr3) is foreign, this frame is a normal
30128c2ecf20Sopenharmony_ci		 * broadcast frame from an IBSS network. Drop it silently.
30138c2ecf20Sopenharmony_ci		 * If BSSID is own, report the dropping of this frame. */
30148c2ecf20Sopenharmony_ci		if (ether_addr_equal(hdr->addr3, dev->dev_addr)) {
30158c2ecf20Sopenharmony_ci			printk(KERN_DEBUG "%s: dropped received packet from %pM"
30168c2ecf20Sopenharmony_ci			       " with no ToDS flag "
30178c2ecf20Sopenharmony_ci			       "(type=0x%02x, subtype=0x%02x)\n", dev->name,
30188c2ecf20Sopenharmony_ci			       hdr->addr2, type >> 2, stype >> 4);
30198c2ecf20Sopenharmony_ci			hostap_dump_rx_80211(dev->name, skb, rx_stats);
30208c2ecf20Sopenharmony_ci		}
30218c2ecf20Sopenharmony_ci		ret = AP_RX_DROP;
30228c2ecf20Sopenharmony_ci		goto out;
30238c2ecf20Sopenharmony_ci	}
30248c2ecf20Sopenharmony_ci
30258c2ecf20Sopenharmony_ci	if (sta) {
30268c2ecf20Sopenharmony_ci		hostap_update_sta_ps2(local, sta, fc & IEEE80211_FCTL_PM,
30278c2ecf20Sopenharmony_ci				      type, stype);
30288c2ecf20Sopenharmony_ci
30298c2ecf20Sopenharmony_ci		sta->rx_packets++;
30308c2ecf20Sopenharmony_ci		sta->rx_bytes += skb->len;
30318c2ecf20Sopenharmony_ci		sta->last_rx = jiffies;
30328c2ecf20Sopenharmony_ci	}
30338c2ecf20Sopenharmony_ci
30348c2ecf20Sopenharmony_ci	if (local->ap->nullfunc_ack && stype == IEEE80211_STYPE_NULLFUNC &&
30358c2ecf20Sopenharmony_ci	    fc & IEEE80211_FCTL_TODS) {
30368c2ecf20Sopenharmony_ci		if (local->hostapd) {
30378c2ecf20Sopenharmony_ci			prism2_rx_80211(local->apdev, skb, rx_stats,
30388c2ecf20Sopenharmony_ci					PRISM2_RX_NULLFUNC_ACK);
30398c2ecf20Sopenharmony_ci#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT
30408c2ecf20Sopenharmony_ci		} else {
30418c2ecf20Sopenharmony_ci			/* some STA f/w's seem to require control::ACK frame
30428c2ecf20Sopenharmony_ci			 * for data::nullfunc, but Prism2 f/w 0.8.0 (at least
30438c2ecf20Sopenharmony_ci			 * from Compaq) does not send this.. Try to generate
30448c2ecf20Sopenharmony_ci			 * ACK for these frames from the host driver to make
30458c2ecf20Sopenharmony_ci			 * power saving work with, e.g., Lucent WaveLAN f/w */
30468c2ecf20Sopenharmony_ci			hostap_rx(dev, skb, rx_stats);
30478c2ecf20Sopenharmony_ci#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */
30488c2ecf20Sopenharmony_ci		}
30498c2ecf20Sopenharmony_ci		ret = AP_RX_EXIT;
30508c2ecf20Sopenharmony_ci		goto out;
30518c2ecf20Sopenharmony_ci	}
30528c2ecf20Sopenharmony_ci
30538c2ecf20Sopenharmony_ci out:
30548c2ecf20Sopenharmony_ci	if (sta)
30558c2ecf20Sopenharmony_ci		atomic_dec(&sta->users);
30568c2ecf20Sopenharmony_ci
30578c2ecf20Sopenharmony_ci	return ret;
30588c2ecf20Sopenharmony_ci}
30598c2ecf20Sopenharmony_ci
30608c2ecf20Sopenharmony_ci
30618c2ecf20Sopenharmony_ci/* Called only as a tasklet (software IRQ) */
30628c2ecf20Sopenharmony_ciint hostap_handle_sta_crypto(local_info_t *local,
30638c2ecf20Sopenharmony_ci			     struct ieee80211_hdr *hdr,
30648c2ecf20Sopenharmony_ci			     struct lib80211_crypt_data **crypt,
30658c2ecf20Sopenharmony_ci			     void **sta_ptr)
30668c2ecf20Sopenharmony_ci{
30678c2ecf20Sopenharmony_ci	struct sta_info *sta;
30688c2ecf20Sopenharmony_ci
30698c2ecf20Sopenharmony_ci	spin_lock(&local->ap->sta_table_lock);
30708c2ecf20Sopenharmony_ci	sta = ap_get_sta(local->ap, hdr->addr2);
30718c2ecf20Sopenharmony_ci	if (sta)
30728c2ecf20Sopenharmony_ci		atomic_inc(&sta->users);
30738c2ecf20Sopenharmony_ci	spin_unlock(&local->ap->sta_table_lock);
30748c2ecf20Sopenharmony_ci
30758c2ecf20Sopenharmony_ci	if (!sta)
30768c2ecf20Sopenharmony_ci		return -1;
30778c2ecf20Sopenharmony_ci
30788c2ecf20Sopenharmony_ci	if (sta->crypt) {
30798c2ecf20Sopenharmony_ci		*crypt = sta->crypt;
30808c2ecf20Sopenharmony_ci		*sta_ptr = sta;
30818c2ecf20Sopenharmony_ci		/* hostap_handle_sta_release() will be called to release STA
30828c2ecf20Sopenharmony_ci		 * info */
30838c2ecf20Sopenharmony_ci	} else
30848c2ecf20Sopenharmony_ci		atomic_dec(&sta->users);
30858c2ecf20Sopenharmony_ci
30868c2ecf20Sopenharmony_ci	return 0;
30878c2ecf20Sopenharmony_ci}
30888c2ecf20Sopenharmony_ci
30898c2ecf20Sopenharmony_ci
30908c2ecf20Sopenharmony_ci/* Called only as a tasklet (software IRQ) */
30918c2ecf20Sopenharmony_ciint hostap_is_sta_assoc(struct ap_data *ap, u8 *sta_addr)
30928c2ecf20Sopenharmony_ci{
30938c2ecf20Sopenharmony_ci	struct sta_info *sta;
30948c2ecf20Sopenharmony_ci	int ret = 0;
30958c2ecf20Sopenharmony_ci
30968c2ecf20Sopenharmony_ci	spin_lock(&ap->sta_table_lock);
30978c2ecf20Sopenharmony_ci	sta = ap_get_sta(ap, sta_addr);
30988c2ecf20Sopenharmony_ci	if (sta != NULL && (sta->flags & WLAN_STA_ASSOC) && !sta->ap)
30998c2ecf20Sopenharmony_ci		ret = 1;
31008c2ecf20Sopenharmony_ci	spin_unlock(&ap->sta_table_lock);
31018c2ecf20Sopenharmony_ci
31028c2ecf20Sopenharmony_ci	return ret;
31038c2ecf20Sopenharmony_ci}
31048c2ecf20Sopenharmony_ci
31058c2ecf20Sopenharmony_ci
31068c2ecf20Sopenharmony_ci/* Called only as a tasklet (software IRQ) */
31078c2ecf20Sopenharmony_ciint hostap_is_sta_authorized(struct ap_data *ap, u8 *sta_addr)
31088c2ecf20Sopenharmony_ci{
31098c2ecf20Sopenharmony_ci	struct sta_info *sta;
31108c2ecf20Sopenharmony_ci	int ret = 0;
31118c2ecf20Sopenharmony_ci
31128c2ecf20Sopenharmony_ci	spin_lock(&ap->sta_table_lock);
31138c2ecf20Sopenharmony_ci	sta = ap_get_sta(ap, sta_addr);
31148c2ecf20Sopenharmony_ci	if (sta != NULL && (sta->flags & WLAN_STA_ASSOC) && !sta->ap &&
31158c2ecf20Sopenharmony_ci	    ((sta->flags & WLAN_STA_AUTHORIZED) ||
31168c2ecf20Sopenharmony_ci	     ap->local->ieee_802_1x == 0))
31178c2ecf20Sopenharmony_ci		ret = 1;
31188c2ecf20Sopenharmony_ci	spin_unlock(&ap->sta_table_lock);
31198c2ecf20Sopenharmony_ci
31208c2ecf20Sopenharmony_ci	return ret;
31218c2ecf20Sopenharmony_ci}
31228c2ecf20Sopenharmony_ci
31238c2ecf20Sopenharmony_ci
31248c2ecf20Sopenharmony_ci/* Called only as a tasklet (software IRQ) */
31258c2ecf20Sopenharmony_ciint hostap_add_sta(struct ap_data *ap, u8 *sta_addr)
31268c2ecf20Sopenharmony_ci{
31278c2ecf20Sopenharmony_ci	struct sta_info *sta;
31288c2ecf20Sopenharmony_ci	int ret = 1;
31298c2ecf20Sopenharmony_ci
31308c2ecf20Sopenharmony_ci	if (!ap)
31318c2ecf20Sopenharmony_ci		return -1;
31328c2ecf20Sopenharmony_ci
31338c2ecf20Sopenharmony_ci	spin_lock(&ap->sta_table_lock);
31348c2ecf20Sopenharmony_ci	sta = ap_get_sta(ap, sta_addr);
31358c2ecf20Sopenharmony_ci	if (sta)
31368c2ecf20Sopenharmony_ci		ret = 0;
31378c2ecf20Sopenharmony_ci	spin_unlock(&ap->sta_table_lock);
31388c2ecf20Sopenharmony_ci
31398c2ecf20Sopenharmony_ci	if (ret == 1) {
31408c2ecf20Sopenharmony_ci		sta = ap_add_sta(ap, sta_addr);
31418c2ecf20Sopenharmony_ci		if (!sta)
31428c2ecf20Sopenharmony_ci			return -1;
31438c2ecf20Sopenharmony_ci		sta->flags = WLAN_STA_AUTH | WLAN_STA_ASSOC;
31448c2ecf20Sopenharmony_ci		sta->ap = 1;
31458c2ecf20Sopenharmony_ci		memset(sta->supported_rates, 0, sizeof(sta->supported_rates));
31468c2ecf20Sopenharmony_ci		/* No way of knowing which rates are supported since we did not
31478c2ecf20Sopenharmony_ci		 * get supported rates element from beacon/assoc req. Assume
31488c2ecf20Sopenharmony_ci		 * that remote end supports all 802.11b rates. */
31498c2ecf20Sopenharmony_ci		sta->supported_rates[0] = 0x82;
31508c2ecf20Sopenharmony_ci		sta->supported_rates[1] = 0x84;
31518c2ecf20Sopenharmony_ci		sta->supported_rates[2] = 0x0b;
31528c2ecf20Sopenharmony_ci		sta->supported_rates[3] = 0x16;
31538c2ecf20Sopenharmony_ci		sta->tx_supp_rates = WLAN_RATE_1M | WLAN_RATE_2M |
31548c2ecf20Sopenharmony_ci			WLAN_RATE_5M5 | WLAN_RATE_11M;
31558c2ecf20Sopenharmony_ci		sta->tx_rate = 110;
31568c2ecf20Sopenharmony_ci		sta->tx_max_rate = sta->tx_rate_idx = 3;
31578c2ecf20Sopenharmony_ci	}
31588c2ecf20Sopenharmony_ci
31598c2ecf20Sopenharmony_ci	return ret;
31608c2ecf20Sopenharmony_ci}
31618c2ecf20Sopenharmony_ci
31628c2ecf20Sopenharmony_ci
31638c2ecf20Sopenharmony_ci/* Called only as a tasklet (software IRQ) */
31648c2ecf20Sopenharmony_ciint hostap_update_rx_stats(struct ap_data *ap,
31658c2ecf20Sopenharmony_ci			   struct ieee80211_hdr *hdr,
31668c2ecf20Sopenharmony_ci			   struct hostap_80211_rx_status *rx_stats)
31678c2ecf20Sopenharmony_ci{
31688c2ecf20Sopenharmony_ci	struct sta_info *sta;
31698c2ecf20Sopenharmony_ci
31708c2ecf20Sopenharmony_ci	if (!ap)
31718c2ecf20Sopenharmony_ci		return -1;
31728c2ecf20Sopenharmony_ci
31738c2ecf20Sopenharmony_ci	spin_lock(&ap->sta_table_lock);
31748c2ecf20Sopenharmony_ci	sta = ap_get_sta(ap, hdr->addr2);
31758c2ecf20Sopenharmony_ci	if (sta) {
31768c2ecf20Sopenharmony_ci		sta->last_rx_silence = rx_stats->noise;
31778c2ecf20Sopenharmony_ci		sta->last_rx_signal = rx_stats->signal;
31788c2ecf20Sopenharmony_ci		sta->last_rx_rate = rx_stats->rate;
31798c2ecf20Sopenharmony_ci		sta->last_rx_updated = IW_QUAL_ALL_UPDATED | IW_QUAL_DBM;
31808c2ecf20Sopenharmony_ci		if (rx_stats->rate == 10)
31818c2ecf20Sopenharmony_ci			sta->rx_count[0]++;
31828c2ecf20Sopenharmony_ci		else if (rx_stats->rate == 20)
31838c2ecf20Sopenharmony_ci			sta->rx_count[1]++;
31848c2ecf20Sopenharmony_ci		else if (rx_stats->rate == 55)
31858c2ecf20Sopenharmony_ci			sta->rx_count[2]++;
31868c2ecf20Sopenharmony_ci		else if (rx_stats->rate == 110)
31878c2ecf20Sopenharmony_ci			sta->rx_count[3]++;
31888c2ecf20Sopenharmony_ci	}
31898c2ecf20Sopenharmony_ci	spin_unlock(&ap->sta_table_lock);
31908c2ecf20Sopenharmony_ci
31918c2ecf20Sopenharmony_ci	return sta ? 0 : -1;
31928c2ecf20Sopenharmony_ci}
31938c2ecf20Sopenharmony_ci
31948c2ecf20Sopenharmony_ci
31958c2ecf20Sopenharmony_civoid hostap_update_rates(local_info_t *local)
31968c2ecf20Sopenharmony_ci{
31978c2ecf20Sopenharmony_ci	struct sta_info *sta;
31988c2ecf20Sopenharmony_ci	struct ap_data *ap = local->ap;
31998c2ecf20Sopenharmony_ci
32008c2ecf20Sopenharmony_ci	if (!ap)
32018c2ecf20Sopenharmony_ci		return;
32028c2ecf20Sopenharmony_ci
32038c2ecf20Sopenharmony_ci	spin_lock_bh(&ap->sta_table_lock);
32048c2ecf20Sopenharmony_ci	list_for_each_entry(sta, &ap->sta_list, list) {
32058c2ecf20Sopenharmony_ci		prism2_check_tx_rates(sta);
32068c2ecf20Sopenharmony_ci	}
32078c2ecf20Sopenharmony_ci	spin_unlock_bh(&ap->sta_table_lock);
32088c2ecf20Sopenharmony_ci}
32098c2ecf20Sopenharmony_ci
32108c2ecf20Sopenharmony_ci
32118c2ecf20Sopenharmony_civoid * ap_crypt_get_ptrs(struct ap_data *ap, u8 *addr, int permanent,
32128c2ecf20Sopenharmony_ci			 struct lib80211_crypt_data ***crypt)
32138c2ecf20Sopenharmony_ci{
32148c2ecf20Sopenharmony_ci	struct sta_info *sta;
32158c2ecf20Sopenharmony_ci
32168c2ecf20Sopenharmony_ci	spin_lock_bh(&ap->sta_table_lock);
32178c2ecf20Sopenharmony_ci	sta = ap_get_sta(ap, addr);
32188c2ecf20Sopenharmony_ci	if (sta)
32198c2ecf20Sopenharmony_ci		atomic_inc(&sta->users);
32208c2ecf20Sopenharmony_ci	spin_unlock_bh(&ap->sta_table_lock);
32218c2ecf20Sopenharmony_ci
32228c2ecf20Sopenharmony_ci	if (!sta && permanent)
32238c2ecf20Sopenharmony_ci		sta = ap_add_sta(ap, addr);
32248c2ecf20Sopenharmony_ci
32258c2ecf20Sopenharmony_ci	if (!sta)
32268c2ecf20Sopenharmony_ci		return NULL;
32278c2ecf20Sopenharmony_ci
32288c2ecf20Sopenharmony_ci	if (permanent)
32298c2ecf20Sopenharmony_ci		sta->flags |= WLAN_STA_PERM;
32308c2ecf20Sopenharmony_ci
32318c2ecf20Sopenharmony_ci	*crypt = &sta->crypt;
32328c2ecf20Sopenharmony_ci
32338c2ecf20Sopenharmony_ci	return sta;
32348c2ecf20Sopenharmony_ci}
32358c2ecf20Sopenharmony_ci
32368c2ecf20Sopenharmony_ci
32378c2ecf20Sopenharmony_civoid hostap_add_wds_links(local_info_t *local)
32388c2ecf20Sopenharmony_ci{
32398c2ecf20Sopenharmony_ci	struct ap_data *ap = local->ap;
32408c2ecf20Sopenharmony_ci	struct sta_info *sta;
32418c2ecf20Sopenharmony_ci
32428c2ecf20Sopenharmony_ci	spin_lock_bh(&ap->sta_table_lock);
32438c2ecf20Sopenharmony_ci	list_for_each_entry(sta, &ap->sta_list, list) {
32448c2ecf20Sopenharmony_ci		if (sta->ap)
32458c2ecf20Sopenharmony_ci			hostap_wds_link_oper(local, sta->addr, WDS_ADD);
32468c2ecf20Sopenharmony_ci	}
32478c2ecf20Sopenharmony_ci	spin_unlock_bh(&ap->sta_table_lock);
32488c2ecf20Sopenharmony_ci
32498c2ecf20Sopenharmony_ci	schedule_work(&local->ap->wds_oper_queue);
32508c2ecf20Sopenharmony_ci}
32518c2ecf20Sopenharmony_ci
32528c2ecf20Sopenharmony_ci
32538c2ecf20Sopenharmony_civoid hostap_wds_link_oper(local_info_t *local, u8 *addr, wds_oper_type type)
32548c2ecf20Sopenharmony_ci{
32558c2ecf20Sopenharmony_ci	struct wds_oper_data *entry;
32568c2ecf20Sopenharmony_ci
32578c2ecf20Sopenharmony_ci	entry = kmalloc(sizeof(*entry), GFP_ATOMIC);
32588c2ecf20Sopenharmony_ci	if (!entry)
32598c2ecf20Sopenharmony_ci		return;
32608c2ecf20Sopenharmony_ci	memcpy(entry->addr, addr, ETH_ALEN);
32618c2ecf20Sopenharmony_ci	entry->type = type;
32628c2ecf20Sopenharmony_ci	spin_lock_bh(&local->lock);
32638c2ecf20Sopenharmony_ci	entry->next = local->ap->wds_oper_entries;
32648c2ecf20Sopenharmony_ci	local->ap->wds_oper_entries = entry;
32658c2ecf20Sopenharmony_ci	spin_unlock_bh(&local->lock);
32668c2ecf20Sopenharmony_ci
32678c2ecf20Sopenharmony_ci	schedule_work(&local->ap->wds_oper_queue);
32688c2ecf20Sopenharmony_ci}
32698c2ecf20Sopenharmony_ci
32708c2ecf20Sopenharmony_ci
32718c2ecf20Sopenharmony_ciEXPORT_SYMBOL(hostap_init_data);
32728c2ecf20Sopenharmony_ciEXPORT_SYMBOL(hostap_init_ap_proc);
32738c2ecf20Sopenharmony_ciEXPORT_SYMBOL(hostap_free_data);
32748c2ecf20Sopenharmony_ciEXPORT_SYMBOL(hostap_check_sta_fw_version);
32758c2ecf20Sopenharmony_ciEXPORT_SYMBOL(hostap_handle_sta_tx_exc);
32768c2ecf20Sopenharmony_ci#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT
32778c2ecf20Sopenharmony_ci#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */
3278