1ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen/*
2ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen * Intersil Prism2 driver with Host AP (software access point) support
3ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen * Copyright (c) 2001-2002, SSH Communications Security Corp and Jouni Malinen
485d32e7b0ea53a11d2a4018d8ad2605052778df7Jouni Malinen * <j@w1.fi>
585d32e7b0ea53a11d2a4018d8ad2605052778df7Jouni Malinen * Copyright (c) 2002-2005, Jouni Malinen <j@w1.fi>
6ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen *
7ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen * This file is to be included into hostap.c when S/W AP functionality is
8ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen * compiled.
9ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen *
10ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen * AP:  FIX:
11ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen * - if unicast Class 2 (assoc,reassoc,disassoc) frame received from
12ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen *   unauthenticated STA, send deauth. frame (8802.11: 5.5)
13ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen * - if unicast Class 3 (data with to/from DS,deauth,pspoll) frame received
14ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen *   from authenticated, but unassoc STA, send disassoc frame (8802.11: 5.5)
15ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen * - if unicast Class 3 received from unauthenticated STA, send deauth. frame
16ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen *   (8802.11: 5.5)
17ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen */
18ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
195fad5a2e1f34b333a801b749c4e143c2ac3e8a4fAdrian Bunk#include <linux/proc_fs.h>
206bbefe86796c07fb8a6d28114f1e3f770586ba05David Howells#include <linux/seq_file.h>
215fad5a2e1f34b333a801b749c4e143c2ac3e8a4fAdrian Bunk#include <linux/delay.h>
225fad5a2e1f34b333a801b749c4e143c2ac3e8a4fAdrian Bunk#include <linux/random.h>
231ea893fde29d8cf1639da8989f4b843dc3283ca8Dan Williams#include <linux/if_arp.h>
245a0e3ad6af8660be21ca98a971cd00f331318c05Tejun Heo#include <linux/slab.h>
25ee40fa0656a730491765545ff7550f3c1ceb0fbcPaul Gortmaker#include <linux/export.h>
266eb07caf1ac5723720caea2ee93cd11b7058a0aaPaul Gortmaker#include <linux/moduleparam.h>
27d22fbd70c2770b5e3ba0d7bc5b29f6ee686cdba4dingtianhong#include <linux/etherdevice.h>
285fad5a2e1f34b333a801b749c4e143c2ac3e8a4fAdrian Bunk
295fad5a2e1f34b333a801b749c4e143c2ac3e8a4fAdrian Bunk#include "hostap_wlan.h"
305fad5a2e1f34b333a801b749c4e143c2ac3e8a4fAdrian Bunk#include "hostap.h"
315fad5a2e1f34b333a801b749c4e143c2ac3e8a4fAdrian Bunk#include "hostap_ap.h"
325fad5a2e1f34b333a801b749c4e143c2ac3e8a4fAdrian Bunk
33ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinenstatic int other_ap_policy[MAX_PARM_DEVICES] = { AP_OTHER_AP_SKIP_ALL,
34ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen						 DEF_INTS };
35ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinenmodule_param_array(other_ap_policy, int, NULL, 0444);
36ff1d2767d5a43c85f944e86a45284b721f66196cJouni MalinenMODULE_PARM_DESC(other_ap_policy, "Other AP beacon monitoring policy (0-3)");
37ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
38ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinenstatic int ap_max_inactivity[MAX_PARM_DEVICES] = { AP_MAX_INACTIVITY_SEC,
39ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen						   DEF_INTS };
40ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinenmodule_param_array(ap_max_inactivity, int, NULL, 0444);
41ff1d2767d5a43c85f944e86a45284b721f66196cJouni MalinenMODULE_PARM_DESC(ap_max_inactivity, "AP timeout (in seconds) for station "
42ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		 "inactivity");
43ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
44ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinenstatic int ap_bridge_packets[MAX_PARM_DEVICES] = { 1, DEF_INTS };
45ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinenmodule_param_array(ap_bridge_packets, int, NULL, 0444);
46ff1d2767d5a43c85f944e86a45284b721f66196cJouni MalinenMODULE_PARM_DESC(ap_bridge_packets, "Bridge packets directly between "
47ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		 "stations");
48ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
49ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinenstatic int autom_ap_wds[MAX_PARM_DEVICES] = { 0, DEF_INTS };
50ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinenmodule_param_array(autom_ap_wds, int, NULL, 0444);
51ff1d2767d5a43c85f944e86a45284b721f66196cJouni MalinenMODULE_PARM_DESC(autom_ap_wds, "Add WDS connections to other APs "
52ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		 "automatically");
53ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
54ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
55ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinenstatic struct sta_info* ap_get_sta(struct ap_data *ap, u8 *sta);
56ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinenstatic void hostap_event_expired_sta(struct net_device *dev,
57ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen				     struct sta_info *sta);
58c4028958b6ecad064b1a6303a6a5906d4fe48d73David Howellsstatic void handle_add_proc_queue(struct work_struct *work);
59ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
60ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT
61c4028958b6ecad064b1a6303a6a5906d4fe48d73David Howellsstatic void handle_wds_oper_queue(struct work_struct *work);
62ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinenstatic void prism2_send_mgmt(struct net_device *dev,
634339d328631aa815fe2181b9164b3690ca2db4daJouni Malinen			     u16 type_subtype, char *body,
64ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			     int body_len, u8 *addr, u16 tx_cb_idx);
65ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */
66ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
67ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
68ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen#ifndef PRISM2_NO_PROCFS_DEBUG
696bbefe86796c07fb8a6d28114f1e3f770586ba05David Howellsstatic int ap_debug_proc_show(struct seq_file *m, void *v)
70ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen{
716bbefe86796c07fb8a6d28114f1e3f770586ba05David Howells	struct ap_data *ap = m->private;
726bbefe86796c07fb8a6d28114f1e3f770586ba05David Howells
736bbefe86796c07fb8a6d28114f1e3f770586ba05David Howells	seq_printf(m, "BridgedUnicastFrames=%u\n", ap->bridged_unicast);
746bbefe86796c07fb8a6d28114f1e3f770586ba05David Howells	seq_printf(m, "BridgedMulticastFrames=%u\n", ap->bridged_multicast);
756bbefe86796c07fb8a6d28114f1e3f770586ba05David Howells	seq_printf(m, "max_inactivity=%u\n", ap->max_inactivity / HZ);
766bbefe86796c07fb8a6d28114f1e3f770586ba05David Howells	seq_printf(m, "bridge_packets=%u\n", ap->bridge_packets);
776bbefe86796c07fb8a6d28114f1e3f770586ba05David Howells	seq_printf(m, "nullfunc_ack=%u\n", ap->nullfunc_ack);
786bbefe86796c07fb8a6d28114f1e3f770586ba05David Howells	seq_printf(m, "autom_ap_wds=%u\n", ap->autom_ap_wds);
796bbefe86796c07fb8a6d28114f1e3f770586ba05David Howells	seq_printf(m, "auth_algs=%u\n", ap->local->auth_algs);
806bbefe86796c07fb8a6d28114f1e3f770586ba05David Howells	seq_printf(m, "tx_drop_nonassoc=%u\n", ap->tx_drop_nonassoc);
816bbefe86796c07fb8a6d28114f1e3f770586ba05David Howells	return 0;
826bbefe86796c07fb8a6d28114f1e3f770586ba05David Howells}
83ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
846bbefe86796c07fb8a6d28114f1e3f770586ba05David Howellsstatic int ap_debug_proc_open(struct inode *inode, struct file *file)
856bbefe86796c07fb8a6d28114f1e3f770586ba05David Howells{
866bbefe86796c07fb8a6d28114f1e3f770586ba05David Howells	return single_open(file, ap_debug_proc_show, PDE_DATA(inode));
87ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen}
886bbefe86796c07fb8a6d28114f1e3f770586ba05David Howells
896bbefe86796c07fb8a6d28114f1e3f770586ba05David Howellsstatic const struct file_operations ap_debug_proc_fops = {
906bbefe86796c07fb8a6d28114f1e3f770586ba05David Howells	.open		= ap_debug_proc_open,
916bbefe86796c07fb8a6d28114f1e3f770586ba05David Howells	.read		= seq_read,
926bbefe86796c07fb8a6d28114f1e3f770586ba05David Howells	.llseek		= seq_lseek,
93bc3041f055c228ec8347580d95cb2b344b503dcbAl Viro	.release	= single_release,
946bbefe86796c07fb8a6d28114f1e3f770586ba05David Howells};
95ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen#endif /* PRISM2_NO_PROCFS_DEBUG */
96ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
97ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
98ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinenstatic void ap_sta_hash_add(struct ap_data *ap, struct sta_info *sta)
99ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen{
100ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	sta->hnext = ap->sta_hash[STA_HASH(sta->addr)];
101ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	ap->sta_hash[STA_HASH(sta->addr)] = sta;
102ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen}
103ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
104ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinenstatic void ap_sta_hash_del(struct ap_data *ap, struct sta_info *sta)
105ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen{
106ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	struct sta_info *s;
107ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
108ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	s = ap->sta_hash[STA_HASH(sta->addr)];
109ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	if (s == NULL) return;
110d22fbd70c2770b5e3ba0d7bc5b29f6ee686cdba4dingtianhong	if (ether_addr_equal(s->addr, sta->addr)) {
111ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		ap->sta_hash[STA_HASH(sta->addr)] = s->hnext;
112ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		return;
113ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	}
114ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
115d22fbd70c2770b5e3ba0d7bc5b29f6ee686cdba4dingtianhong	while (s->hnext != NULL && !ether_addr_equal(s->hnext->addr, sta->addr))
116ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		s = s->hnext;
117ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	if (s->hnext != NULL)
118ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		s->hnext = s->hnext->hnext;
119ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	else
120e174961ca1a0b28f7abf0be47973ad57cb74e5f0Johannes Berg		printk("AP: could not remove STA %pM from hash table\n",
121e174961ca1a0b28f7abf0be47973ad57cb74e5f0Johannes Berg		       sta->addr);
122ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen}
123ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
124ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinenstatic void ap_free_sta(struct ap_data *ap, struct sta_info *sta)
125ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen{
126ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	if (sta->ap && sta->local)
127ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		hostap_event_expired_sta(sta->local->dev, sta);
128ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
129ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	if (ap->proc != NULL) {
130ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		char name[20];
131e174961ca1a0b28f7abf0be47973ad57cb74e5f0Johannes Berg		sprintf(name, "%pM", sta->addr);
132ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		remove_proc_entry(name, ap->proc);
133ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	}
134ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
135ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	if (sta->crypt) {
136ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		sta->crypt->ops->deinit(sta->crypt->priv);
137ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		kfree(sta->crypt);
138ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		sta->crypt = NULL;
139ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	}
140ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
141ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	skb_queue_purge(&sta->tx_buf);
142ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
143ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	ap->num_sta--;
144ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT
145ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	if (sta->aid > 0)
146ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		ap->sta_aid[sta->aid - 1] = NULL;
147ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
148ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	if (!sta->ap && sta->u.sta.challenge)
149ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		kfree(sta->u.sta.challenge);
15072471c0d3197d02da53897868692f0b91042f8a5Kirill Tkhai	del_timer_sync(&sta->timer);
151ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */
152ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
153ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	kfree(sta);
154ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen}
155ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
156ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
157ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinenstatic void hostap_set_tim(local_info_t *local, int aid, int set)
158ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen{
159ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	if (local->func->set_tim)
160ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		local->func->set_tim(local->dev, aid, set);
161ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen}
162ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
163ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
164ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinenstatic void hostap_event_new_sta(struct net_device *dev, struct sta_info *sta)
165ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen{
166ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	union iwreq_data wrqu;
167ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	memset(&wrqu, 0, sizeof(wrqu));
168ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	memcpy(wrqu.addr.sa_data, sta->addr, ETH_ALEN);
169ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	wrqu.addr.sa_family = ARPHRD_ETHER;
170ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	wireless_send_event(dev, IWEVREGISTERED, &wrqu, NULL);
171ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen}
172ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
173ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
174ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinenstatic void hostap_event_expired_sta(struct net_device *dev,
175ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen				     struct sta_info *sta)
176ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen{
177ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	union iwreq_data wrqu;
178ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	memset(&wrqu, 0, sizeof(wrqu));
179ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	memcpy(wrqu.addr.sa_data, sta->addr, ETH_ALEN);
180ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	wrqu.addr.sa_family = ARPHRD_ETHER;
181ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	wireless_send_event(dev, IWEVEXPIRED, &wrqu, NULL);
182ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen}
183ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
184ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
185ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT
186ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
187ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinenstatic void ap_handle_timer(unsigned long data)
188ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen{
189ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	struct sta_info *sta = (struct sta_info *) data;
190ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	local_info_t *local;
191ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	struct ap_data *ap;
192ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	unsigned long next_time = 0;
193ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	int was_assoc;
194ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
195ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	if (sta == NULL || sta->local == NULL || sta->local->ap == NULL) {
196ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		PDEBUG(DEBUG_AP, "ap_handle_timer() called with NULL data\n");
197ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		return;
198ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	}
199ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
200ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	local = sta->local;
201ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	ap = local->ap;
202ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	was_assoc = sta->flags & WLAN_STA_ASSOC;
203ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
204ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	if (atomic_read(&sta->users) != 0)
205ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		next_time = jiffies + HZ;
206ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	else if ((sta->flags & WLAN_STA_PERM) && !(sta->flags & WLAN_STA_AUTH))
207ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		next_time = jiffies + ap->max_inactivity;
208ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
209ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	if (time_before(jiffies, sta->last_rx + ap->max_inactivity)) {
210ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		/* station activity detected; reset timeout state */
211ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		sta->timeout_next = STA_NULLFUNC;
212ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		next_time = sta->last_rx + ap->max_inactivity;
213ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	} else if (sta->timeout_next == STA_DISASSOC &&
214ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		   !(sta->flags & WLAN_STA_PENDING_POLL)) {
215ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		/* STA ACKed data nullfunc frame poll */
216ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		sta->timeout_next = STA_NULLFUNC;
217ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		next_time = jiffies + ap->max_inactivity;
218ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	}
219ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
220ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	if (next_time) {
221ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		sta->timer.expires = next_time;
222ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		add_timer(&sta->timer);
223ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		return;
224ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	}
225ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
226ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	if (sta->ap)
227ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		sta->timeout_next = STA_DEAUTH;
228ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
229ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	if (sta->timeout_next == STA_DEAUTH && !(sta->flags & WLAN_STA_PERM)) {
230ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		spin_lock(&ap->sta_table_lock);
231ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		ap_sta_hash_del(ap, sta);
232ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		list_del(&sta->list);
233ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		spin_unlock(&ap->sta_table_lock);
234ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		sta->flags &= ~(WLAN_STA_AUTH | WLAN_STA_ASSOC);
235ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	} else if (sta->timeout_next == STA_DISASSOC)
236ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		sta->flags &= ~WLAN_STA_ASSOC;
237ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
238ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	if (was_assoc && !(sta->flags & WLAN_STA_ASSOC) && !sta->ap)
239ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		hostap_event_expired_sta(local->dev, sta);
240ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
241ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	if (sta->timeout_next == STA_DEAUTH && sta->aid > 0 &&
242ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	    !skb_queue_empty(&sta->tx_buf)) {
243ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		hostap_set_tim(local, sta->aid, 0);
244ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		sta->flags &= ~WLAN_STA_TIM;
245ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	}
246ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
247ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	if (sta->ap) {
248ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		if (ap->autom_ap_wds) {
249ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			PDEBUG(DEBUG_AP, "%s: removing automatic WDS "
250e174961ca1a0b28f7abf0be47973ad57cb74e5f0Johannes Berg			       "connection to AP %pM\n",
251e174961ca1a0b28f7abf0be47973ad57cb74e5f0Johannes Berg			       local->dev->name, sta->addr);
252ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			hostap_wds_link_oper(local, sta->addr, WDS_DEL);
253ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		}
254ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	} else if (sta->timeout_next == STA_NULLFUNC) {
255ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		/* send data frame to poll STA and check whether this frame
256ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		 * is ACKed */
2574339d328631aa815fe2181b9164b3690ca2db4daJouni Malinen		/* FIX: IEEE80211_STYPE_NULLFUNC would be more appropriate, but
258ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		 * it is apparently not retried so TX Exc events are not
259ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		 * received for it */
260ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		sta->flags |= WLAN_STA_PENDING_POLL;
2614339d328631aa815fe2181b9164b3690ca2db4daJouni Malinen		prism2_send_mgmt(local->dev, IEEE80211_FTYPE_DATA |
2624339d328631aa815fe2181b9164b3690ca2db4daJouni Malinen				 IEEE80211_STYPE_DATA, NULL, 0,
263ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen				 sta->addr, ap->tx_callback_poll);
264ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	} else {
265ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		int deauth = sta->timeout_next == STA_DEAUTH;
2668a9faf3cd08b91aca1502dbe18e3b5063fda2e87Al Viro		__le16 resp;
267e174961ca1a0b28f7abf0be47973ad57cb74e5f0Johannes Berg		PDEBUG(DEBUG_AP, "%s: sending %s info to STA %pM"
268ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		       "(last=%lu, jiffies=%lu)\n",
269ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		       local->dev->name,
270ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		       deauth ? "deauthentication" : "disassociation",
271e174961ca1a0b28f7abf0be47973ad57cb74e5f0Johannes Berg		       sta->addr, sta->last_rx, jiffies);
272ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
273ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		resp = cpu_to_le16(deauth ? WLAN_REASON_PREV_AUTH_NOT_VALID :
274ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen				   WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY);
2754339d328631aa815fe2181b9164b3690ca2db4daJouni Malinen		prism2_send_mgmt(local->dev, IEEE80211_FTYPE_MGMT |
2764339d328631aa815fe2181b9164b3690ca2db4daJouni Malinen				 (deauth ? IEEE80211_STYPE_DEAUTH :
2774339d328631aa815fe2181b9164b3690ca2db4daJouni Malinen				  IEEE80211_STYPE_DISASSOC),
278ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen				 (char *) &resp, 2, sta->addr, 0);
279ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	}
280ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
281ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	if (sta->timeout_next == STA_DEAUTH) {
282ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		if (sta->flags & WLAN_STA_PERM) {
283e174961ca1a0b28f7abf0be47973ad57cb74e5f0Johannes Berg			PDEBUG(DEBUG_AP, "%s: STA %pM"
2840795af5729b18218767fab27c44b1384f72dc9adJoe Perches			       " would have been removed, "
2850795af5729b18218767fab27c44b1384f72dc9adJoe Perches			       "but it has 'perm' flag\n",
286e174961ca1a0b28f7abf0be47973ad57cb74e5f0Johannes Berg			       local->dev->name, sta->addr);
287ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		} else
288ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			ap_free_sta(ap, sta);
289ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		return;
290ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	}
291ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
292ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	if (sta->timeout_next == STA_NULLFUNC) {
293ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		sta->timeout_next = STA_DISASSOC;
294ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		sta->timer.expires = jiffies + AP_DISASSOC_DELAY;
295ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	} else {
296ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		sta->timeout_next = STA_DEAUTH;
297ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		sta->timer.expires = jiffies + AP_DEAUTH_DELAY;
298ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	}
299ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
300ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	add_timer(&sta->timer);
301ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen}
302ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
303ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
304ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinenvoid hostap_deauth_all_stas(struct net_device *dev, struct ap_data *ap,
305ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			    int resend)
306ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen{
307ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	u8 addr[ETH_ALEN];
3088a9faf3cd08b91aca1502dbe18e3b5063fda2e87Al Viro	__le16 resp;
309ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	int i;
310ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
311ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	PDEBUG(DEBUG_AP, "%s: Deauthenticate all stations\n", dev->name);
312ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	memset(addr, 0xff, ETH_ALEN);
313ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
3148a9faf3cd08b91aca1502dbe18e3b5063fda2e87Al Viro	resp = cpu_to_le16(WLAN_REASON_PREV_AUTH_NOT_VALID);
315ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
316ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	/* deauth message sent; try to resend it few times; the message is
317ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	 * broadcast, so it may be delayed until next DTIM; there is not much
318ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	 * else we can do at this point since the driver is going to be shut
319ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	 * down */
320ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	for (i = 0; i < 5; i++) {
3214339d328631aa815fe2181b9164b3690ca2db4daJouni Malinen		prism2_send_mgmt(dev, IEEE80211_FTYPE_MGMT |
3224339d328631aa815fe2181b9164b3690ca2db4daJouni Malinen				 IEEE80211_STYPE_DEAUTH,
323ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen				 (char *) &resp, 2, addr, 0);
324ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
325ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		if (!resend || ap->num_sta <= 0)
326ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			return;
327ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
328ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		mdelay(50);
329ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	}
330ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen}
331ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
332ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
3336bbefe86796c07fb8a6d28114f1e3f770586ba05David Howellsstatic int ap_control_proc_show(struct seq_file *m, void *v)
334ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen{
3356bbefe86796c07fb8a6d28114f1e3f770586ba05David Howells	struct ap_data *ap = m->private;
336ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	char *policy_txt;
337ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	struct mac_entry *entry;
338ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
3396bbefe86796c07fb8a6d28114f1e3f770586ba05David Howells	if (v == SEQ_START_TOKEN) {
3406bbefe86796c07fb8a6d28114f1e3f770586ba05David Howells		switch (ap->mac_restrictions.policy) {
3416bbefe86796c07fb8a6d28114f1e3f770586ba05David Howells		case MAC_POLICY_OPEN:
3426bbefe86796c07fb8a6d28114f1e3f770586ba05David Howells			policy_txt = "open";
3436bbefe86796c07fb8a6d28114f1e3f770586ba05David Howells			break;
3446bbefe86796c07fb8a6d28114f1e3f770586ba05David Howells		case MAC_POLICY_ALLOW:
3456bbefe86796c07fb8a6d28114f1e3f770586ba05David Howells			policy_txt = "allow";
3466bbefe86796c07fb8a6d28114f1e3f770586ba05David Howells			break;
3476bbefe86796c07fb8a6d28114f1e3f770586ba05David Howells		case MAC_POLICY_DENY:
3486bbefe86796c07fb8a6d28114f1e3f770586ba05David Howells			policy_txt = "deny";
3496bbefe86796c07fb8a6d28114f1e3f770586ba05David Howells			break;
3506bbefe86796c07fb8a6d28114f1e3f770586ba05David Howells		default:
3516bbefe86796c07fb8a6d28114f1e3f770586ba05David Howells			policy_txt = "unknown";
3526bbefe86796c07fb8a6d28114f1e3f770586ba05David Howells			break;
3536bbefe86796c07fb8a6d28114f1e3f770586ba05David Howells		}
3546bbefe86796c07fb8a6d28114f1e3f770586ba05David Howells		seq_printf(m, "MAC policy: %s\n", policy_txt);
3556bbefe86796c07fb8a6d28114f1e3f770586ba05David Howells		seq_printf(m, "MAC entries: %u\n", ap->mac_restrictions.entries);
3566bbefe86796c07fb8a6d28114f1e3f770586ba05David Howells		seq_puts(m, "MAC list:\n");
357ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		return 0;
358ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	}
359ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
3606bbefe86796c07fb8a6d28114f1e3f770586ba05David Howells	entry = v;
3616bbefe86796c07fb8a6d28114f1e3f770586ba05David Howells	seq_printf(m, "%pM\n", entry->addr);
3626bbefe86796c07fb8a6d28114f1e3f770586ba05David Howells	return 0;
3636bbefe86796c07fb8a6d28114f1e3f770586ba05David Howells}
3646bbefe86796c07fb8a6d28114f1e3f770586ba05David Howells
3656bbefe86796c07fb8a6d28114f1e3f770586ba05David Howellsstatic void *ap_control_proc_start(struct seq_file *m, loff_t *_pos)
3666bbefe86796c07fb8a6d28114f1e3f770586ba05David Howells{
3676bbefe86796c07fb8a6d28114f1e3f770586ba05David Howells	struct ap_data *ap = m->private;
368ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	spin_lock_bh(&ap->mac_restrictions.lock);
3696bbefe86796c07fb8a6d28114f1e3f770586ba05David Howells	return seq_list_start_head(&ap->mac_restrictions.mac_list, *_pos);
3706bbefe86796c07fb8a6d28114f1e3f770586ba05David Howells}
371ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
3726bbefe86796c07fb8a6d28114f1e3f770586ba05David Howellsstatic void *ap_control_proc_next(struct seq_file *m, void *v, loff_t *_pos)
3736bbefe86796c07fb8a6d28114f1e3f770586ba05David Howells{
3746bbefe86796c07fb8a6d28114f1e3f770586ba05David Howells	struct ap_data *ap = m->private;
3756bbefe86796c07fb8a6d28114f1e3f770586ba05David Howells	return seq_list_next(v, &ap->mac_restrictions.mac_list, _pos);
3766bbefe86796c07fb8a6d28114f1e3f770586ba05David Howells}
3776bbefe86796c07fb8a6d28114f1e3f770586ba05David Howells
3786bbefe86796c07fb8a6d28114f1e3f770586ba05David Howellsstatic void ap_control_proc_stop(struct seq_file *m, void *v)
3796bbefe86796c07fb8a6d28114f1e3f770586ba05David Howells{
3806bbefe86796c07fb8a6d28114f1e3f770586ba05David Howells	struct ap_data *ap = m->private;
381ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	spin_unlock_bh(&ap->mac_restrictions.lock);
3826bbefe86796c07fb8a6d28114f1e3f770586ba05David Howells}
3836bbefe86796c07fb8a6d28114f1e3f770586ba05David Howells
3846bbefe86796c07fb8a6d28114f1e3f770586ba05David Howellsstatic const struct seq_operations ap_control_proc_seqops = {
3856bbefe86796c07fb8a6d28114f1e3f770586ba05David Howells	.start	= ap_control_proc_start,
3866bbefe86796c07fb8a6d28114f1e3f770586ba05David Howells	.next	= ap_control_proc_next,
3876bbefe86796c07fb8a6d28114f1e3f770586ba05David Howells	.stop	= ap_control_proc_stop,
3886bbefe86796c07fb8a6d28114f1e3f770586ba05David Howells	.show	= ap_control_proc_show,
3896bbefe86796c07fb8a6d28114f1e3f770586ba05David Howells};
390ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
3916bbefe86796c07fb8a6d28114f1e3f770586ba05David Howellsstatic int ap_control_proc_open(struct inode *inode, struct file *file)
3926bbefe86796c07fb8a6d28114f1e3f770586ba05David Howells{
3936bbefe86796c07fb8a6d28114f1e3f770586ba05David Howells	int ret = seq_open(file, &ap_control_proc_seqops);
3946bbefe86796c07fb8a6d28114f1e3f770586ba05David Howells	if (ret == 0) {
3956bbefe86796c07fb8a6d28114f1e3f770586ba05David Howells		struct seq_file *m = file->private_data;
3966bbefe86796c07fb8a6d28114f1e3f770586ba05David Howells		m->private = PDE_DATA(inode);
3976bbefe86796c07fb8a6d28114f1e3f770586ba05David Howells	}
3986bbefe86796c07fb8a6d28114f1e3f770586ba05David Howells	return ret;
399ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen}
400ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
4016bbefe86796c07fb8a6d28114f1e3f770586ba05David Howellsstatic const struct file_operations ap_control_proc_fops = {
4026bbefe86796c07fb8a6d28114f1e3f770586ba05David Howells	.open		= ap_control_proc_open,
4036bbefe86796c07fb8a6d28114f1e3f770586ba05David Howells	.read		= seq_read,
4046bbefe86796c07fb8a6d28114f1e3f770586ba05David Howells	.llseek		= seq_lseek,
4056bbefe86796c07fb8a6d28114f1e3f770586ba05David Howells	.release	= seq_release,
4066bbefe86796c07fb8a6d28114f1e3f770586ba05David Howells};
4076bbefe86796c07fb8a6d28114f1e3f770586ba05David Howells
408ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
4095fad5a2e1f34b333a801b749c4e143c2ac3e8a4fAdrian Bunkint ap_control_add_mac(struct mac_restrictions *mac_restrictions, u8 *mac)
410ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen{
411ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	struct mac_entry *entry;
412ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
413ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	entry = kmalloc(sizeof(struct mac_entry), GFP_KERNEL);
414ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	if (entry == NULL)
415b53cf458ea20dd7f5e32611366f63728e40c9021Kumar Amit Mehta		return -ENOMEM;
416ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
417ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	memcpy(entry->addr, mac, ETH_ALEN);
418ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
419ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	spin_lock_bh(&mac_restrictions->lock);
420ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	list_add_tail(&entry->list, &mac_restrictions->mac_list);
421ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	mac_restrictions->entries++;
422ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	spin_unlock_bh(&mac_restrictions->lock);
423ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
424ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	return 0;
425ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen}
426ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
427ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
4285fad5a2e1f34b333a801b749c4e143c2ac3e8a4fAdrian Bunkint ap_control_del_mac(struct mac_restrictions *mac_restrictions, u8 *mac)
429ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen{
430ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	struct list_head *ptr;
431ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	struct mac_entry *entry;
432ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
433ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	spin_lock_bh(&mac_restrictions->lock);
434ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	for (ptr = mac_restrictions->mac_list.next;
435ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	     ptr != &mac_restrictions->mac_list; ptr = ptr->next) {
436ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		entry = list_entry(ptr, struct mac_entry, list);
437ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
438d22fbd70c2770b5e3ba0d7bc5b29f6ee686cdba4dingtianhong		if (ether_addr_equal(entry->addr, mac)) {
439ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			list_del(ptr);
440ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			kfree(entry);
441ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			mac_restrictions->entries--;
442ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			spin_unlock_bh(&mac_restrictions->lock);
443ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			return 0;
444ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		}
445ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	}
446ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	spin_unlock_bh(&mac_restrictions->lock);
447ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	return -1;
448ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen}
449ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
450ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
451ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinenstatic int ap_control_mac_deny(struct mac_restrictions *mac_restrictions,
452ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			       u8 *mac)
453ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen{
454ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	struct mac_entry *entry;
455ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	int found = 0;
456ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
457ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	if (mac_restrictions->policy == MAC_POLICY_OPEN)
458ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		return 0;
459ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
460ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	spin_lock_bh(&mac_restrictions->lock);
461c15057313725942c6af8dcb60b4d4322101316d9Matthias Kaehlcke	list_for_each_entry(entry, &mac_restrictions->mac_list, list) {
462d22fbd70c2770b5e3ba0d7bc5b29f6ee686cdba4dingtianhong		if (ether_addr_equal(entry->addr, mac)) {
463ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			found = 1;
464ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			break;
465ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		}
466ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	}
467ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	spin_unlock_bh(&mac_restrictions->lock);
468ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
469ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	if (mac_restrictions->policy == MAC_POLICY_ALLOW)
470ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		return !found;
471ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	else
472ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		return found;
473ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen}
474ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
475ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
4765fad5a2e1f34b333a801b749c4e143c2ac3e8a4fAdrian Bunkvoid ap_control_flush_macs(struct mac_restrictions *mac_restrictions)
477ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen{
478ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	struct list_head *ptr, *n;
479ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	struct mac_entry *entry;
480ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
481ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	if (mac_restrictions->entries == 0)
482ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		return;
483ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
484ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	spin_lock_bh(&mac_restrictions->lock);
485ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	for (ptr = mac_restrictions->mac_list.next, n = ptr->next;
486ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	     ptr != &mac_restrictions->mac_list;
487ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	     ptr = n, n = ptr->next) {
488ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		entry = list_entry(ptr, struct mac_entry, list);
489ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		list_del(ptr);
490ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		kfree(entry);
491ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	}
492ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	mac_restrictions->entries = 0;
493ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	spin_unlock_bh(&mac_restrictions->lock);
494ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen}
495ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
496ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
4975fad5a2e1f34b333a801b749c4e143c2ac3e8a4fAdrian Bunkint ap_control_kick_mac(struct ap_data *ap, struct net_device *dev, u8 *mac)
498ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen{
499ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	struct sta_info *sta;
5008a9faf3cd08b91aca1502dbe18e3b5063fda2e87Al Viro	__le16 resp;
501ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
502ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	spin_lock_bh(&ap->sta_table_lock);
503ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	sta = ap_get_sta(ap, mac);
504ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	if (sta) {
505ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		ap_sta_hash_del(ap, sta);
506ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		list_del(&sta->list);
507ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	}
508ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	spin_unlock_bh(&ap->sta_table_lock);
509ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
510ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	if (!sta)
511ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		return -EINVAL;
512ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
513ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	resp = cpu_to_le16(WLAN_REASON_PREV_AUTH_NOT_VALID);
5144339d328631aa815fe2181b9164b3690ca2db4daJouni Malinen	prism2_send_mgmt(dev, IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_DEAUTH,
515ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			 (char *) &resp, 2, sta->addr, 0);
516ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
517ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	if ((sta->flags & WLAN_STA_ASSOC) && !sta->ap)
518ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		hostap_event_expired_sta(dev, sta);
519ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
520ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	ap_free_sta(ap, sta);
521ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
522ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	return 0;
523ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen}
524ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
525ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */
526ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
527ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
5285fad5a2e1f34b333a801b749c4e143c2ac3e8a4fAdrian Bunkvoid ap_control_kickall(struct ap_data *ap)
529ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen{
530ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	struct list_head *ptr, *n;
531ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	struct sta_info *sta;
53274fae82c8bd5dd78365abe25506a9ba388d4a889Jeff Garzik
533ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	spin_lock_bh(&ap->sta_table_lock);
534ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	for (ptr = ap->sta_list.next, n = ptr->next; ptr != &ap->sta_list;
535ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	     ptr = n, n = ptr->next) {
536ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		sta = list_entry(ptr, struct sta_info, list);
537ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		ap_sta_hash_del(ap, sta);
538ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		list_del(&sta->list);
539ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		if ((sta->flags & WLAN_STA_ASSOC) && !sta->ap && sta->local)
540ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			hostap_event_expired_sta(sta->local->dev, sta);
541ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		ap_free_sta(ap, sta);
542ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	}
543ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	spin_unlock_bh(&ap->sta_table_lock);
544ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen}
545ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
546ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
547ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT
548ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
5496bbefe86796c07fb8a6d28114f1e3f770586ba05David Howellsstatic int prism2_ap_proc_show(struct seq_file *m, void *v)
550ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen{
5516bbefe86796c07fb8a6d28114f1e3f770586ba05David Howells	struct sta_info *sta = v;
552ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	int i;
553ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
5546bbefe86796c07fb8a6d28114f1e3f770586ba05David Howells	if (v == SEQ_START_TOKEN) {
5556bbefe86796c07fb8a6d28114f1e3f770586ba05David Howells		seq_printf(m, "# BSSID CHAN SIGNAL NOISE RATE SSID FLAGS\n");
556ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		return 0;
557ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	}
558ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
5596bbefe86796c07fb8a6d28114f1e3f770586ba05David Howells	if (!sta->ap)
560ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		return 0;
5616bbefe86796c07fb8a6d28114f1e3f770586ba05David Howells
5626bbefe86796c07fb8a6d28114f1e3f770586ba05David Howells	seq_printf(m, "%pM %d %d %d %d '",
5636bbefe86796c07fb8a6d28114f1e3f770586ba05David Howells		   sta->addr,
5646bbefe86796c07fb8a6d28114f1e3f770586ba05David Howells		   sta->u.ap.channel, sta->last_rx_signal,
5656bbefe86796c07fb8a6d28114f1e3f770586ba05David Howells		   sta->last_rx_silence, sta->last_rx_rate);
5666bbefe86796c07fb8a6d28114f1e3f770586ba05David Howells
5676bbefe86796c07fb8a6d28114f1e3f770586ba05David Howells	for (i = 0; i < sta->u.ap.ssid_len; i++) {
5686bbefe86796c07fb8a6d28114f1e3f770586ba05David Howells		if (sta->u.ap.ssid[i] >= 32 && sta->u.ap.ssid[i] < 127)
5696bbefe86796c07fb8a6d28114f1e3f770586ba05David Howells			seq_putc(m, sta->u.ap.ssid[i]);
5706bbefe86796c07fb8a6d28114f1e3f770586ba05David Howells		else
5716bbefe86796c07fb8a6d28114f1e3f770586ba05David Howells			seq_printf(m, "<%02x>", sta->u.ap.ssid[i]);
572ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	}
573ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
5746bbefe86796c07fb8a6d28114f1e3f770586ba05David Howells	seq_putc(m, '\'');
5756bbefe86796c07fb8a6d28114f1e3f770586ba05David Howells	if (sta->capability & WLAN_CAPABILITY_ESS)
5766bbefe86796c07fb8a6d28114f1e3f770586ba05David Howells		seq_puts(m, " [ESS]");
5776bbefe86796c07fb8a6d28114f1e3f770586ba05David Howells	if (sta->capability & WLAN_CAPABILITY_IBSS)
5786bbefe86796c07fb8a6d28114f1e3f770586ba05David Howells		seq_puts(m, " [IBSS]");
5796bbefe86796c07fb8a6d28114f1e3f770586ba05David Howells	if (sta->capability & WLAN_CAPABILITY_PRIVACY)
5806bbefe86796c07fb8a6d28114f1e3f770586ba05David Howells		seq_puts(m, " [WEP]");
5816bbefe86796c07fb8a6d28114f1e3f770586ba05David Howells	seq_putc(m, '\n');
5826bbefe86796c07fb8a6d28114f1e3f770586ba05David Howells	return 0;
5836bbefe86796c07fb8a6d28114f1e3f770586ba05David Howells}
584ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
5856bbefe86796c07fb8a6d28114f1e3f770586ba05David Howellsstatic void *prism2_ap_proc_start(struct seq_file *m, loff_t *_pos)
5866bbefe86796c07fb8a6d28114f1e3f770586ba05David Howells{
5876bbefe86796c07fb8a6d28114f1e3f770586ba05David Howells	struct ap_data *ap = m->private;
5886bbefe86796c07fb8a6d28114f1e3f770586ba05David Howells	spin_lock_bh(&ap->sta_table_lock);
5896bbefe86796c07fb8a6d28114f1e3f770586ba05David Howells	return seq_list_start_head(&ap->sta_list, *_pos);
5906bbefe86796c07fb8a6d28114f1e3f770586ba05David Howells}
5916bbefe86796c07fb8a6d28114f1e3f770586ba05David Howells
5926bbefe86796c07fb8a6d28114f1e3f770586ba05David Howellsstatic void *prism2_ap_proc_next(struct seq_file *m, void *v, loff_t *_pos)
5936bbefe86796c07fb8a6d28114f1e3f770586ba05David Howells{
5946bbefe86796c07fb8a6d28114f1e3f770586ba05David Howells	struct ap_data *ap = m->private;
5956bbefe86796c07fb8a6d28114f1e3f770586ba05David Howells	return seq_list_next(v, &ap->sta_list, _pos);
596ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen}
5976bbefe86796c07fb8a6d28114f1e3f770586ba05David Howells
5986bbefe86796c07fb8a6d28114f1e3f770586ba05David Howellsstatic void prism2_ap_proc_stop(struct seq_file *m, void *v)
5996bbefe86796c07fb8a6d28114f1e3f770586ba05David Howells{
6006bbefe86796c07fb8a6d28114f1e3f770586ba05David Howells	struct ap_data *ap = m->private;
6016bbefe86796c07fb8a6d28114f1e3f770586ba05David Howells	spin_unlock_bh(&ap->sta_table_lock);
6026bbefe86796c07fb8a6d28114f1e3f770586ba05David Howells}
6036bbefe86796c07fb8a6d28114f1e3f770586ba05David Howells
6046bbefe86796c07fb8a6d28114f1e3f770586ba05David Howellsstatic const struct seq_operations prism2_ap_proc_seqops = {
6056bbefe86796c07fb8a6d28114f1e3f770586ba05David Howells	.start	= prism2_ap_proc_start,
6066bbefe86796c07fb8a6d28114f1e3f770586ba05David Howells	.next	= prism2_ap_proc_next,
6076bbefe86796c07fb8a6d28114f1e3f770586ba05David Howells	.stop	= prism2_ap_proc_stop,
6086bbefe86796c07fb8a6d28114f1e3f770586ba05David Howells	.show	= prism2_ap_proc_show,
6096bbefe86796c07fb8a6d28114f1e3f770586ba05David Howells};
6106bbefe86796c07fb8a6d28114f1e3f770586ba05David Howells
6116bbefe86796c07fb8a6d28114f1e3f770586ba05David Howellsstatic int prism2_ap_proc_open(struct inode *inode, struct file *file)
6126bbefe86796c07fb8a6d28114f1e3f770586ba05David Howells{
6136bbefe86796c07fb8a6d28114f1e3f770586ba05David Howells	int ret = seq_open(file, &prism2_ap_proc_seqops);
6146bbefe86796c07fb8a6d28114f1e3f770586ba05David Howells	if (ret == 0) {
6156bbefe86796c07fb8a6d28114f1e3f770586ba05David Howells		struct seq_file *m = file->private_data;
6166bbefe86796c07fb8a6d28114f1e3f770586ba05David Howells		m->private = PDE_DATA(inode);
6176bbefe86796c07fb8a6d28114f1e3f770586ba05David Howells	}
6186bbefe86796c07fb8a6d28114f1e3f770586ba05David Howells	return ret;
6196bbefe86796c07fb8a6d28114f1e3f770586ba05David Howells}
6206bbefe86796c07fb8a6d28114f1e3f770586ba05David Howells
6216bbefe86796c07fb8a6d28114f1e3f770586ba05David Howellsstatic const struct file_operations prism2_ap_proc_fops = {
6226bbefe86796c07fb8a6d28114f1e3f770586ba05David Howells	.open		= prism2_ap_proc_open,
6236bbefe86796c07fb8a6d28114f1e3f770586ba05David Howells	.read		= seq_read,
6246bbefe86796c07fb8a6d28114f1e3f770586ba05David Howells	.llseek		= seq_lseek,
6256bbefe86796c07fb8a6d28114f1e3f770586ba05David Howells	.release	= seq_release,
6266bbefe86796c07fb8a6d28114f1e3f770586ba05David Howells};
627ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */
628ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
629ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
630ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinenvoid hostap_check_sta_fw_version(struct ap_data *ap, int sta_fw_ver)
631ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen{
632ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	if (!ap)
633ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		return;
634ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
635ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	if (sta_fw_ver == PRISM2_FW_VER(0,8,0)) {
636ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		PDEBUG(DEBUG_AP, "Using data::nullfunc ACK workaround - "
637ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		       "firmware upgrade recommended\n");
638ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		ap->nullfunc_ack = 1;
639ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	} else
640ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		ap->nullfunc_ack = 0;
641ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
642ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	if (sta_fw_ver == PRISM2_FW_VER(1,4,2)) {
643ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		printk(KERN_WARNING "%s: Warning: secondary station firmware "
644ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		       "version 1.4.2 does not seem to work in Host AP mode\n",
645ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		       ap->local->dev->name);
646ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	}
647ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen}
648ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
649ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
650ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen/* Called only as a tasklet (software IRQ) */
651ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinenstatic void hostap_ap_tx_cb(struct sk_buff *skb, int ok, void *data)
652ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen{
653ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	struct ap_data *ap = data;
6541ea893fde29d8cf1639da8989f4b843dc3283ca8Dan Williams	struct ieee80211_hdr *hdr;
655ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
656ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	if (!ap->local->hostapd || !ap->local->apdev) {
657ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		dev_kfree_skb(skb);
658ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		return;
659ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	}
660ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
661ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	/* Pass the TX callback frame to the hostapd; use 802.11 header version
662ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	 * 1 to indicate failure (no ACK) and 2 success (frame ACKed) */
663ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
6641ea893fde29d8cf1639da8989f4b843dc3283ca8Dan Williams	hdr = (struct ieee80211_hdr *) skb->data;
6651ea893fde29d8cf1639da8989f4b843dc3283ca8Dan Williams	hdr->frame_control &= cpu_to_le16(~IEEE80211_FCTL_VERS);
6661ea893fde29d8cf1639da8989f4b843dc3283ca8Dan Williams	hdr->frame_control |= cpu_to_le16(ok ? BIT(1) : BIT(0));
667ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
668ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	skb->dev = ap->local->apdev;
6691ea893fde29d8cf1639da8989f4b843dc3283ca8Dan Williams	skb_pull(skb, hostap_80211_get_hdrlen(hdr->frame_control));
670ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	skb->pkt_type = PACKET_OTHERHOST;
671c1b4aa3fb619782213af2af6652663c8f9cef373Harvey Harrison	skb->protocol = cpu_to_be16(ETH_P_802_2);
672ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	memset(skb->cb, 0, sizeof(skb->cb));
673ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	netif_rx(skb);
674ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen}
675ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
676ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
677ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT
678ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen/* Called only as a tasklet (software IRQ) */
679ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinenstatic void hostap_ap_tx_cb_auth(struct sk_buff *skb, int ok, void *data)
680ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen{
681ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	struct ap_data *ap = data;
682ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	struct net_device *dev = ap->local->dev;
6831ea893fde29d8cf1639da8989f4b843dc3283ca8Dan Williams	struct ieee80211_hdr *hdr;
6841ea893fde29d8cf1639da8989f4b843dc3283ca8Dan Williams	u16 auth_alg, auth_transaction, status;
6858a9faf3cd08b91aca1502dbe18e3b5063fda2e87Al Viro	__le16 *pos;
686ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	struct sta_info *sta = NULL;
687ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	char *txt = NULL;
688ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
689ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	if (ap->local->hostapd) {
690ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		dev_kfree_skb(skb);
691ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		return;
692ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	}
693ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
6941ea893fde29d8cf1639da8989f4b843dc3283ca8Dan Williams	hdr = (struct ieee80211_hdr *) skb->data;
6951ea893fde29d8cf1639da8989f4b843dc3283ca8Dan Williams	if (!ieee80211_is_auth(hdr->frame_control) ||
696ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	    skb->len < IEEE80211_MGMT_HDR_LEN + 6) {
697ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		printk(KERN_DEBUG "%s: hostap_ap_tx_cb_auth received invalid "
698ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		       "frame\n", dev->name);
699ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		dev_kfree_skb(skb);
700ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		return;
701ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	}
702ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
7038a9faf3cd08b91aca1502dbe18e3b5063fda2e87Al Viro	pos = (__le16 *) (skb->data + IEEE80211_MGMT_HDR_LEN);
704ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	auth_alg = le16_to_cpu(*pos++);
705ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	auth_transaction = le16_to_cpu(*pos++);
706ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	status = le16_to_cpu(*pos++);
707ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
708ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	if (!ok) {
709ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		txt = "frame was not ACKed";
710ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		goto done;
711ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	}
712ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
713ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	spin_lock(&ap->sta_table_lock);
714ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	sta = ap_get_sta(ap, hdr->addr1);
715ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	if (sta)
716ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		atomic_inc(&sta->users);
717ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	spin_unlock(&ap->sta_table_lock);
718ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
719ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	if (!sta) {
720ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		txt = "STA not found";
721ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		goto done;
722ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	}
723ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
724ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	if (status == WLAN_STATUS_SUCCESS &&
725ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	    ((auth_alg == WLAN_AUTH_OPEN && auth_transaction == 2) ||
726ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	     (auth_alg == WLAN_AUTH_SHARED_KEY && auth_transaction == 4))) {
727ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		txt = "STA authenticated";
728ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		sta->flags |= WLAN_STA_AUTH;
729ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		sta->last_auth = jiffies;
730ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	} else if (status != WLAN_STATUS_SUCCESS)
731ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		txt = "authentication failed";
732ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
733ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen done:
734ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	if (sta)
735ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		atomic_dec(&sta->users);
736ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	if (txt) {
737e174961ca1a0b28f7abf0be47973ad57cb74e5f0Johannes Berg		PDEBUG(DEBUG_AP, "%s: %pM auth_cb - alg=%d "
7380795af5729b18218767fab27c44b1384f72dc9adJoe Perches		       "trans#=%d status=%d - %s\n",
739e174961ca1a0b28f7abf0be47973ad57cb74e5f0Johannes Berg		       dev->name, hdr->addr1,
74021f644f3eabde637f255f75ad05d0821a7a36b7fDavid S. Miller		       auth_alg, auth_transaction, status, txt);
741ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	}
742ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	dev_kfree_skb(skb);
743ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen}
744ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
745ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
746ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen/* Called only as a tasklet (software IRQ) */
747ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinenstatic void hostap_ap_tx_cb_assoc(struct sk_buff *skb, int ok, void *data)
748ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen{
749ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	struct ap_data *ap = data;
750ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	struct net_device *dev = ap->local->dev;
7511ea893fde29d8cf1639da8989f4b843dc3283ca8Dan Williams	struct ieee80211_hdr *hdr;
7521baf8a90bd5ad61bc0c55521cc097586531e7eb7Justin P. Mattock	u16 status;
7538a9faf3cd08b91aca1502dbe18e3b5063fda2e87Al Viro	__le16 *pos;
754ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	struct sta_info *sta = NULL;
755ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	char *txt = NULL;
756ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
757ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	if (ap->local->hostapd) {
758ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		dev_kfree_skb(skb);
759ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		return;
760ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	}
761ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
7621ea893fde29d8cf1639da8989f4b843dc3283ca8Dan Williams	hdr = (struct ieee80211_hdr *) skb->data;
7631ea893fde29d8cf1639da8989f4b843dc3283ca8Dan Williams	if ((!ieee80211_is_assoc_resp(hdr->frame_control) &&
7641ea893fde29d8cf1639da8989f4b843dc3283ca8Dan Williams	     !ieee80211_is_reassoc_resp(hdr->frame_control)) ||
765ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	    skb->len < IEEE80211_MGMT_HDR_LEN + 4) {
766ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		printk(KERN_DEBUG "%s: hostap_ap_tx_cb_assoc received invalid "
767ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		       "frame\n", dev->name);
768ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		dev_kfree_skb(skb);
769ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		return;
770ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	}
771ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
772ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	if (!ok) {
773ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		txt = "frame was not ACKed";
774ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		goto done;
775ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	}
776ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
777ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	spin_lock(&ap->sta_table_lock);
778ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	sta = ap_get_sta(ap, hdr->addr1);
779ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	if (sta)
780ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		atomic_inc(&sta->users);
781ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	spin_unlock(&ap->sta_table_lock);
782ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
783ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	if (!sta) {
784ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		txt = "STA not found";
785ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		goto done;
786ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	}
787ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
7888a9faf3cd08b91aca1502dbe18e3b5063fda2e87Al Viro	pos = (__le16 *) (skb->data + IEEE80211_MGMT_HDR_LEN);
789ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	pos++;
790ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	status = le16_to_cpu(*pos++);
791ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	if (status == WLAN_STATUS_SUCCESS) {
792ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		if (!(sta->flags & WLAN_STA_ASSOC))
793ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			hostap_event_new_sta(dev, sta);
794ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		txt = "STA associated";
795ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		sta->flags |= WLAN_STA_ASSOC;
796ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		sta->last_assoc = jiffies;
797ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	} else
798ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		txt = "association failed";
799ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
800ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen done:
801ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	if (sta)
802ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		atomic_dec(&sta->users);
803ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	if (txt) {
804e174961ca1a0b28f7abf0be47973ad57cb74e5f0Johannes Berg		PDEBUG(DEBUG_AP, "%s: %pM assoc_cb - %s\n",
805e174961ca1a0b28f7abf0be47973ad57cb74e5f0Johannes Berg		       dev->name, hdr->addr1, txt);
806ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	}
807ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	dev_kfree_skb(skb);
808ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen}
809ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
810ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen/* Called only as a tasklet (software IRQ); TX callback for poll frames used
811ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen * in verifying whether the STA is still present. */
812ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinenstatic void hostap_ap_tx_cb_poll(struct sk_buff *skb, int ok, void *data)
813ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen{
814ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	struct ap_data *ap = data;
8151ea893fde29d8cf1639da8989f4b843dc3283ca8Dan Williams	struct ieee80211_hdr *hdr;
816ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	struct sta_info *sta;
817ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
818ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	if (skb->len < 24)
819ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		goto fail;
8201ea893fde29d8cf1639da8989f4b843dc3283ca8Dan Williams	hdr = (struct ieee80211_hdr *) skb->data;
821ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	if (ok) {
822ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		spin_lock(&ap->sta_table_lock);
823ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		sta = ap_get_sta(ap, hdr->addr1);
824ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		if (sta)
825ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			sta->flags &= ~WLAN_STA_PENDING_POLL;
826ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		spin_unlock(&ap->sta_table_lock);
827ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	} else {
828e174961ca1a0b28f7abf0be47973ad57cb74e5f0Johannes Berg		PDEBUG(DEBUG_AP,
829e174961ca1a0b28f7abf0be47973ad57cb74e5f0Johannes Berg		       "%s: STA %pM did not ACK activity poll frame\n",
830e174961ca1a0b28f7abf0be47973ad57cb74e5f0Johannes Berg		       ap->local->dev->name, hdr->addr1);
831ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	}
832ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
833ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen fail:
834ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	dev_kfree_skb(skb);
835ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen}
836ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */
837ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
838ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
839ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinenvoid hostap_init_data(local_info_t *local)
840ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen{
841ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	struct ap_data *ap = local->ap;
842ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
843ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	if (ap == NULL) {
844ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		printk(KERN_WARNING "hostap_init_data: ap == NULL\n");
845ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		return;
846ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	}
847ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	memset(ap, 0, sizeof(struct ap_data));
848ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	ap->local = local;
849ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
850ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	ap->ap_policy = GET_INT_PARM(other_ap_policy, local->card_idx);
851ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	ap->bridge_packets = GET_INT_PARM(ap_bridge_packets, local->card_idx);
852ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	ap->max_inactivity =
853ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		GET_INT_PARM(ap_max_inactivity, local->card_idx) * HZ;
854ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	ap->autom_ap_wds = GET_INT_PARM(autom_ap_wds, local->card_idx);
855ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
856ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	spin_lock_init(&ap->sta_table_lock);
857ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	INIT_LIST_HEAD(&ap->sta_list);
858ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
859ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	/* Initialize task queue structure for AP management */
860c4028958b6ecad064b1a6303a6a5906d4fe48d73David Howells	INIT_WORK(&local->ap->add_sta_proc_queue, handle_add_proc_queue);
861ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
862ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	ap->tx_callback_idx =
863ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		hostap_tx_callback_register(local, hostap_ap_tx_cb, ap);
864ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	if (ap->tx_callback_idx == 0)
865ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		printk(KERN_WARNING "%s: failed to register TX callback for "
866ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		       "AP\n", local->dev->name);
867ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT
868c4028958b6ecad064b1a6303a6a5906d4fe48d73David Howells	INIT_WORK(&local->ap->wds_oper_queue, handle_wds_oper_queue);
869ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
870ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	ap->tx_callback_auth =
871ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		hostap_tx_callback_register(local, hostap_ap_tx_cb_auth, ap);
872ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	ap->tx_callback_assoc =
873ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		hostap_tx_callback_register(local, hostap_ap_tx_cb_assoc, ap);
874ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	ap->tx_callback_poll =
875ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		hostap_tx_callback_register(local, hostap_ap_tx_cb_poll, ap);
876ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	if (ap->tx_callback_auth == 0 || ap->tx_callback_assoc == 0 ||
877ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		ap->tx_callback_poll == 0)
878ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		printk(KERN_WARNING "%s: failed to register TX callback for "
879ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		       "AP\n", local->dev->name);
880ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
881ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	spin_lock_init(&ap->mac_restrictions.lock);
882ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	INIT_LIST_HEAD(&ap->mac_restrictions.mac_list);
883ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */
884ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
885ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	ap->initialized = 1;
886ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen}
887ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
888ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
889ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinenvoid hostap_init_ap_proc(local_info_t *local)
890ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen{
891ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	struct ap_data *ap = local->ap;
892ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
893ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	ap->proc = local->proc;
894ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	if (ap->proc == NULL)
895ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		return;
896ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
897ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen#ifndef PRISM2_NO_PROCFS_DEBUG
8986bbefe86796c07fb8a6d28114f1e3f770586ba05David Howells	proc_create_data("ap_debug", 0, ap->proc, &ap_debug_proc_fops, ap);
899ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen#endif /* PRISM2_NO_PROCFS_DEBUG */
900ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
901ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT
9026bbefe86796c07fb8a6d28114f1e3f770586ba05David Howells	proc_create_data("ap_control", 0, ap->proc, &ap_control_proc_fops, ap);
9036bbefe86796c07fb8a6d28114f1e3f770586ba05David Howells	proc_create_data("ap", 0, ap->proc, &prism2_ap_proc_fops, ap);
904ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */
905ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
906ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen}
907ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
908ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
909ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinenvoid hostap_free_data(struct ap_data *ap)
910ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen{
911c15057313725942c6af8dcb60b4d4322101316d9Matthias Kaehlcke	struct sta_info *n, *sta;
912ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
913ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	if (ap == NULL || !ap->initialized) {
914ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		printk(KERN_DEBUG "hostap_free_data: ap has not yet been "
915ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		       "initialized - skip resource freeing\n");
916ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		return;
917ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	}
918ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
91943829731dd372d04d6706c51052b9dabab9ca356Tejun Heo	flush_work(&ap->add_sta_proc_queue);
9201635953305694ece16d99078ca6d32f3d4e7eb36Tejun Heo
921ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT
92243829731dd372d04d6706c51052b9dabab9ca356Tejun Heo	flush_work(&ap->wds_oper_queue);
923ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	if (ap->crypt)
924ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		ap->crypt->deinit(ap->crypt_priv);
925ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	ap->crypt = ap->crypt_priv = NULL;
926ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */
927ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
928c15057313725942c6af8dcb60b4d4322101316d9Matthias Kaehlcke	list_for_each_entry_safe(sta, n, &ap->sta_list, list) {
929ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		ap_sta_hash_del(ap, sta);
930ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		list_del(&sta->list);
931ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		if ((sta->flags & WLAN_STA_ASSOC) && !sta->ap && sta->local)
932ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			hostap_event_expired_sta(sta->local->dev, sta);
933ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		ap_free_sta(ap, sta);
934ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	}
935ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
936ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen#ifndef PRISM2_NO_PROCFS_DEBUG
937ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	if (ap->proc != NULL) {
938ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		remove_proc_entry("ap_debug", ap->proc);
939ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	}
940ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen#endif /* PRISM2_NO_PROCFS_DEBUG */
941ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
942ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT
943ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	if (ap->proc != NULL) {
944ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	  remove_proc_entry("ap", ap->proc);
945ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		remove_proc_entry("ap_control", ap->proc);
946ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	}
947ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	ap_control_flush_macs(&ap->mac_restrictions);
948ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */
949ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
950ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	ap->initialized = 0;
951ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen}
952ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
953ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
954ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen/* caller should have mutex for AP STA list handling */
955ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinenstatic struct sta_info* ap_get_sta(struct ap_data *ap, u8 *sta)
956ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen{
957ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	struct sta_info *s;
958ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
959ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	s = ap->sta_hash[STA_HASH(sta)];
960d22fbd70c2770b5e3ba0d7bc5b29f6ee686cdba4dingtianhong	while (s != NULL && !ether_addr_equal(s->addr, sta))
961ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		s = s->hnext;
962ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	return s;
963ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen}
964ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
965ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
966ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT
967ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
968ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen/* Called from timer handler and from scheduled AP queue handlers */
969ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinenstatic void prism2_send_mgmt(struct net_device *dev,
9704339d328631aa815fe2181b9164b3690ca2db4daJouni Malinen			     u16 type_subtype, char *body,
971ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			     int body_len, u8 *addr, u16 tx_cb_idx)
972ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen{
973ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	struct hostap_interface *iface;
974ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	local_info_t *local;
9751ea893fde29d8cf1639da8989f4b843dc3283ca8Dan Williams	struct ieee80211_hdr *hdr;
976ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	u16 fc;
977ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	struct sk_buff *skb;
978ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	struct hostap_skb_tx_data *meta;
979ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	int hdrlen;
980ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
981ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	iface = netdev_priv(dev);
982ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	local = iface->local;
983ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	dev = local->dev; /* always use master radio device */
984ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	iface = netdev_priv(dev);
985ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
986ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	if (!(dev->flags & IFF_UP)) {
987ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		PDEBUG(DEBUG_AP, "%s: prism2_send_mgmt - device is not UP - "
988ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		       "cannot send frame\n", dev->name);
989ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		return;
990ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	}
991ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
992ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	skb = dev_alloc_skb(sizeof(*hdr) + body_len);
993ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	if (skb == NULL) {
994ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		PDEBUG(DEBUG_AP, "%s: prism2_send_mgmt failed to allocate "
995ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		       "skb\n", dev->name);
996ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		return;
997ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	}
998ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
9994339d328631aa815fe2181b9164b3690ca2db4daJouni Malinen	fc = type_subtype;
10001ea893fde29d8cf1639da8989f4b843dc3283ca8Dan Williams	hdrlen = hostap_80211_get_hdrlen(cpu_to_le16(type_subtype));
10011ea893fde29d8cf1639da8989f4b843dc3283ca8Dan Williams	hdr = (struct ieee80211_hdr *) skb_put(skb, hdrlen);
1002ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	if (body)
1003ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		memcpy(skb_put(skb, body_len), body, body_len);
1004ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
1005ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	memset(hdr, 0, hdrlen);
1006ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
1007ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	/* FIX: ctrl::ack sending used special HFA384X_TX_CTRL_802_11
1008ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	 * tx_control instead of using local->tx_control */
1009ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
1010ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
1011ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	memcpy(hdr->addr1, addr, ETH_ALEN); /* DA / RA */
10121ea893fde29d8cf1639da8989f4b843dc3283ca8Dan Williams	if (ieee80211_is_data(hdr->frame_control)) {
1013b2f4a2e3b1620667da9654f9e220c92ea52bdbe4Jouni Malinen		fc |= IEEE80211_FCTL_FROMDS;
1014ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		memcpy(hdr->addr2, dev->dev_addr, ETH_ALEN); /* BSSID */
1015ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		memcpy(hdr->addr3, dev->dev_addr, ETH_ALEN); /* SA */
10161ea893fde29d8cf1639da8989f4b843dc3283ca8Dan Williams	} else if (ieee80211_is_ctl(hdr->frame_control)) {
1017ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		/* control:ACK does not have addr2 or addr3 */
1018ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		memset(hdr->addr2, 0, ETH_ALEN);
1019ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		memset(hdr->addr3, 0, ETH_ALEN);
1020ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	} else {
1021ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		memcpy(hdr->addr2, dev->dev_addr, ETH_ALEN); /* SA */
1022ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		memcpy(hdr->addr3, dev->dev_addr, ETH_ALEN); /* BSSID */
1023ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	}
1024ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
10251ea893fde29d8cf1639da8989f4b843dc3283ca8Dan Williams	hdr->frame_control = cpu_to_le16(fc);
1026ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
1027ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	meta = (struct hostap_skb_tx_data *) skb->cb;
1028ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	memset(meta, 0, sizeof(*meta));
1029ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	meta->magic = HOSTAP_SKB_TX_DATA_MAGIC;
1030ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	meta->iface = iface;
1031ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	meta->tx_cb_idx = tx_cb_idx;
1032ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
1033ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	skb->dev = dev;
1034459a98ed881802dee55897441bc7f77af614368eArnaldo Carvalho de Melo	skb_reset_mac_header(skb);
1035c1d2bbe1cd6c7bbdc6d532cefebb66c7efb789ceArnaldo Carvalho de Melo	skb_reset_network_header(skb);
1036ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	dev_queue_xmit(skb);
1037ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen}
1038ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */
1039ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
1040ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
10416bbefe86796c07fb8a6d28114f1e3f770586ba05David Howellsstatic int prism2_sta_proc_show(struct seq_file *m, void *v)
1042ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen{
10436bbefe86796c07fb8a6d28114f1e3f770586ba05David Howells	struct sta_info *sta = m->private;
1044ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	int i;
1045ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
1046ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	/* FIX: possible race condition.. the STA data could have just expired,
1047ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	 * but proc entry was still here so that the read could have started;
1048ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	 * some locking should be done here.. */
1049ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
10506bbefe86796c07fb8a6d28114f1e3f770586ba05David Howells	seq_printf(m,
10516bbefe86796c07fb8a6d28114f1e3f770586ba05David Howells		   "%s=%pM\nusers=%d\naid=%d\n"
10526bbefe86796c07fb8a6d28114f1e3f770586ba05David Howells		   "flags=0x%04x%s%s%s%s%s%s%s\n"
10536bbefe86796c07fb8a6d28114f1e3f770586ba05David Howells		   "capability=0x%02x\nlisten_interval=%d\nsupported_rates=",
10546bbefe86796c07fb8a6d28114f1e3f770586ba05David Howells		   sta->ap ? "AP" : "STA",
10556bbefe86796c07fb8a6d28114f1e3f770586ba05David Howells		   sta->addr, atomic_read(&sta->users), sta->aid,
10566bbefe86796c07fb8a6d28114f1e3f770586ba05David Howells		   sta->flags,
10576bbefe86796c07fb8a6d28114f1e3f770586ba05David Howells		   sta->flags & WLAN_STA_AUTH ? " AUTH" : "",
10586bbefe86796c07fb8a6d28114f1e3f770586ba05David Howells		   sta->flags & WLAN_STA_ASSOC ? " ASSOC" : "",
10596bbefe86796c07fb8a6d28114f1e3f770586ba05David Howells		   sta->flags & WLAN_STA_PS ? " PS" : "",
10606bbefe86796c07fb8a6d28114f1e3f770586ba05David Howells		   sta->flags & WLAN_STA_TIM ? " TIM" : "",
10616bbefe86796c07fb8a6d28114f1e3f770586ba05David Howells		   sta->flags & WLAN_STA_PERM ? " PERM" : "",
10626bbefe86796c07fb8a6d28114f1e3f770586ba05David Howells		   sta->flags & WLAN_STA_AUTHORIZED ? " AUTHORIZED" : "",
10636bbefe86796c07fb8a6d28114f1e3f770586ba05David Howells		   sta->flags & WLAN_STA_PENDING_POLL ? " POLL" : "",
10646bbefe86796c07fb8a6d28114f1e3f770586ba05David Howells		   sta->capability, sta->listen_interval);
1065ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	/* supported_rates: 500 kbit/s units with msb ignored */
1066ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	for (i = 0; i < sizeof(sta->supported_rates); i++)
1067ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		if (sta->supported_rates[i] != 0)
10686bbefe86796c07fb8a6d28114f1e3f770586ba05David Howells			seq_printf(m, "%d%sMbps ",
10696bbefe86796c07fb8a6d28114f1e3f770586ba05David Howells				   (sta->supported_rates[i] & 0x7f) / 2,
10706bbefe86796c07fb8a6d28114f1e3f770586ba05David Howells				   sta->supported_rates[i] & 1 ? ".5" : "");
10716bbefe86796c07fb8a6d28114f1e3f770586ba05David Howells	seq_printf(m,
10726bbefe86796c07fb8a6d28114f1e3f770586ba05David Howells		   "\njiffies=%lu\nlast_auth=%lu\nlast_assoc=%lu\n"
10736bbefe86796c07fb8a6d28114f1e3f770586ba05David Howells		   "last_rx=%lu\nlast_tx=%lu\nrx_packets=%lu\n"
10746bbefe86796c07fb8a6d28114f1e3f770586ba05David Howells		   "tx_packets=%lu\n"
10756bbefe86796c07fb8a6d28114f1e3f770586ba05David Howells		   "rx_bytes=%lu\ntx_bytes=%lu\nbuffer_count=%d\n"
10766bbefe86796c07fb8a6d28114f1e3f770586ba05David Howells		   "last_rx: silence=%d dBm signal=%d dBm rate=%d%s Mbps\n"
10776bbefe86796c07fb8a6d28114f1e3f770586ba05David Howells		   "tx_rate=%d\ntx[1M]=%d\ntx[2M]=%d\ntx[5.5M]=%d\n"
10786bbefe86796c07fb8a6d28114f1e3f770586ba05David Howells		   "tx[11M]=%d\n"
10796bbefe86796c07fb8a6d28114f1e3f770586ba05David Howells		   "rx[1M]=%d\nrx[2M]=%d\nrx[5.5M]=%d\nrx[11M]=%d\n",
10806bbefe86796c07fb8a6d28114f1e3f770586ba05David Howells		   jiffies, sta->last_auth, sta->last_assoc, sta->last_rx,
10816bbefe86796c07fb8a6d28114f1e3f770586ba05David Howells		   sta->last_tx,
10826bbefe86796c07fb8a6d28114f1e3f770586ba05David Howells		   sta->rx_packets, sta->tx_packets, sta->rx_bytes,
10836bbefe86796c07fb8a6d28114f1e3f770586ba05David Howells		   sta->tx_bytes, skb_queue_len(&sta->tx_buf),
10846bbefe86796c07fb8a6d28114f1e3f770586ba05David Howells		   sta->last_rx_silence,
10856bbefe86796c07fb8a6d28114f1e3f770586ba05David Howells		   sta->last_rx_signal, sta->last_rx_rate / 10,
10866bbefe86796c07fb8a6d28114f1e3f770586ba05David Howells		   sta->last_rx_rate % 10 ? ".5" : "",
10876bbefe86796c07fb8a6d28114f1e3f770586ba05David Howells		   sta->tx_rate, sta->tx_count[0], sta->tx_count[1],
10886bbefe86796c07fb8a6d28114f1e3f770586ba05David Howells		   sta->tx_count[2], sta->tx_count[3],  sta->rx_count[0],
10896bbefe86796c07fb8a6d28114f1e3f770586ba05David Howells		   sta->rx_count[1], sta->rx_count[2], sta->rx_count[3]);
1090ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	if (sta->crypt && sta->crypt->ops && sta->crypt->ops->print_stats)
10916bbefe86796c07fb8a6d28114f1e3f770586ba05David Howells		sta->crypt->ops->print_stats(m, sta->crypt->priv);
1092ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT
1093ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	if (sta->ap) {
1094ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		if (sta->u.ap.channel >= 0)
10956bbefe86796c07fb8a6d28114f1e3f770586ba05David Howells			seq_printf(m, "channel=%d\n", sta->u.ap.channel);
10966bbefe86796c07fb8a6d28114f1e3f770586ba05David Howells		seq_puts(m, "ssid=");
10976bbefe86796c07fb8a6d28114f1e3f770586ba05David Howells		for (i = 0; i < sta->u.ap.ssid_len; i++) {
10986bbefe86796c07fb8a6d28114f1e3f770586ba05David Howells			if (sta->u.ap.ssid[i] >= 32 && sta->u.ap.ssid[i] < 127)
10996bbefe86796c07fb8a6d28114f1e3f770586ba05David Howells				seq_putc(m, sta->u.ap.ssid[i]);
11006bbefe86796c07fb8a6d28114f1e3f770586ba05David Howells			else
11016bbefe86796c07fb8a6d28114f1e3f770586ba05David Howells				seq_printf(m, "<%02x>", sta->u.ap.ssid[i]);
11026bbefe86796c07fb8a6d28114f1e3f770586ba05David Howells		}
11036bbefe86796c07fb8a6d28114f1e3f770586ba05David Howells		seq_putc(m, '\n');
1104ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	}
1105ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */
1106ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
11076bbefe86796c07fb8a6d28114f1e3f770586ba05David Howells	return 0;
11086bbefe86796c07fb8a6d28114f1e3f770586ba05David Howells}
11096bbefe86796c07fb8a6d28114f1e3f770586ba05David Howells
11106bbefe86796c07fb8a6d28114f1e3f770586ba05David Howellsstatic int prism2_sta_proc_open(struct inode *inode, struct file *file)
11116bbefe86796c07fb8a6d28114f1e3f770586ba05David Howells{
11126bbefe86796c07fb8a6d28114f1e3f770586ba05David Howells	return single_open(file, prism2_sta_proc_show, PDE_DATA(inode));
1113ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen}
1114ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
11156bbefe86796c07fb8a6d28114f1e3f770586ba05David Howellsstatic const struct file_operations prism2_sta_proc_fops = {
11166bbefe86796c07fb8a6d28114f1e3f770586ba05David Howells	.open		= prism2_sta_proc_open,
11176bbefe86796c07fb8a6d28114f1e3f770586ba05David Howells	.read		= seq_read,
11186bbefe86796c07fb8a6d28114f1e3f770586ba05David Howells	.llseek		= seq_lseek,
1119bc3041f055c228ec8347580d95cb2b344b503dcbAl Viro	.release	= single_release,
11206bbefe86796c07fb8a6d28114f1e3f770586ba05David Howells};
1121ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
1122c4028958b6ecad064b1a6303a6a5906d4fe48d73David Howellsstatic void handle_add_proc_queue(struct work_struct *work)
1123ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen{
1124c4028958b6ecad064b1a6303a6a5906d4fe48d73David Howells	struct ap_data *ap = container_of(work, struct ap_data,
1125c4028958b6ecad064b1a6303a6a5906d4fe48d73David Howells					  add_sta_proc_queue);
1126ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	struct sta_info *sta;
1127ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	char name[20];
1128ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	struct add_sta_proc_data *entry, *prev;
1129ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
1130ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	entry = ap->add_sta_proc_entries;
1131ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	ap->add_sta_proc_entries = NULL;
1132ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
1133ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	while (entry) {
1134ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		spin_lock_bh(&ap->sta_table_lock);
1135ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		sta = ap_get_sta(ap, entry->addr);
1136ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		if (sta)
1137ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			atomic_inc(&sta->users);
1138ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		spin_unlock_bh(&ap->sta_table_lock);
1139ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
1140ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		if (sta) {
1141e174961ca1a0b28f7abf0be47973ad57cb74e5f0Johannes Berg			sprintf(name, "%pM", sta->addr);
11426bbefe86796c07fb8a6d28114f1e3f770586ba05David Howells			sta->proc = proc_create_data(
1143ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen				name, 0, ap->proc,
11446bbefe86796c07fb8a6d28114f1e3f770586ba05David Howells				&prism2_sta_proc_fops, sta);
1145ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
1146ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			atomic_dec(&sta->users);
1147ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		}
1148ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
1149ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		prev = entry;
1150ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		entry = entry->next;
1151ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		kfree(prev);
1152ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	}
1153ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen}
1154ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
1155ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
1156ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinenstatic struct sta_info * ap_add_sta(struct ap_data *ap, u8 *addr)
1157ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen{
1158ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	struct sta_info *sta;
1159ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
1160b0471bb7b779f5deea109e5bfdfe8c18d4a06241Yan Burman	sta = kzalloc(sizeof(struct sta_info), GFP_ATOMIC);
1161ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	if (sta == NULL) {
1162ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		PDEBUG(DEBUG_AP, "AP: kmalloc failed\n");
1163ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		return NULL;
1164ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	}
1165ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
1166ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	/* initialize STA info data */
1167ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	sta->local = ap->local;
1168ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	skb_queue_head_init(&sta->tx_buf);
1169ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	memcpy(sta->addr, addr, ETH_ALEN);
1170ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
1171ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	atomic_inc(&sta->users);
1172ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	spin_lock_bh(&ap->sta_table_lock);
1173ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	list_add(&sta->list, &ap->sta_list);
1174ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	ap->num_sta++;
1175ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	ap_sta_hash_add(ap, sta);
1176ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	spin_unlock_bh(&ap->sta_table_lock);
1177ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
1178ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	if (ap->proc) {
1179ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		struct add_sta_proc_data *entry;
1180ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		/* schedule a non-interrupt context process to add a procfs
1181ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		 * entry for the STA since procfs code use GFP_KERNEL */
1182ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		entry = kmalloc(sizeof(*entry), GFP_ATOMIC);
1183ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		if (entry) {
1184ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			memcpy(entry->addr, sta->addr, ETH_ALEN);
1185ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			entry->next = ap->add_sta_proc_entries;
1186ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			ap->add_sta_proc_entries = entry;
1187ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			schedule_work(&ap->add_sta_proc_queue);
1188ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		} else
1189ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			printk(KERN_DEBUG "Failed to add STA proc data\n");
1190ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	}
1191ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
1192ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT
1193ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	init_timer(&sta->timer);
1194ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	sta->timer.expires = jiffies + ap->max_inactivity;
1195ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	sta->timer.data = (unsigned long) sta;
1196ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	sta->timer.function = ap_handle_timer;
1197ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	if (!ap->local->hostapd)
1198ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		add_timer(&sta->timer);
1199ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */
1200ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
1201ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	return sta;
1202ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen}
1203ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
1204ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
1205ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinenstatic int ap_tx_rate_ok(int rateidx, struct sta_info *sta,
1206ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			 local_info_t *local)
1207ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen{
1208ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	if (rateidx > sta->tx_max_rate ||
1209ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	    !(sta->tx_supp_rates & (1 << rateidx)))
1210ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		return 0;
1211ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
1212ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	if (local->tx_rate_control != 0 &&
1213ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	    !(local->tx_rate_control & (1 << rateidx)))
1214ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		return 0;
1215ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
1216ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	return 1;
1217ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen}
1218ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
1219ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
1220ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinenstatic void prism2_check_tx_rates(struct sta_info *sta)
1221ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen{
1222ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	int i;
1223ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
1224ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	sta->tx_supp_rates = 0;
1225ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	for (i = 0; i < sizeof(sta->supported_rates); i++) {
1226ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		if ((sta->supported_rates[i] & 0x7f) == 2)
1227ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			sta->tx_supp_rates |= WLAN_RATE_1M;
1228ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		if ((sta->supported_rates[i] & 0x7f) == 4)
1229ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			sta->tx_supp_rates |= WLAN_RATE_2M;
1230ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		if ((sta->supported_rates[i] & 0x7f) == 11)
1231ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			sta->tx_supp_rates |= WLAN_RATE_5M5;
1232ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		if ((sta->supported_rates[i] & 0x7f) == 22)
1233ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			sta->tx_supp_rates |= WLAN_RATE_11M;
1234ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	}
1235ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	sta->tx_max_rate = sta->tx_rate = sta->tx_rate_idx = 0;
1236ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	if (sta->tx_supp_rates & WLAN_RATE_1M) {
1237ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		sta->tx_max_rate = 0;
1238ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		if (ap_tx_rate_ok(0, sta, sta->local)) {
1239ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			sta->tx_rate = 10;
1240ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			sta->tx_rate_idx = 0;
1241ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		}
1242ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	}
1243ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	if (sta->tx_supp_rates & WLAN_RATE_2M) {
1244ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		sta->tx_max_rate = 1;
1245ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		if (ap_tx_rate_ok(1, sta, sta->local)) {
1246ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			sta->tx_rate = 20;
1247ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			sta->tx_rate_idx = 1;
1248ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		}
1249ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	}
1250ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	if (sta->tx_supp_rates & WLAN_RATE_5M5) {
1251ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		sta->tx_max_rate = 2;
1252ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		if (ap_tx_rate_ok(2, sta, sta->local)) {
1253ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			sta->tx_rate = 55;
1254ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			sta->tx_rate_idx = 2;
1255ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		}
1256ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	}
1257ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	if (sta->tx_supp_rates & WLAN_RATE_11M) {
1258ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		sta->tx_max_rate = 3;
1259ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		if (ap_tx_rate_ok(3, sta, sta->local)) {
1260ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			sta->tx_rate = 110;
1261ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			sta->tx_rate_idx = 3;
1262ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		}
1263ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	}
1264ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen}
1265ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
1266ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
1267ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT
1268ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
1269ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinenstatic void ap_crypt_init(struct ap_data *ap)
1270ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen{
1271274bfb8dc5ffa16cb073801bebe76ab7f4e2e73dJohn W. Linville	ap->crypt = lib80211_get_crypto_ops("WEP");
1272ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
1273ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	if (ap->crypt) {
1274ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		if (ap->crypt->init) {
1275ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			ap->crypt_priv = ap->crypt->init(0);
1276ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			if (ap->crypt_priv == NULL)
1277ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen				ap->crypt = NULL;
1278ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			else {
1279ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen				u8 key[WEP_KEY_LEN];
1280ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen				get_random_bytes(key, WEP_KEY_LEN);
1281ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen				ap->crypt->set_key(key, WEP_KEY_LEN, NULL,
1282ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen						   ap->crypt_priv);
1283ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			}
1284ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		}
1285ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	}
1286ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
1287ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	if (ap->crypt == NULL) {
1288ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		printk(KERN_WARNING "AP could not initialize WEP: load module "
1289274bfb8dc5ffa16cb073801bebe76ab7f4e2e73dJohn W. Linville		       "lib80211_crypt_wep.ko\n");
1290ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	}
1291ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen}
1292ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
1293ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
1294ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen/* Generate challenge data for shared key authentication. IEEE 802.11 specifies
129525d1fbfdd98e6547f2ba3e36787f20d251019591Uwe Kleine-König * that WEP algorithm is used for generating challenge. This should be unique,
1296ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen * but otherwise there is not really need for randomness etc. Initialize WEP
1297ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen * with pseudo random key and then use increasing IV to get unique challenge
1298ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen * streams.
1299ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen *
1300ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen * Called only as a scheduled task for pending AP frames.
1301ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen */
1302ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinenstatic char * ap_auth_make_challenge(struct ap_data *ap)
1303ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen{
1304ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	char *tmpbuf;
1305ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	struct sk_buff *skb;
1306ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
1307ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	if (ap->crypt == NULL) {
1308ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		ap_crypt_init(ap);
1309ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		if (ap->crypt == NULL)
1310ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			return NULL;
1311ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	}
1312ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
13135cbded585d129d0226cb48ac4202b253c781be26Robert P. J. Day	tmpbuf = kmalloc(WLAN_AUTH_CHALLENGE_LEN, GFP_ATOMIC);
1314ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	if (tmpbuf == NULL) {
1315ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		PDEBUG(DEBUG_AP, "AP: kmalloc failed for challenge\n");
1316ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		return NULL;
1317ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	}
1318ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
1319ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	skb = dev_alloc_skb(WLAN_AUTH_CHALLENGE_LEN +
13205bfc819b53ed67c76f33f969ab627070e85d87c1James Ketrenos			    ap->crypt->extra_mpdu_prefix_len +
13215bfc819b53ed67c76f33f969ab627070e85d87c1James Ketrenos			    ap->crypt->extra_mpdu_postfix_len);
1322ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	if (skb == NULL) {
1323ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		kfree(tmpbuf);
1324ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		return NULL;
1325ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	}
1326ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
13275bfc819b53ed67c76f33f969ab627070e85d87c1James Ketrenos	skb_reserve(skb, ap->crypt->extra_mpdu_prefix_len);
1328ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	memset(skb_put(skb, WLAN_AUTH_CHALLENGE_LEN), 0,
1329ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	       WLAN_AUTH_CHALLENGE_LEN);
1330ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	if (ap->crypt->encrypt_mpdu(skb, 0, ap->crypt_priv)) {
1331ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		dev_kfree_skb(skb);
1332ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		kfree(tmpbuf);
1333ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		return NULL;
1334ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	}
1335ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
1336d626f62b11e00c16e81e4308ab93d3f13551812aArnaldo Carvalho de Melo	skb_copy_from_linear_data_offset(skb, ap->crypt->extra_mpdu_prefix_len,
1337d626f62b11e00c16e81e4308ab93d3f13551812aArnaldo Carvalho de Melo					 tmpbuf, WLAN_AUTH_CHALLENGE_LEN);
1338ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	dev_kfree_skb(skb);
1339ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
1340ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	return tmpbuf;
1341ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen}
1342ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
1343ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
1344ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen/* Called only as a scheduled task for pending AP frames. */
1345ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinenstatic void handle_authen(local_info_t *local, struct sk_buff *skb,
1346ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			  struct hostap_80211_rx_status *rx_stats)
1347ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen{
1348ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	struct net_device *dev = local->dev;
13491ea893fde29d8cf1639da8989f4b843dc3283ca8Dan Williams	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
1350ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	size_t hdrlen;
1351ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	struct ap_data *ap = local->ap;
1352ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	char body[8 + WLAN_AUTH_CHALLENGE_LEN], *challenge = NULL;
1353ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	int len, olen;
13548a9faf3cd08b91aca1502dbe18e3b5063fda2e87Al Viro	u16 auth_alg, auth_transaction, status_code;
13558a9faf3cd08b91aca1502dbe18e3b5063fda2e87Al Viro	__le16 *pos;
13561ea893fde29d8cf1639da8989f4b843dc3283ca8Dan Williams	u16 resp = WLAN_STATUS_SUCCESS;
1357ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	struct sta_info *sta = NULL;
1358274bfb8dc5ffa16cb073801bebe76ab7f4e2e73dJohn W. Linville	struct lib80211_crypt_data *crypt;
1359ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	char *txt = "";
1360ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
1361ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	len = skb->len - IEEE80211_MGMT_HDR_LEN;
1362ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
13631ea893fde29d8cf1639da8989f4b843dc3283ca8Dan Williams	hdrlen = hostap_80211_get_hdrlen(hdr->frame_control);
1364ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
1365ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	if (len < 6) {
1366ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		PDEBUG(DEBUG_AP, "%s: handle_authen - too short payload "
1367e174961ca1a0b28f7abf0be47973ad57cb74e5f0Johannes Berg		       "(len=%d) from %pM\n", dev->name, len, hdr->addr2);
1368ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		return;
1369ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	}
1370ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
1371ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	spin_lock_bh(&local->ap->sta_table_lock);
1372ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	sta = ap_get_sta(local->ap, hdr->addr2);
1373ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	if (sta)
1374ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		atomic_inc(&sta->users);
1375ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	spin_unlock_bh(&local->ap->sta_table_lock);
1376ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
1377ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	if (sta && sta->crypt)
1378ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		crypt = sta->crypt;
1379ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	else {
1380ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		int idx = 0;
1381ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		if (skb->len >= hdrlen + 3)
1382ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			idx = skb->data[hdrlen + 3] >> 6;
1383274bfb8dc5ffa16cb073801bebe76ab7f4e2e73dJohn W. Linville		crypt = local->crypt_info.crypt[idx];
1384ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	}
1385ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
13868a9faf3cd08b91aca1502dbe18e3b5063fda2e87Al Viro	pos = (__le16 *) (skb->data + IEEE80211_MGMT_HDR_LEN);
1387ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	auth_alg = __le16_to_cpu(*pos);
1388ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	pos++;
1389ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	auth_transaction = __le16_to_cpu(*pos);
1390ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	pos++;
1391ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	status_code = __le16_to_cpu(*pos);
1392ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	pos++;
1393ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
1394d22fbd70c2770b5e3ba0d7bc5b29f6ee686cdba4dingtianhong	if (ether_addr_equal(dev->dev_addr, hdr->addr2) ||
1395ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	    ap_control_mac_deny(&ap->mac_restrictions, hdr->addr2)) {
1396ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		txt = "authentication denied";
1397ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
1398ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		goto fail;
1399ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	}
1400ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
1401ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	if (((local->auth_algs & PRISM2_AUTH_OPEN) &&
1402ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	     auth_alg == WLAN_AUTH_OPEN) ||
1403ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	    ((local->auth_algs & PRISM2_AUTH_SHARED_KEY) &&
1404ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	     crypt && auth_alg == WLAN_AUTH_SHARED_KEY)) {
1405ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	} else {
1406ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		txt = "unsupported algorithm";
1407ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		resp = WLAN_STATUS_NOT_SUPPORTED_AUTH_ALG;
1408ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		goto fail;
1409ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	}
1410ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
1411ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	if (len >= 8) {
1412ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		u8 *u = (u8 *) pos;
1413ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		if (*u == WLAN_EID_CHALLENGE) {
1414ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			if (*(u + 1) != WLAN_AUTH_CHALLENGE_LEN) {
1415ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen				txt = "invalid challenge len";
1416ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen				resp = WLAN_STATUS_CHALLENGE_FAIL;
1417ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen				goto fail;
1418ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			}
1419ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			if (len - 8 < WLAN_AUTH_CHALLENGE_LEN) {
1420ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen				txt = "challenge underflow";
1421ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen				resp = WLAN_STATUS_CHALLENGE_FAIL;
1422ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen				goto fail;
1423ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			}
1424ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			challenge = (char *) (u + 2);
1425ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		}
1426ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	}
1427ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
1428ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	if (sta && sta->ap) {
1429ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		if (time_after(jiffies, sta->u.ap.last_beacon +
1430ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			       (10 * sta->listen_interval * HZ) / 1024)) {
1431ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			PDEBUG(DEBUG_AP, "%s: no beacons received for a while,"
1432e174961ca1a0b28f7abf0be47973ad57cb74e5f0Johannes Berg			       " assuming AP %pM is now STA\n",
1433e174961ca1a0b28f7abf0be47973ad57cb74e5f0Johannes Berg			       dev->name, sta->addr);
1434ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			sta->ap = 0;
1435ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			sta->flags = 0;
1436ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			sta->u.sta.challenge = NULL;
1437ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		} else {
1438ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			txt = "AP trying to authenticate?";
1439ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
1440ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			goto fail;
1441ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		}
1442ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	}
1443ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
1444ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	if ((auth_alg == WLAN_AUTH_OPEN && auth_transaction == 1) ||
1445ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	    (auth_alg == WLAN_AUTH_SHARED_KEY &&
1446ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	     (auth_transaction == 1 ||
1447ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	      (auth_transaction == 3 && sta != NULL &&
1448ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	       sta->u.sta.challenge != NULL)))) {
1449ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	} else {
1450ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		txt = "unknown authentication transaction number";
1451ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		resp = WLAN_STATUS_UNKNOWN_AUTH_TRANSACTION;
1452ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		goto fail;
1453ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	}
1454ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
1455ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	if (sta == NULL) {
1456ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		txt = "new STA";
1457ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
1458ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		if (local->ap->num_sta >= MAX_STA_COUNT) {
1459ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			/* FIX: might try to remove some old STAs first? */
1460ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			txt = "no more room for new STAs";
1461ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
1462ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			goto fail;
1463ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		}
1464ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
1465ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		sta = ap_add_sta(local->ap, hdr->addr2);
1466ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		if (sta == NULL) {
1467ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			txt = "ap_add_sta failed";
1468ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
1469ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			goto fail;
1470ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		}
1471ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	}
1472ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
1473ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	switch (auth_alg) {
1474ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	case WLAN_AUTH_OPEN:
1475ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		txt = "authOK";
1476ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		/* IEEE 802.11 standard is not completely clear about
1477ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		 * whether STA is considered authenticated after
1478ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		 * authentication OK frame has been send or after it
1479ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		 * has been ACKed. In order to reduce interoperability
1480ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		 * issues, mark the STA authenticated before ACK. */
1481ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		sta->flags |= WLAN_STA_AUTH;
1482ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		break;
1483ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
1484ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	case WLAN_AUTH_SHARED_KEY:
1485ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		if (auth_transaction == 1) {
1486ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			if (sta->u.sta.challenge == NULL) {
1487ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen				sta->u.sta.challenge =
1488ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen					ap_auth_make_challenge(local->ap);
1489ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen				if (sta->u.sta.challenge == NULL) {
1490ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen					resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
1491ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen					goto fail;
1492ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen				}
1493ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			}
1494ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		} else {
1495ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			if (sta->u.sta.challenge == NULL ||
1496ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			    challenge == NULL ||
1497ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			    memcmp(sta->u.sta.challenge, challenge,
1498ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen				   WLAN_AUTH_CHALLENGE_LEN) != 0 ||
14991ea893fde29d8cf1639da8989f4b843dc3283ca8Dan Williams			    !ieee80211_has_protected(hdr->frame_control)) {
1500ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen				txt = "challenge response incorrect";
1501ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen				resp = WLAN_STATUS_CHALLENGE_FAIL;
1502ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen				goto fail;
1503ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			}
1504ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
1505ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			txt = "challenge OK - authOK";
1506ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			/* IEEE 802.11 standard is not completely clear about
1507ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			 * whether STA is considered authenticated after
1508ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			 * authentication OK frame has been send or after it
1509ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			 * has been ACKed. In order to reduce interoperability
1510ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			 * issues, mark the STA authenticated before ACK. */
1511ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			sta->flags |= WLAN_STA_AUTH;
1512ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			kfree(sta->u.sta.challenge);
1513ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			sta->u.sta.challenge = NULL;
1514ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		}
1515ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		break;
1516ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	}
1517ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
1518ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen fail:
15198a9faf3cd08b91aca1502dbe18e3b5063fda2e87Al Viro	pos = (__le16 *) body;
1520ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	*pos = cpu_to_le16(auth_alg);
1521ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	pos++;
1522ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	*pos = cpu_to_le16(auth_transaction + 1);
1523ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	pos++;
1524ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	*pos = cpu_to_le16(resp); /* status_code */
1525ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	pos++;
1526ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	olen = 6;
1527ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
1528ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	if (resp == WLAN_STATUS_SUCCESS && sta != NULL &&
1529ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	    sta->u.sta.challenge != NULL &&
1530ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	    auth_alg == WLAN_AUTH_SHARED_KEY && auth_transaction == 1) {
1531ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		u8 *tmp = (u8 *) pos;
1532ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		*tmp++ = WLAN_EID_CHALLENGE;
1533ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		*tmp++ = WLAN_AUTH_CHALLENGE_LEN;
1534ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		pos++;
1535ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		memcpy(pos, sta->u.sta.challenge, WLAN_AUTH_CHALLENGE_LEN);
1536ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		olen += 2 + WLAN_AUTH_CHALLENGE_LEN;
1537ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	}
1538ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
15394339d328631aa815fe2181b9164b3690ca2db4daJouni Malinen	prism2_send_mgmt(dev, IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_AUTH,
1540ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			 body, olen, hdr->addr2, ap->tx_callback_auth);
1541ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
1542ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	if (sta) {
1543ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		sta->last_rx = jiffies;
1544ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		atomic_dec(&sta->users);
1545ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	}
1546ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
1547ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	if (resp) {
1548e174961ca1a0b28f7abf0be47973ad57cb74e5f0Johannes Berg		PDEBUG(DEBUG_AP, "%s: %pM auth (alg=%d "
15490795af5729b18218767fab27c44b1384f72dc9adJoe Perches		       "trans#=%d stat=%d len=%d fc=%04x) ==> %d (%s)\n",
1550e174961ca1a0b28f7abf0be47973ad57cb74e5f0Johannes Berg		       dev->name, hdr->addr2,
155121f644f3eabde637f255f75ad05d0821a7a36b7fDavid S. Miller		       auth_alg, auth_transaction, status_code, len,
15521ea893fde29d8cf1639da8989f4b843dc3283ca8Dan Williams		       le16_to_cpu(hdr->frame_control), resp, txt);
1553ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	}
1554ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen}
1555ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
1556ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
1557ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen/* Called only as a scheduled task for pending AP frames. */
1558ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinenstatic void handle_assoc(local_info_t *local, struct sk_buff *skb,
1559ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			 struct hostap_80211_rx_status *rx_stats, int reassoc)
1560ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen{
1561ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	struct net_device *dev = local->dev;
15621ea893fde29d8cf1639da8989f4b843dc3283ca8Dan Williams	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
1563ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	char body[12], *p, *lpos;
1564ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	int len, left;
15658a9faf3cd08b91aca1502dbe18e3b5063fda2e87Al Viro	__le16 *pos;
1566ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	u16 resp = WLAN_STATUS_SUCCESS;
1567ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	struct sta_info *sta = NULL;
1568ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	int send_deauth = 0;
1569ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	char *txt = "";
1570ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	u8 prev_ap[ETH_ALEN];
1571ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
1572ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	left = len = skb->len - IEEE80211_MGMT_HDR_LEN;
1573ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
1574ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	if (len < (reassoc ? 10 : 4)) {
1575ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		PDEBUG(DEBUG_AP, "%s: handle_assoc - too short payload "
1576e174961ca1a0b28f7abf0be47973ad57cb74e5f0Johannes Berg		       "(len=%d, reassoc=%d) from %pM\n",
1577e174961ca1a0b28f7abf0be47973ad57cb74e5f0Johannes Berg		       dev->name, len, reassoc, hdr->addr2);
1578ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		return;
1579ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	}
1580ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
1581ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	spin_lock_bh(&local->ap->sta_table_lock);
1582ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	sta = ap_get_sta(local->ap, hdr->addr2);
1583ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	if (sta == NULL || (sta->flags & WLAN_STA_AUTH) == 0) {
1584ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		spin_unlock_bh(&local->ap->sta_table_lock);
1585ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		txt = "trying to associate before authentication";
1586ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		send_deauth = 1;
1587ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
1588ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		sta = NULL; /* do not decrement sta->users */
1589ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		goto fail;
1590ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	}
1591ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	atomic_inc(&sta->users);
1592ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	spin_unlock_bh(&local->ap->sta_table_lock);
1593ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
15948a9faf3cd08b91aca1502dbe18e3b5063fda2e87Al Viro	pos = (__le16 *) (skb->data + IEEE80211_MGMT_HDR_LEN);
1595ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	sta->capability = __le16_to_cpu(*pos);
1596ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	pos++; left -= 2;
1597ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	sta->listen_interval = __le16_to_cpu(*pos);
1598ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	pos++; left -= 2;
1599ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
1600ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	if (reassoc) {
1601ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		memcpy(prev_ap, pos, ETH_ALEN);
1602ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		pos++; pos++; pos++; left -= 6;
1603ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	} else
1604ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		memset(prev_ap, 0, ETH_ALEN);
1605ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
1606ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	if (left >= 2) {
1607ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		unsigned int ileft;
1608ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		unsigned char *u = (unsigned char *) pos;
1609ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
1610ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		if (*u == WLAN_EID_SSID) {
1611ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			u++; left--;
1612ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			ileft = *u;
1613ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			u++; left--;
1614ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
1615ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			if (ileft > left || ileft > MAX_SSID_LEN) {
1616ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen				txt = "SSID overflow";
1617ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen				resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
1618ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen				goto fail;
1619ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			}
1620ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
1621ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			if (ileft != strlen(local->essid) ||
1622ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			    memcmp(local->essid, u, ileft) != 0) {
1623ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen				txt = "not our SSID";
1624ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen				resp = WLAN_STATUS_ASSOC_DENIED_UNSPEC;
1625ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen				goto fail;
1626ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			}
1627ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
1628ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			u += ileft;
1629ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			left -= ileft;
1630ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		}
1631ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
1632ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		if (left >= 2 && *u == WLAN_EID_SUPP_RATES) {
1633ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			u++; left--;
1634ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			ileft = *u;
1635ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			u++; left--;
163674fae82c8bd5dd78365abe25506a9ba388d4a889Jeff Garzik
1637ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			if (ileft > left || ileft == 0 ||
1638ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			    ileft > WLAN_SUPP_RATES_MAX) {
1639ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen				txt = "SUPP_RATES len error";
1640ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen				resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
1641ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen				goto fail;
1642ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			}
1643ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
1644ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			memset(sta->supported_rates, 0,
1645ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			       sizeof(sta->supported_rates));
1646ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			memcpy(sta->supported_rates, u, ileft);
1647ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			prism2_check_tx_rates(sta);
1648ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
1649ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			u += ileft;
1650ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			left -= ileft;
1651ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		}
1652ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
1653ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		if (left > 0) {
1654e174961ca1a0b28f7abf0be47973ad57cb74e5f0Johannes Berg			PDEBUG(DEBUG_AP, "%s: assoc from %pM"
16550795af5729b18218767fab27c44b1384f72dc9adJoe Perches			       " with extra data (%d bytes) [",
1656e174961ca1a0b28f7abf0be47973ad57cb74e5f0Johannes Berg			       dev->name, hdr->addr2, left);
1657ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			while (left > 0) {
1658ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen				PDEBUG2(DEBUG_AP, "<%02x>", *u);
1659ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen				u++; left--;
1660ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			}
1661ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			PDEBUG2(DEBUG_AP, "]\n");
1662ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		}
1663ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	} else {
1664ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		txt = "frame underflow";
1665ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
1666ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		goto fail;
1667ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	}
1668ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
1669ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	/* get a unique AID */
1670ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	if (sta->aid > 0)
1671ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		txt = "OK, old AID";
1672ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	else {
1673ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		spin_lock_bh(&local->ap->sta_table_lock);
1674ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		for (sta->aid = 1; sta->aid <= MAX_AID_TABLE_SIZE; sta->aid++)
1675ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			if (local->ap->sta_aid[sta->aid - 1] == NULL)
1676ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen				break;
1677ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		if (sta->aid > MAX_AID_TABLE_SIZE) {
1678ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			sta->aid = 0;
1679ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			spin_unlock_bh(&local->ap->sta_table_lock);
1680ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			resp = WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA;
1681ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			txt = "no room for more AIDs";
1682ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		} else {
1683ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			local->ap->sta_aid[sta->aid - 1] = sta;
1684ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			spin_unlock_bh(&local->ap->sta_table_lock);
1685ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			txt = "OK, new AID";
1686ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		}
1687ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	}
1688ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
1689ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen fail:
16908a9faf3cd08b91aca1502dbe18e3b5063fda2e87Al Viro	pos = (__le16 *) body;
1691ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
1692ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	if (send_deauth) {
16938a9faf3cd08b91aca1502dbe18e3b5063fda2e87Al Viro		*pos = cpu_to_le16(WLAN_REASON_STA_REQ_ASSOC_WITHOUT_AUTH);
1694ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		pos++;
1695ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	} else {
1696ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		/* FIX: CF-Pollable and CF-PollReq should be set to match the
1697ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		 * values in beacons/probe responses */
1698ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		/* FIX: how about privacy and WEP? */
1699ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		/* capability */
17008a9faf3cd08b91aca1502dbe18e3b5063fda2e87Al Viro		*pos = cpu_to_le16(WLAN_CAPABILITY_ESS);
1701ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		pos++;
1702ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
1703ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		/* status_code */
17048a9faf3cd08b91aca1502dbe18e3b5063fda2e87Al Viro		*pos = cpu_to_le16(resp);
1705ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		pos++;
1706ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
17078a9faf3cd08b91aca1502dbe18e3b5063fda2e87Al Viro		*pos = cpu_to_le16((sta && sta->aid > 0 ? sta->aid : 0) |
1708ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen				     BIT(14) | BIT(15)); /* AID */
1709ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		pos++;
1710ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
1711ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		/* Supported rates (Information element) */
1712ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		p = (char *) pos;
1713ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		*p++ = WLAN_EID_SUPP_RATES;
1714ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		lpos = p;
1715ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		*p++ = 0; /* len */
1716ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		if (local->tx_rate_control & WLAN_RATE_1M) {
1717ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			*p++ = local->basic_rates & WLAN_RATE_1M ? 0x82 : 0x02;
1718ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			(*lpos)++;
1719ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		}
1720ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		if (local->tx_rate_control & WLAN_RATE_2M) {
1721ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			*p++ = local->basic_rates & WLAN_RATE_2M ? 0x84 : 0x04;
1722ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			(*lpos)++;
1723ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		}
1724ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		if (local->tx_rate_control & WLAN_RATE_5M5) {
1725ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			*p++ = local->basic_rates & WLAN_RATE_5M5 ?
1726ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen				0x8b : 0x0b;
1727ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			(*lpos)++;
1728ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		}
1729ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		if (local->tx_rate_control & WLAN_RATE_11M) {
1730ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			*p++ = local->basic_rates & WLAN_RATE_11M ?
1731ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen				0x96 : 0x16;
1732ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			(*lpos)++;
1733ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		}
17348a9faf3cd08b91aca1502dbe18e3b5063fda2e87Al Viro		pos = (__le16 *) p;
1735ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	}
1736ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
17374339d328631aa815fe2181b9164b3690ca2db4daJouni Malinen	prism2_send_mgmt(dev, IEEE80211_FTYPE_MGMT |
17384339d328631aa815fe2181b9164b3690ca2db4daJouni Malinen			 (send_deauth ? IEEE80211_STYPE_DEAUTH :
17394339d328631aa815fe2181b9164b3690ca2db4daJouni Malinen			  (reassoc ? IEEE80211_STYPE_REASSOC_RESP :
17404339d328631aa815fe2181b9164b3690ca2db4daJouni Malinen			   IEEE80211_STYPE_ASSOC_RESP)),
1741ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			 body, (u8 *) pos - (u8 *) body,
1742ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			 hdr->addr2,
1743ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			 send_deauth ? 0 : local->ap->tx_callback_assoc);
1744ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
1745ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	if (sta) {
1746ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		if (resp == WLAN_STATUS_SUCCESS) {
1747ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			sta->last_rx = jiffies;
1748ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			/* STA will be marked associated from TX callback, if
1749ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			 * AssocResp is ACKed */
1750ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		}
1751ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		atomic_dec(&sta->users);
1752ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	}
1753ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
1754ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen#if 0
1755e174961ca1a0b28f7abf0be47973ad57cb74e5f0Johannes Berg	PDEBUG(DEBUG_AP, "%s: %pM %sassoc (len=%d "
1756e174961ca1a0b28f7abf0be47973ad57cb74e5f0Johannes Berg	       "prev_ap=%pM) => %d(%d) (%s)\n",
175721f644f3eabde637f255f75ad05d0821a7a36b7fDavid S. Miller	       dev->name,
1758e174961ca1a0b28f7abf0be47973ad57cb74e5f0Johannes Berg	       hdr->addr2,
175921f644f3eabde637f255f75ad05d0821a7a36b7fDavid S. Miller	       reassoc ? "re" : "", len,
1760e174961ca1a0b28f7abf0be47973ad57cb74e5f0Johannes Berg	       prev_ap,
176121f644f3eabde637f255f75ad05d0821a7a36b7fDavid S. Miller	       resp, send_deauth, txt);
1762ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen#endif
1763ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen}
1764ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
1765ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
1766ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen/* Called only as a scheduled task for pending AP frames. */
1767ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinenstatic void handle_deauth(local_info_t *local, struct sk_buff *skb,
1768ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			  struct hostap_80211_rx_status *rx_stats)
1769ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen{
1770ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	struct net_device *dev = local->dev;
17711ea893fde29d8cf1639da8989f4b843dc3283ca8Dan Williams	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
1772ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	char *body = (char *) (skb->data + IEEE80211_MGMT_HDR_LEN);
1773ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	int len;
17748a9faf3cd08b91aca1502dbe18e3b5063fda2e87Al Viro	u16 reason_code;
17758a9faf3cd08b91aca1502dbe18e3b5063fda2e87Al Viro	__le16 *pos;
1776ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	struct sta_info *sta = NULL;
1777ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
1778ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	len = skb->len - IEEE80211_MGMT_HDR_LEN;
1779ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
1780ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	if (len < 2) {
1781ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		printk("handle_deauth - too short payload (len=%d)\n", len);
1782ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		return;
1783ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	}
1784ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
17858a9faf3cd08b91aca1502dbe18e3b5063fda2e87Al Viro	pos = (__le16 *) body;
17868a9faf3cd08b91aca1502dbe18e3b5063fda2e87Al Viro	reason_code = le16_to_cpu(*pos);
1787ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
1788e174961ca1a0b28f7abf0be47973ad57cb74e5f0Johannes Berg	PDEBUG(DEBUG_AP, "%s: deauthentication: %pM len=%d, "
1789e174961ca1a0b28f7abf0be47973ad57cb74e5f0Johannes Berg	       "reason_code=%d\n", dev->name, hdr->addr2,
179021f644f3eabde637f255f75ad05d0821a7a36b7fDavid S. Miller	       len, reason_code);
1791ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
1792ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	spin_lock_bh(&local->ap->sta_table_lock);
1793ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	sta = ap_get_sta(local->ap, hdr->addr2);
1794ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	if (sta != NULL) {
1795ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		if ((sta->flags & WLAN_STA_ASSOC) && !sta->ap)
1796ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			hostap_event_expired_sta(local->dev, sta);
1797ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		sta->flags &= ~(WLAN_STA_AUTH | WLAN_STA_ASSOC);
1798ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	}
1799ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	spin_unlock_bh(&local->ap->sta_table_lock);
1800ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	if (sta == NULL) {
1801e174961ca1a0b28f7abf0be47973ad57cb74e5f0Johannes Berg		printk("%s: deauthentication from %pM, "
1802ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	       "reason_code=%d, but STA not authenticated\n", dev->name,
1803e174961ca1a0b28f7abf0be47973ad57cb74e5f0Johannes Berg		       hdr->addr2, reason_code);
1804ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	}
1805ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen}
1806ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
1807ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
1808ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen/* Called only as a scheduled task for pending AP frames. */
1809ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinenstatic void handle_disassoc(local_info_t *local, struct sk_buff *skb,
1810ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			    struct hostap_80211_rx_status *rx_stats)
1811ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen{
1812ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	struct net_device *dev = local->dev;
18131ea893fde29d8cf1639da8989f4b843dc3283ca8Dan Williams	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
1814ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	char *body = skb->data + IEEE80211_MGMT_HDR_LEN;
1815ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	int len;
18168a9faf3cd08b91aca1502dbe18e3b5063fda2e87Al Viro	u16 reason_code;
18178a9faf3cd08b91aca1502dbe18e3b5063fda2e87Al Viro	__le16 *pos;
1818ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	struct sta_info *sta = NULL;
1819ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
1820ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	len = skb->len - IEEE80211_MGMT_HDR_LEN;
1821ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
1822ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	if (len < 2) {
1823ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		printk("handle_disassoc - too short payload (len=%d)\n", len);
1824ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		return;
1825ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	}
1826ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
18278a9faf3cd08b91aca1502dbe18e3b5063fda2e87Al Viro	pos = (__le16 *) body;
18288a9faf3cd08b91aca1502dbe18e3b5063fda2e87Al Viro	reason_code = le16_to_cpu(*pos);
1829ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
1830e174961ca1a0b28f7abf0be47973ad57cb74e5f0Johannes Berg	PDEBUG(DEBUG_AP, "%s: disassociation: %pM len=%d, "
1831e174961ca1a0b28f7abf0be47973ad57cb74e5f0Johannes Berg	       "reason_code=%d\n", dev->name, hdr->addr2,
183221f644f3eabde637f255f75ad05d0821a7a36b7fDavid S. Miller	       len, reason_code);
1833ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
1834ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	spin_lock_bh(&local->ap->sta_table_lock);
1835ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	sta = ap_get_sta(local->ap, hdr->addr2);
1836ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	if (sta != NULL) {
1837ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		if ((sta->flags & WLAN_STA_ASSOC) && !sta->ap)
1838ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			hostap_event_expired_sta(local->dev, sta);
1839ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		sta->flags &= ~WLAN_STA_ASSOC;
1840ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	}
1841ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	spin_unlock_bh(&local->ap->sta_table_lock);
1842ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	if (sta == NULL) {
1843e174961ca1a0b28f7abf0be47973ad57cb74e5f0Johannes Berg		printk("%s: disassociation from %pM, "
1844ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		       "reason_code=%d, but STA not authenticated\n",
1845e174961ca1a0b28f7abf0be47973ad57cb74e5f0Johannes Berg		       dev->name, hdr->addr2, reason_code);
1846ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	}
1847ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen}
1848ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
1849ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
1850ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen/* Called only as a scheduled task for pending AP frames. */
1851ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinenstatic void ap_handle_data_nullfunc(local_info_t *local,
18521ea893fde29d8cf1639da8989f4b843dc3283ca8Dan Williams				    struct ieee80211_hdr *hdr)
1853ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen{
1854ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	struct net_device *dev = local->dev;
1855ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
1856ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	/* some STA f/w's seem to require control::ACK frame for
1857ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	 * data::nullfunc, but at least Prism2 station f/w version 0.8.0 does
1858ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	 * not send this..
1859ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	 * send control::ACK for the data::nullfunc */
1860ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
1861ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	printk(KERN_DEBUG "Sending control::ACK for data::nullfunc\n");
18624339d328631aa815fe2181b9164b3690ca2db4daJouni Malinen	prism2_send_mgmt(dev, IEEE80211_FTYPE_CTL | IEEE80211_STYPE_ACK,
1863ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			 NULL, 0, hdr->addr2, 0);
1864ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen}
1865ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
1866ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
1867ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen/* Called only as a scheduled task for pending AP frames. */
1868ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinenstatic void ap_handle_dropped_data(local_info_t *local,
18691ea893fde29d8cf1639da8989f4b843dc3283ca8Dan Williams				   struct ieee80211_hdr *hdr)
1870ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen{
1871ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	struct net_device *dev = local->dev;
1872ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	struct sta_info *sta;
18738a9faf3cd08b91aca1502dbe18e3b5063fda2e87Al Viro	__le16 reason;
1874ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
1875ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	spin_lock_bh(&local->ap->sta_table_lock);
1876ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	sta = ap_get_sta(local->ap, hdr->addr2);
1877ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	if (sta)
1878ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		atomic_inc(&sta->users);
1879ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	spin_unlock_bh(&local->ap->sta_table_lock);
1880ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
1881ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	if (sta != NULL && (sta->flags & WLAN_STA_ASSOC)) {
1882ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		PDEBUG(DEBUG_AP, "ap_handle_dropped_data: STA is now okay?\n");
1883ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		atomic_dec(&sta->users);
1884ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		return;
1885ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	}
1886ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
18878a9faf3cd08b91aca1502dbe18e3b5063fda2e87Al Viro	reason = cpu_to_le16(WLAN_REASON_CLASS3_FRAME_FROM_NONASSOC_STA);
18884339d328631aa815fe2181b9164b3690ca2db4daJouni Malinen	prism2_send_mgmt(dev, IEEE80211_FTYPE_MGMT |
1889ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			 ((sta == NULL || !(sta->flags & WLAN_STA_ASSOC)) ?
18904339d328631aa815fe2181b9164b3690ca2db4daJouni Malinen			  IEEE80211_STYPE_DEAUTH : IEEE80211_STYPE_DISASSOC),
1891ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			 (char *) &reason, sizeof(reason), hdr->addr2, 0);
1892ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
1893ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	if (sta)
1894ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		atomic_dec(&sta->users);
1895ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen}
1896ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
1897ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */
1898ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
1899ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
1900ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen/* Called only as a scheduled task for pending AP frames. */
1901ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinenstatic void pspoll_send_buffered(local_info_t *local, struct sta_info *sta,
1902ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen				 struct sk_buff *skb)
1903ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen{
19045bee720fd7fa5ed4eade96058acd3a684da30932Jouni Malinen	struct hostap_skb_tx_data *meta;
19055bee720fd7fa5ed4eade96058acd3a684da30932Jouni Malinen
1906ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	if (!(sta->flags & WLAN_STA_PS)) {
1907ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		/* Station has moved to non-PS mode, so send all buffered
1908ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		 * frames using normal device queue. */
1909ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		dev_queue_xmit(skb);
1910ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		return;
1911ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	}
1912ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
1913ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	/* add a flag for hostap_handle_sta_tx() to know that this skb should
1914ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	 * be passed through even though STA is using PS */
19155bee720fd7fa5ed4eade96058acd3a684da30932Jouni Malinen	meta = (struct hostap_skb_tx_data *) skb->cb;
19165bee720fd7fa5ed4eade96058acd3a684da30932Jouni Malinen	meta->flags |= HOSTAP_TX_FLAGS_BUFFERED_FRAME;
1917ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	if (!skb_queue_empty(&sta->tx_buf)) {
1918ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		/* indicate to STA that more frames follow */
19195bee720fd7fa5ed4eade96058acd3a684da30932Jouni Malinen		meta->flags |= HOSTAP_TX_FLAGS_ADD_MOREDATA;
1920ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	}
1921ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	dev_queue_xmit(skb);
1922ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen}
1923ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
1924ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
1925ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen/* Called only as a scheduled task for pending AP frames. */
1926ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinenstatic void handle_pspoll(local_info_t *local,
19271ea893fde29d8cf1639da8989f4b843dc3283ca8Dan Williams			  struct ieee80211_hdr *hdr,
1928ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			  struct hostap_80211_rx_status *rx_stats)
1929ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen{
1930ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	struct net_device *dev = local->dev;
1931ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	struct sta_info *sta;
1932ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	u16 aid;
1933ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	struct sk_buff *skb;
1934ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
1935e174961ca1a0b28f7abf0be47973ad57cb74e5f0Johannes Berg	PDEBUG(DEBUG_PS2, "handle_pspoll: BSSID=%pM, TA=%pM PWRMGT=%d\n",
19361ea893fde29d8cf1639da8989f4b843dc3283ca8Dan Williams	       hdr->addr1, hdr->addr2, !!ieee80211_has_pm(hdr->frame_control));
1937ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
1938d22fbd70c2770b5e3ba0d7bc5b29f6ee686cdba4dingtianhong	if (!ether_addr_equal(hdr->addr1, dev->dev_addr)) {
1939e174961ca1a0b28f7abf0be47973ad57cb74e5f0Johannes Berg		PDEBUG(DEBUG_AP,
1940e174961ca1a0b28f7abf0be47973ad57cb74e5f0Johannes Berg		       "handle_pspoll - addr1(BSSID)=%pM not own MAC\n",
1941e174961ca1a0b28f7abf0be47973ad57cb74e5f0Johannes Berg		       hdr->addr1);
1942ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		return;
1943ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	}
1944ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
19458a9faf3cd08b91aca1502dbe18e3b5063fda2e87Al Viro	aid = le16_to_cpu(hdr->duration_id);
1946ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	if ((aid & (BIT(15) | BIT(14))) != (BIT(15) | BIT(14))) {
1947ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		PDEBUG(DEBUG_PS, "   PSPOLL and AID[15:14] not set\n");
1948ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		return;
1949ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	}
19501bcca3c463e4930cef9986b05165bb0b3eb46f63Pavel Roskin	aid &= ~(BIT(15) | BIT(14));
1951ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	if (aid == 0 || aid > MAX_AID_TABLE_SIZE) {
1952ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		PDEBUG(DEBUG_PS, "   invalid aid=%d\n", aid);
1953ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		return;
1954ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	}
1955ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	PDEBUG(DEBUG_PS2, "   aid=%d\n", aid);
1956ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
1957ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	spin_lock_bh(&local->ap->sta_table_lock);
1958ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	sta = ap_get_sta(local->ap, hdr->addr2);
1959ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	if (sta)
1960ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		atomic_inc(&sta->users);
1961ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	spin_unlock_bh(&local->ap->sta_table_lock);
1962ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
1963ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	if (sta == NULL) {
1964ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		PDEBUG(DEBUG_PS, "   STA not found\n");
1965ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		return;
1966ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	}
1967ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	if (sta->aid != aid) {
1968ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		PDEBUG(DEBUG_PS, "   received aid=%i does not match with "
1969ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		       "assoc.aid=%d\n", aid, sta->aid);
1970ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		return;
1971ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	}
1972ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
1973ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	/* FIX: todo:
1974ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	 * - add timeout for buffering (clear aid in TIM vector if buffer timed
1975ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	 *   out (expiry time must be longer than ListenInterval for
1976ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	 *   the corresponding STA; "8802-11: 11.2.1.9 AP aging function"
1977ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	 * - what to do, if buffered, pspolled, and sent frame is not ACKed by
1978ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	 *   sta; store buffer for later use and leave TIM aid bit set? use
1979ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	 *   TX event to check whether frame was ACKed?
1980ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	 */
1981ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
1982ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	while ((skb = skb_dequeue(&sta->tx_buf)) != NULL) {
1983ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		/* send buffered frame .. */
1984ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		PDEBUG(DEBUG_PS2, "Sending buffered frame to STA after PS POLL"
1985ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		       " (buffer_count=%d)\n", skb_queue_len(&sta->tx_buf));
1986ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
1987ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		pspoll_send_buffered(local, sta, skb);
1988ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
1989ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		if (sta->flags & WLAN_STA_PS) {
1990ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			/* send only one buffered packet per PS Poll */
1991ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			/* FIX: should ignore further PS Polls until the
1992ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			 * buffered packet that was just sent is acknowledged
1993ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			 * (Tx or TxExc event) */
1994ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			break;
1995ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		}
1996ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	}
1997ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
1998ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	if (skb_queue_empty(&sta->tx_buf)) {
1999ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		/* try to clear aid from TIM */
2000ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		if (!(sta->flags & WLAN_STA_TIM))
2001ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			PDEBUG(DEBUG_PS2,  "Re-unsetting TIM for aid %d\n",
2002ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			       aid);
2003ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		hostap_set_tim(local, aid, 0);
2004ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		sta->flags &= ~WLAN_STA_TIM;
2005ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	}
2006ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
2007ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	atomic_dec(&sta->users);
2008ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen}
2009ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
2010ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
2011ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT
2012ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
2013c4028958b6ecad064b1a6303a6a5906d4fe48d73David Howellsstatic void handle_wds_oper_queue(struct work_struct *work)
2014ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen{
2015c4028958b6ecad064b1a6303a6a5906d4fe48d73David Howells	struct ap_data *ap = container_of(work, struct ap_data,
2016c4028958b6ecad064b1a6303a6a5906d4fe48d73David Howells					  wds_oper_queue);
2017c4028958b6ecad064b1a6303a6a5906d4fe48d73David Howells	local_info_t *local = ap->local;
2018ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	struct wds_oper_data *entry, *prev;
2019ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
2020ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	spin_lock_bh(&local->lock);
2021ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	entry = local->ap->wds_oper_entries;
2022ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	local->ap->wds_oper_entries = NULL;
2023ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	spin_unlock_bh(&local->lock);
2024ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
2025ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	while (entry) {
2026ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		PDEBUG(DEBUG_AP, "%s: %s automatic WDS connection "
2027e174961ca1a0b28f7abf0be47973ad57cb74e5f0Johannes Berg		       "to AP %pM\n",
2028ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		       local->dev->name,
2029ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		       entry->type == WDS_ADD ? "adding" : "removing",
2030e174961ca1a0b28f7abf0be47973ad57cb74e5f0Johannes Berg		       entry->addr);
2031ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		if (entry->type == WDS_ADD)
2032ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			prism2_wds_add(local, entry->addr, 0);
2033ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		else if (entry->type == WDS_DEL)
2034ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			prism2_wds_del(local, entry->addr, 0, 1);
2035ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
2036ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		prev = entry;
2037ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		entry = entry->next;
2038ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		kfree(prev);
2039ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	}
2040ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen}
2041ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
2042ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
2043ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen/* Called only as a scheduled task for pending AP frames. */
2044ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinenstatic void handle_beacon(local_info_t *local, struct sk_buff *skb,
2045ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			  struct hostap_80211_rx_status *rx_stats)
2046ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen{
20471ea893fde29d8cf1639da8989f4b843dc3283ca8Dan Williams	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
2048ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	char *body = skb->data + IEEE80211_MGMT_HDR_LEN;
2049ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	int len, left;
20508a9faf3cd08b91aca1502dbe18e3b5063fda2e87Al Viro	u16 beacon_int, capability;
20518a9faf3cd08b91aca1502dbe18e3b5063fda2e87Al Viro	__le16 *pos;
2052ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	char *ssid = NULL;
2053ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	unsigned char *supp_rates = NULL;
2054ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	int ssid_len = 0, supp_rates_len = 0;
2055ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	struct sta_info *sta = NULL;
2056ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	int new_sta = 0, channel = -1;
2057ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
2058ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	len = skb->len - IEEE80211_MGMT_HDR_LEN;
2059ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
2060ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	if (len < 8 + 2 + 2) {
2061ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		printk(KERN_DEBUG "handle_beacon - too short payload "
2062ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		       "(len=%d)\n", len);
2063ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		return;
2064ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	}
2065ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
20668a9faf3cd08b91aca1502dbe18e3b5063fda2e87Al Viro	pos = (__le16 *) body;
2067ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	left = len;
2068ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
2069ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	/* Timestamp (8 octets) */
2070ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	pos += 4; left -= 8;
2071ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	/* Beacon interval (2 octets) */
20728a9faf3cd08b91aca1502dbe18e3b5063fda2e87Al Viro	beacon_int = le16_to_cpu(*pos);
2073ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	pos++; left -= 2;
2074ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	/* Capability information (2 octets) */
20758a9faf3cd08b91aca1502dbe18e3b5063fda2e87Al Viro	capability = le16_to_cpu(*pos);
2076ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	pos++; left -= 2;
2077ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
2078ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	if (local->ap->ap_policy != AP_OTHER_AP_EVEN_IBSS &&
2079ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	    capability & WLAN_CAPABILITY_IBSS)
2080ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		return;
2081ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
2082ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	if (left >= 2) {
2083ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		unsigned int ileft;
2084ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		unsigned char *u = (unsigned char *) pos;
2085ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
2086ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		if (*u == WLAN_EID_SSID) {
2087ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			u++; left--;
2088ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			ileft = *u;
2089ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			u++; left--;
2090ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
2091ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			if (ileft > left || ileft > MAX_SSID_LEN) {
2092ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen				PDEBUG(DEBUG_AP, "SSID: overflow\n");
2093ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen				return;
2094ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			}
2095ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
2096ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			if (local->ap->ap_policy == AP_OTHER_AP_SAME_SSID &&
2097ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			    (ileft != strlen(local->essid) ||
2098ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			     memcmp(local->essid, u, ileft) != 0)) {
2099ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen				/* not our SSID */
2100ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen				return;
2101ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			}
2102ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
2103ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			ssid = u;
2104ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			ssid_len = ileft;
2105ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
2106ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			u += ileft;
2107ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			left -= ileft;
2108ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		}
2109ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
2110ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		if (*u == WLAN_EID_SUPP_RATES) {
2111ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			u++; left--;
2112ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			ileft = *u;
2113ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			u++; left--;
211474fae82c8bd5dd78365abe25506a9ba388d4a889Jeff Garzik
2115ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			if (ileft > left || ileft == 0 || ileft > 8) {
2116ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen				PDEBUG(DEBUG_AP, " - SUPP_RATES len error\n");
2117ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen				return;
2118ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			}
2119ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
2120ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			supp_rates = u;
2121ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			supp_rates_len = ileft;
2122ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
2123ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			u += ileft;
2124ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			left -= ileft;
2125ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		}
2126ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
2127ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		if (*u == WLAN_EID_DS_PARAMS) {
2128ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			u++; left--;
2129ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			ileft = *u;
2130ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			u++; left--;
213174fae82c8bd5dd78365abe25506a9ba388d4a889Jeff Garzik
2132ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			if (ileft > left || ileft != 1) {
2133ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen				PDEBUG(DEBUG_AP, " - DS_PARAMS len error\n");
2134ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen				return;
2135ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			}
2136ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
2137ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			channel = *u;
2138ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
2139ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			u += ileft;
2140ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			left -= ileft;
2141ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		}
2142ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	}
2143ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
2144ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	spin_lock_bh(&local->ap->sta_table_lock);
2145ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	sta = ap_get_sta(local->ap, hdr->addr2);
2146ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	if (sta != NULL)
2147ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		atomic_inc(&sta->users);
2148ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	spin_unlock_bh(&local->ap->sta_table_lock);
2149ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
2150ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	if (sta == NULL) {
2151ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		/* add new AP */
2152ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		new_sta = 1;
2153ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		sta = ap_add_sta(local->ap, hdr->addr2);
2154ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		if (sta == NULL) {
2155ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			printk(KERN_INFO "prism2: kmalloc failed for AP "
2156ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			       "data structure\n");
2157ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			return;
2158ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		}
2159ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		hostap_event_new_sta(local->dev, sta);
2160ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
2161ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		/* mark APs authentication and associated for pseudo ad-hoc
2162ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		 * style communication */
2163ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		sta->flags = WLAN_STA_AUTH | WLAN_STA_ASSOC;
2164ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
2165ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		if (local->ap->autom_ap_wds) {
2166ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			hostap_wds_link_oper(local, sta->addr, WDS_ADD);
2167ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		}
2168ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	}
2169ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
2170ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	sta->ap = 1;
2171ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	if (ssid) {
2172ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		sta->u.ap.ssid_len = ssid_len;
2173ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		memcpy(sta->u.ap.ssid, ssid, ssid_len);
2174ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		sta->u.ap.ssid[ssid_len] = '\0';
2175ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	} else {
2176ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		sta->u.ap.ssid_len = 0;
2177ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		sta->u.ap.ssid[0] = '\0';
2178ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	}
2179ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	sta->u.ap.channel = channel;
2180ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	sta->rx_packets++;
2181ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	sta->rx_bytes += len;
2182ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	sta->u.ap.last_beacon = sta->last_rx = jiffies;
2183ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	sta->capability = capability;
2184ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	sta->listen_interval = beacon_int;
2185ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
2186ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	atomic_dec(&sta->users);
2187ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
2188ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	if (new_sta) {
2189ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		memset(sta->supported_rates, 0, sizeof(sta->supported_rates));
2190ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		memcpy(sta->supported_rates, supp_rates, supp_rates_len);
2191ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		prism2_check_tx_rates(sta);
2192ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	}
2193ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen}
2194ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
2195ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */
2196ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
2197ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
2198ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen/* Called only as a tasklet. */
2199ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinenstatic void handle_ap_item(local_info_t *local, struct sk_buff *skb,
2200ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			   struct hostap_80211_rx_status *rx_stats)
2201ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen{
2202ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT
2203ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	struct net_device *dev = local->dev;
2204ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */
2205ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	u16 fc, type, stype;
22061ea893fde29d8cf1639da8989f4b843dc3283ca8Dan Williams	struct ieee80211_hdr *hdr;
2207ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
2208ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	/* FIX: should give skb->len to handler functions and check that the
2209ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	 * buffer is long enough */
22101ea893fde29d8cf1639da8989f4b843dc3283ca8Dan Williams	hdr = (struct ieee80211_hdr *) skb->data;
22111ea893fde29d8cf1639da8989f4b843dc3283ca8Dan Williams	fc = le16_to_cpu(hdr->frame_control);
22121ea893fde29d8cf1639da8989f4b843dc3283ca8Dan Williams	type = fc & IEEE80211_FCTL_FTYPE;
22131ea893fde29d8cf1639da8989f4b843dc3283ca8Dan Williams	stype = fc & IEEE80211_FCTL_STYPE;
2214ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
2215ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT
22164339d328631aa815fe2181b9164b3690ca2db4daJouni Malinen	if (!local->hostapd && type == IEEE80211_FTYPE_DATA) {
2217ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		PDEBUG(DEBUG_AP, "handle_ap_item - data frame\n");
2218ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
2219b2f4a2e3b1620667da9654f9e220c92ea52bdbe4Jouni Malinen		if (!(fc & IEEE80211_FCTL_TODS) ||
2220b2f4a2e3b1620667da9654f9e220c92ea52bdbe4Jouni Malinen		    (fc & IEEE80211_FCTL_FROMDS)) {
22214339d328631aa815fe2181b9164b3690ca2db4daJouni Malinen			if (stype == IEEE80211_STYPE_NULLFUNC) {
2222ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen				/* no ToDS nullfunc seems to be used to check
2223ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen				 * AP association; so send reject message to
2224ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen				 * speed up re-association */
2225ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen				ap_handle_dropped_data(local, hdr);
2226ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen				goto done;
2227ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			}
2228ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			PDEBUG(DEBUG_AP, "   not ToDS frame (fc=0x%04x)\n",
2229ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			       fc);
2230ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			goto done;
2231ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		}
2232ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
2233d22fbd70c2770b5e3ba0d7bc5b29f6ee686cdba4dingtianhong		if (!ether_addr_equal(hdr->addr1, dev->dev_addr)) {
2234e174961ca1a0b28f7abf0be47973ad57cb74e5f0Johannes Berg			PDEBUG(DEBUG_AP, "handle_ap_item - addr1(BSSID)=%pM"
2235e174961ca1a0b28f7abf0be47973ad57cb74e5f0Johannes Berg			       " not own MAC\n", hdr->addr1);
2236ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			goto done;
2237ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		}
2238ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
22394339d328631aa815fe2181b9164b3690ca2db4daJouni Malinen		if (local->ap->nullfunc_ack &&
22404339d328631aa815fe2181b9164b3690ca2db4daJouni Malinen		    stype == IEEE80211_STYPE_NULLFUNC)
2241ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			ap_handle_data_nullfunc(local, hdr);
2242ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		else
2243ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			ap_handle_dropped_data(local, hdr);
2244ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		goto done;
2245ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	}
2246ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
22474339d328631aa815fe2181b9164b3690ca2db4daJouni Malinen	if (type == IEEE80211_FTYPE_MGMT && stype == IEEE80211_STYPE_BEACON) {
2248ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		handle_beacon(local, skb, rx_stats);
2249ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		goto done;
2250ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	}
2251ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */
2252ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
22534339d328631aa815fe2181b9164b3690ca2db4daJouni Malinen	if (type == IEEE80211_FTYPE_CTL && stype == IEEE80211_STYPE_PSPOLL) {
2254ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		handle_pspoll(local, hdr, rx_stats);
2255ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		goto done;
2256ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	}
2257ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
2258ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	if (local->hostapd) {
2259ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		PDEBUG(DEBUG_AP, "Unknown frame in AP queue: type=0x%02x "
2260ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		       "subtype=0x%02x\n", type, stype);
2261ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		goto done;
2262ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	}
2263ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
2264ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT
22654339d328631aa815fe2181b9164b3690ca2db4daJouni Malinen	if (type != IEEE80211_FTYPE_MGMT) {
2266ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		PDEBUG(DEBUG_AP, "handle_ap_item - not a management frame?\n");
2267ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		goto done;
2268ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	}
2269ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
2270d22fbd70c2770b5e3ba0d7bc5b29f6ee686cdba4dingtianhong	if (!ether_addr_equal(hdr->addr1, dev->dev_addr)) {
2271e174961ca1a0b28f7abf0be47973ad57cb74e5f0Johannes Berg		PDEBUG(DEBUG_AP, "handle_ap_item - addr1(DA)=%pM"
2272e174961ca1a0b28f7abf0be47973ad57cb74e5f0Johannes Berg		       " not own MAC\n", hdr->addr1);
2273ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		goto done;
2274ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	}
2275ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
2276d22fbd70c2770b5e3ba0d7bc5b29f6ee686cdba4dingtianhong	if (!ether_addr_equal(hdr->addr3, dev->dev_addr)) {
2277e174961ca1a0b28f7abf0be47973ad57cb74e5f0Johannes Berg		PDEBUG(DEBUG_AP, "handle_ap_item - addr3(BSSID)=%pM"
2278e174961ca1a0b28f7abf0be47973ad57cb74e5f0Johannes Berg		       " not own MAC\n", hdr->addr3);
2279ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		goto done;
2280ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	}
2281ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
2282ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	switch (stype) {
22834339d328631aa815fe2181b9164b3690ca2db4daJouni Malinen	case IEEE80211_STYPE_ASSOC_REQ:
2284ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		handle_assoc(local, skb, rx_stats, 0);
2285ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		break;
22864339d328631aa815fe2181b9164b3690ca2db4daJouni Malinen	case IEEE80211_STYPE_ASSOC_RESP:
2287ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		PDEBUG(DEBUG_AP, "==> ASSOC RESP (ignored)\n");
2288ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		break;
22894339d328631aa815fe2181b9164b3690ca2db4daJouni Malinen	case IEEE80211_STYPE_REASSOC_REQ:
2290ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		handle_assoc(local, skb, rx_stats, 1);
2291ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		break;
22924339d328631aa815fe2181b9164b3690ca2db4daJouni Malinen	case IEEE80211_STYPE_REASSOC_RESP:
2293ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		PDEBUG(DEBUG_AP, "==> REASSOC RESP (ignored)\n");
2294ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		break;
22954339d328631aa815fe2181b9164b3690ca2db4daJouni Malinen	case IEEE80211_STYPE_ATIM:
2296ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		PDEBUG(DEBUG_AP, "==> ATIM (ignored)\n");
2297ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		break;
22984339d328631aa815fe2181b9164b3690ca2db4daJouni Malinen	case IEEE80211_STYPE_DISASSOC:
2299ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		handle_disassoc(local, skb, rx_stats);
2300ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		break;
23014339d328631aa815fe2181b9164b3690ca2db4daJouni Malinen	case IEEE80211_STYPE_AUTH:
2302ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		handle_authen(local, skb, rx_stats);
2303ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		break;
23044339d328631aa815fe2181b9164b3690ca2db4daJouni Malinen	case IEEE80211_STYPE_DEAUTH:
2305ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		handle_deauth(local, skb, rx_stats);
2306ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		break;
2307ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	default:
23084339d328631aa815fe2181b9164b3690ca2db4daJouni Malinen		PDEBUG(DEBUG_AP, "Unknown mgmt frame subtype 0x%02x\n",
23094339d328631aa815fe2181b9164b3690ca2db4daJouni Malinen		       stype >> 4);
2310ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		break;
2311ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	}
2312ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */
2313ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
2314ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen done:
2315ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	dev_kfree_skb(skb);
2316ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen}
2317ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
2318ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
2319ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen/* Called only as a tasklet (software IRQ) */
2320ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinenvoid hostap_rx(struct net_device *dev, struct sk_buff *skb,
2321ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	       struct hostap_80211_rx_status *rx_stats)
2322ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen{
2323ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	struct hostap_interface *iface;
2324ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	local_info_t *local;
23251ea893fde29d8cf1639da8989f4b843dc3283ca8Dan Williams	struct ieee80211_hdr *hdr;
2326ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
2327ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	iface = netdev_priv(dev);
2328ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	local = iface->local;
2329ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
2330ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	if (skb->len < 16)
2331ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		goto drop;
2332ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
23334cfa8e45f4bb26ff38155f94a810a876b739958dStephen Hemminger	dev->stats.rx_packets++;
2334ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
23351ea893fde29d8cf1639da8989f4b843dc3283ca8Dan Williams	hdr = (struct ieee80211_hdr *) skb->data;
2336ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
2337ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	if (local->ap->ap_policy == AP_OTHER_AP_SKIP_ALL &&
23381ea893fde29d8cf1639da8989f4b843dc3283ca8Dan Williams	    ieee80211_is_beacon(hdr->frame_control))
2339ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		goto drop;
2340ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
2341c1b4aa3fb619782213af2af6652663c8f9cef373Harvey Harrison	skb->protocol = cpu_to_be16(ETH_P_HOSTAP);
2342ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	handle_ap_item(local, skb, rx_stats);
2343ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	return;
2344ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
2345ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen drop:
2346ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	dev_kfree_skb(skb);
2347ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen}
2348ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
2349ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
2350ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen/* Called only as a tasklet (software IRQ) */
2351ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinenstatic void schedule_packet_send(local_info_t *local, struct sta_info *sta)
2352ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen{
2353ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	struct sk_buff *skb;
23541ea893fde29d8cf1639da8989f4b843dc3283ca8Dan Williams	struct ieee80211_hdr *hdr;
2355ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	struct hostap_80211_rx_status rx_stats;
2356ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
2357ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	if (skb_queue_empty(&sta->tx_buf))
2358ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		return;
2359ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
2360ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	skb = dev_alloc_skb(16);
2361ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	if (skb == NULL) {
2362ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		printk(KERN_DEBUG "%s: schedule_packet_send: skb alloc "
2363ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		       "failed\n", local->dev->name);
2364ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		return;
2365ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	}
2366ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
23671ea893fde29d8cf1639da8989f4b843dc3283ca8Dan Williams	hdr = (struct ieee80211_hdr *) skb_put(skb, 16);
2368ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
2369ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	/* Generate a fake pspoll frame to start packet delivery */
23701ea893fde29d8cf1639da8989f4b843dc3283ca8Dan Williams	hdr->frame_control = cpu_to_le16(
23714339d328631aa815fe2181b9164b3690ca2db4daJouni Malinen		IEEE80211_FTYPE_CTL | IEEE80211_STYPE_PSPOLL);
2372ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	memcpy(hdr->addr1, local->dev->dev_addr, ETH_ALEN);
2373ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	memcpy(hdr->addr2, sta->addr, ETH_ALEN);
2374ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	hdr->duration_id = cpu_to_le16(sta->aid | BIT(15) | BIT(14));
2375ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
2376e174961ca1a0b28f7abf0be47973ad57cb74e5f0Johannes Berg	PDEBUG(DEBUG_PS2,
2377e174961ca1a0b28f7abf0be47973ad57cb74e5f0Johannes Berg	       "%s: Scheduling buffered packet delivery for STA %pM\n",
2378e174961ca1a0b28f7abf0be47973ad57cb74e5f0Johannes Berg	       local->dev->name, sta->addr);
2379ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
2380ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	skb->dev = local->dev;
2381ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
2382ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	memset(&rx_stats, 0, sizeof(rx_stats));
2383ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	hostap_rx(local->dev, skb, &rx_stats);
2384ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen}
2385ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
2386ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
23875fad5a2e1f34b333a801b749c4e143c2ac3e8a4fAdrian Bunkint prism2_ap_get_sta_qual(local_info_t *local, struct sockaddr addr[],
23885fad5a2e1f34b333a801b749c4e143c2ac3e8a4fAdrian Bunk			   struct iw_quality qual[], int buf_size,
23895fad5a2e1f34b333a801b749c4e143c2ac3e8a4fAdrian Bunk			   int aplist)
2390ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen{
2391ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	struct ap_data *ap = local->ap;
2392ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	struct list_head *ptr;
2393ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	int count = 0;
2394ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
2395ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	spin_lock_bh(&ap->sta_table_lock);
2396ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
2397ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	for (ptr = ap->sta_list.next; ptr != NULL && ptr != &ap->sta_list;
2398ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	     ptr = ptr->next) {
2399ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		struct sta_info *sta = (struct sta_info *) ptr;
2400ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
2401ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		if (aplist && !sta->ap)
2402ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			continue;
2403ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		addr[count].sa_family = ARPHRD_ETHER;
2404ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		memcpy(addr[count].sa_data, sta->addr, ETH_ALEN);
2405ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		if (sta->last_rx_silence == 0)
2406ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			qual[count].qual = sta->last_rx_signal < 27 ?
2407ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen				0 : (sta->last_rx_signal - 27) * 92 / 127;
2408ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		else
2409ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			qual[count].qual = sta->last_rx_signal -
2410ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen				sta->last_rx_silence - 35;
2411ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		qual[count].level = HFA384X_LEVEL_TO_dBm(sta->last_rx_signal);
2412ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		qual[count].noise = HFA384X_LEVEL_TO_dBm(sta->last_rx_silence);
2413ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		qual[count].updated = sta->last_rx_updated;
2414ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
2415c28df16ed70d1b6cefd12135e3c68bfccd1bb635Jean Tourrilhes		sta->last_rx_updated = IW_QUAL_DBM;
2416ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
2417ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		count++;
2418ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		if (count >= buf_size)
2419ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			break;
2420ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	}
2421ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	spin_unlock_bh(&ap->sta_table_lock);
2422ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
2423ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	return count;
2424ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen}
2425ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
2426ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
242725985edcedea6396277003854657b5f3cb31a628Lucas De Marchi/* Translate our list of Access Points & Stations to a card independent
2428ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen * format that the Wireless Tools will understand - Jean II */
2429ccc580571cf0799d0460a085a7632b77753f083eDavid S. Millerint prism2_ap_translate_scan(struct net_device *dev,
2430ccc580571cf0799d0460a085a7632b77753f083eDavid S. Miller			     struct iw_request_info *info, char *buffer)
2431ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen{
2432ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	struct hostap_interface *iface;
2433ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	local_info_t *local;
2434ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	struct ap_data *ap;
2435ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	struct list_head *ptr;
2436ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	struct iw_event iwe;
2437ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	char *current_ev = buffer;
2438ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	char *end_buf = buffer + IW_SCAN_MAX_DATA;
2439ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen#if !defined(PRISM2_NO_KERNEL_IEEE80211_MGMT)
2440ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	char buf[64];
2441ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen#endif
2442ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
2443ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	iface = netdev_priv(dev);
2444ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	local = iface->local;
2445ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	ap = local->ap;
2446ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
2447ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	spin_lock_bh(&ap->sta_table_lock);
2448ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
2449ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	for (ptr = ap->sta_list.next; ptr != NULL && ptr != &ap->sta_list;
2450ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	     ptr = ptr->next) {
2451ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		struct sta_info *sta = (struct sta_info *) ptr;
2452ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
2453ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		/* First entry *MUST* be the AP MAC address */
2454ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		memset(&iwe, 0, sizeof(iwe));
2455ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		iwe.cmd = SIOCGIWAP;
2456ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		iwe.u.ap_addr.sa_family = ARPHRD_ETHER;
2457ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		memcpy(iwe.u.ap_addr.sa_data, sta->addr, ETH_ALEN);
2458ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		iwe.len = IW_EV_ADDR_LEN;
2459ccc580571cf0799d0460a085a7632b77753f083eDavid S. Miller		current_ev = iwe_stream_add_event(info, current_ev, end_buf,
2460ccc580571cf0799d0460a085a7632b77753f083eDavid S. Miller						  &iwe, IW_EV_ADDR_LEN);
2461ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
2462ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		/* Use the mode to indicate if it's a station or
2463ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		 * an Access Point */
2464ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		memset(&iwe, 0, sizeof(iwe));
2465ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		iwe.cmd = SIOCGIWMODE;
2466ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		if (sta->ap)
2467ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			iwe.u.mode = IW_MODE_MASTER;
2468ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		else
2469ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			iwe.u.mode = IW_MODE_INFRA;
2470ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		iwe.len = IW_EV_UINT_LEN;
2471ccc580571cf0799d0460a085a7632b77753f083eDavid S. Miller		current_ev = iwe_stream_add_event(info, current_ev, end_buf,
2472ccc580571cf0799d0460a085a7632b77753f083eDavid S. Miller						  &iwe, IW_EV_UINT_LEN);
2473ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
2474ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		/* Some quality */
2475ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		memset(&iwe, 0, sizeof(iwe));
2476ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		iwe.cmd = IWEVQUAL;
2477ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		if (sta->last_rx_silence == 0)
2478ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			iwe.u.qual.qual = sta->last_rx_signal < 27 ?
2479ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen				0 : (sta->last_rx_signal - 27) * 92 / 127;
2480ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		else
2481ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			iwe.u.qual.qual = sta->last_rx_signal -
2482ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen				sta->last_rx_silence - 35;
2483ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		iwe.u.qual.level = HFA384X_LEVEL_TO_dBm(sta->last_rx_signal);
2484ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		iwe.u.qual.noise = HFA384X_LEVEL_TO_dBm(sta->last_rx_silence);
2485ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		iwe.u.qual.updated = sta->last_rx_updated;
2486ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		iwe.len = IW_EV_QUAL_LEN;
2487ccc580571cf0799d0460a085a7632b77753f083eDavid S. Miller		current_ev = iwe_stream_add_event(info, current_ev, end_buf,
2488ccc580571cf0799d0460a085a7632b77753f083eDavid S. Miller						  &iwe, IW_EV_QUAL_LEN);
2489ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
2490ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT
2491ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		if (sta->ap) {
2492ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			memset(&iwe, 0, sizeof(iwe));
2493ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			iwe.cmd = SIOCGIWESSID;
2494ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			iwe.u.data.length = sta->u.ap.ssid_len;
2495ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			iwe.u.data.flags = 1;
2496ccc580571cf0799d0460a085a7632b77753f083eDavid S. Miller			current_ev = iwe_stream_add_point(info, current_ev,
2497ccc580571cf0799d0460a085a7632b77753f083eDavid S. Miller							  end_buf, &iwe,
2498ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen							  sta->u.ap.ssid);
2499ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
2500ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			memset(&iwe, 0, sizeof(iwe));
2501ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			iwe.cmd = SIOCGIWENCODE;
2502ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			if (sta->capability & WLAN_CAPABILITY_PRIVACY)
2503ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen				iwe.u.data.flags =
2504ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen					IW_ENCODE_ENABLED | IW_ENCODE_NOKEY;
2505ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			else
2506ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen				iwe.u.data.flags = IW_ENCODE_DISABLED;
2507ccc580571cf0799d0460a085a7632b77753f083eDavid S. Miller			current_ev = iwe_stream_add_point(info, current_ev,
2508ccc580571cf0799d0460a085a7632b77753f083eDavid S. Miller							  end_buf, &iwe,
2509ccc580571cf0799d0460a085a7632b77753f083eDavid S. Miller							  sta->u.ap.ssid);
2510ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
2511ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			if (sta->u.ap.channel > 0 &&
2512ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			    sta->u.ap.channel <= FREQ_COUNT) {
2513ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen				memset(&iwe, 0, sizeof(iwe));
2514ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen				iwe.cmd = SIOCGIWFREQ;
2515ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen				iwe.u.freq.m = freq_list[sta->u.ap.channel - 1]
2516ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen					* 100000;
2517ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen				iwe.u.freq.e = 1;
2518ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen				current_ev = iwe_stream_add_event(
2519ccc580571cf0799d0460a085a7632b77753f083eDavid S. Miller					info, current_ev, end_buf, &iwe,
2520ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen					IW_EV_FREQ_LEN);
2521ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			}
2522ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
2523ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			memset(&iwe, 0, sizeof(iwe));
2524ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			iwe.cmd = IWEVCUSTOM;
2525ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			sprintf(buf, "beacon_interval=%d",
2526ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen				sta->listen_interval);
2527ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			iwe.u.data.length = strlen(buf);
2528ccc580571cf0799d0460a085a7632b77753f083eDavid S. Miller			current_ev = iwe_stream_add_point(info, current_ev,
2529ccc580571cf0799d0460a085a7632b77753f083eDavid S. Miller							  end_buf, &iwe, buf);
2530ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		}
2531ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */
2532ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
2533c28df16ed70d1b6cefd12135e3c68bfccd1bb635Jean Tourrilhes		sta->last_rx_updated = IW_QUAL_DBM;
2534ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
2535ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		/* To be continued, we should make good use of IWEVCUSTOM */
2536ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	}
2537ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
2538ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	spin_unlock_bh(&ap->sta_table_lock);
2539ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
2540ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	return current_ev - buffer;
2541ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen}
2542ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
2543ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
2544ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinenstatic int prism2_hostapd_add_sta(struct ap_data *ap,
2545ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen				  struct prism2_hostapd_param *param)
2546ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen{
2547ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	struct sta_info *sta;
2548ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
2549ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	spin_lock_bh(&ap->sta_table_lock);
2550ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	sta = ap_get_sta(ap, param->sta_addr);
2551ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	if (sta)
2552ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		atomic_inc(&sta->users);
2553ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	spin_unlock_bh(&ap->sta_table_lock);
2554ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
2555ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	if (sta == NULL) {
2556ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		sta = ap_add_sta(ap, param->sta_addr);
2557ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		if (sta == NULL)
2558ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			return -1;
2559ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	}
2560ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
2561ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	if (!(sta->flags & WLAN_STA_ASSOC) && !sta->ap && sta->local)
2562ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		hostap_event_new_sta(sta->local->dev, sta);
2563ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
2564ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	sta->flags |= WLAN_STA_AUTH | WLAN_STA_ASSOC;
2565ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	sta->last_rx = jiffies;
2566ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	sta->aid = param->u.add_sta.aid;
2567ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	sta->capability = param->u.add_sta.capability;
2568ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	sta->tx_supp_rates = param->u.add_sta.tx_supp_rates;
2569ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	if (sta->tx_supp_rates & WLAN_RATE_1M)
2570ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		sta->supported_rates[0] = 2;
2571ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	if (sta->tx_supp_rates & WLAN_RATE_2M)
2572ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		sta->supported_rates[1] = 4;
2573ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen 	if (sta->tx_supp_rates & WLAN_RATE_5M5)
2574ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		sta->supported_rates[2] = 11;
2575ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	if (sta->tx_supp_rates & WLAN_RATE_11M)
2576ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		sta->supported_rates[3] = 22;
2577ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	prism2_check_tx_rates(sta);
2578ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	atomic_dec(&sta->users);
2579ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	return 0;
2580ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen}
2581ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
2582ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
2583ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinenstatic int prism2_hostapd_remove_sta(struct ap_data *ap,
2584ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen				     struct prism2_hostapd_param *param)
2585ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen{
2586ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	struct sta_info *sta;
2587ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
2588ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	spin_lock_bh(&ap->sta_table_lock);
2589ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	sta = ap_get_sta(ap, param->sta_addr);
2590ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	if (sta) {
2591ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		ap_sta_hash_del(ap, sta);
2592ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		list_del(&sta->list);
2593ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	}
2594ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	spin_unlock_bh(&ap->sta_table_lock);
2595ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
2596ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	if (!sta)
2597ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		return -ENOENT;
2598ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
2599ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	if ((sta->flags & WLAN_STA_ASSOC) && !sta->ap && sta->local)
2600ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		hostap_event_expired_sta(sta->local->dev, sta);
2601ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	ap_free_sta(ap, sta);
2602ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
2603ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	return 0;
2604ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen}
2605ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
2606ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
2607ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinenstatic int prism2_hostapd_get_info_sta(struct ap_data *ap,
2608ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen				       struct prism2_hostapd_param *param)
2609ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen{
2610ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	struct sta_info *sta;
2611ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
2612ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	spin_lock_bh(&ap->sta_table_lock);
2613ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	sta = ap_get_sta(ap, param->sta_addr);
2614ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	if (sta)
2615ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		atomic_inc(&sta->users);
2616ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	spin_unlock_bh(&ap->sta_table_lock);
2617ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
2618ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	if (!sta)
2619ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		return -ENOENT;
2620ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
2621ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	param->u.get_info_sta.inactive_sec = (jiffies - sta->last_rx) / HZ;
2622ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
2623ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	atomic_dec(&sta->users);
2624ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
2625ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	return 1;
2626ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen}
2627ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
2628ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
2629ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinenstatic int prism2_hostapd_set_flags_sta(struct ap_data *ap,
2630ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen					struct prism2_hostapd_param *param)
2631ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen{
2632ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	struct sta_info *sta;
2633ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
2634ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	spin_lock_bh(&ap->sta_table_lock);
2635ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	sta = ap_get_sta(ap, param->sta_addr);
2636ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	if (sta) {
2637ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		sta->flags |= param->u.set_flags_sta.flags_or;
2638ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		sta->flags &= param->u.set_flags_sta.flags_and;
2639ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	}
2640ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	spin_unlock_bh(&ap->sta_table_lock);
2641ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
2642ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	if (!sta)
2643ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		return -ENOENT;
2644ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
2645ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	return 0;
2646ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen}
2647ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
2648ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
2649ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinenstatic int prism2_hostapd_sta_clear_stats(struct ap_data *ap,
2650ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen					  struct prism2_hostapd_param *param)
2651ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen{
2652ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	struct sta_info *sta;
2653ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	int rate;
2654ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
2655ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	spin_lock_bh(&ap->sta_table_lock);
2656ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	sta = ap_get_sta(ap, param->sta_addr);
2657ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	if (sta) {
2658ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		sta->rx_packets = sta->tx_packets = 0;
2659ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		sta->rx_bytes = sta->tx_bytes = 0;
2660ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		for (rate = 0; rate < WLAN_RATE_COUNT; rate++) {
2661ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			sta->tx_count[rate] = 0;
2662ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			sta->rx_count[rate] = 0;
2663ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		}
2664ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	}
2665ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	spin_unlock_bh(&ap->sta_table_lock);
2666ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
2667ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	if (!sta)
2668ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		return -ENOENT;
2669ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
2670ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	return 0;
2671ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen}
2672ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
2673ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
26745fad5a2e1f34b333a801b749c4e143c2ac3e8a4fAdrian Bunkint prism2_hostapd(struct ap_data *ap, struct prism2_hostapd_param *param)
2675ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen{
2676ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	switch (param->cmd) {
2677ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	case PRISM2_HOSTAPD_FLUSH:
2678ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		ap_control_kickall(ap);
2679ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		return 0;
2680ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	case PRISM2_HOSTAPD_ADD_STA:
2681ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		return prism2_hostapd_add_sta(ap, param);
2682ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	case PRISM2_HOSTAPD_REMOVE_STA:
2683ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		return prism2_hostapd_remove_sta(ap, param);
2684ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	case PRISM2_HOSTAPD_GET_INFO_STA:
2685ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		return prism2_hostapd_get_info_sta(ap, param);
2686ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	case PRISM2_HOSTAPD_SET_FLAGS_STA:
2687ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		return prism2_hostapd_set_flags_sta(ap, param);
2688ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	case PRISM2_HOSTAPD_STA_CLEAR_STATS:
2689ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		return prism2_hostapd_sta_clear_stats(ap, param);
2690ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	default:
2691ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		printk(KERN_WARNING "prism2_hostapd: unknown cmd=%d\n",
2692ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		       param->cmd);
2693ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		return -EOPNOTSUPP;
2694ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	}
2695ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen}
2696ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
2697ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
2698ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen/* Update station info for host-based TX rate control and return current
2699ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen * TX rate */
2700ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinenstatic int ap_update_sta_tx_rate(struct sta_info *sta, struct net_device *dev)
2701ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen{
2702ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	int ret = sta->tx_rate;
2703ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	struct hostap_interface *iface;
2704ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	local_info_t *local;
2705ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
2706ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	iface = netdev_priv(dev);
2707ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	local = iface->local;
2708ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
2709ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	sta->tx_count[sta->tx_rate_idx]++;
2710ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	sta->tx_since_last_failure++;
2711ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	sta->tx_consecutive_exc = 0;
2712ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	if (sta->tx_since_last_failure >= WLAN_RATE_UPDATE_COUNT &&
2713ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	    sta->tx_rate_idx < sta->tx_max_rate) {
2714ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		/* use next higher rate */
2715ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		int old_rate, new_rate;
2716ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		old_rate = new_rate = sta->tx_rate_idx;
2717ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		while (new_rate < sta->tx_max_rate) {
2718ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			new_rate++;
2719ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			if (ap_tx_rate_ok(new_rate, sta, local)) {
2720ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen				sta->tx_rate_idx = new_rate;
2721ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen				break;
2722ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			}
2723ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		}
2724ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		if (old_rate != sta->tx_rate_idx) {
2725ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			switch (sta->tx_rate_idx) {
2726ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			case 0: sta->tx_rate = 10; break;
2727ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			case 1: sta->tx_rate = 20; break;
2728ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			case 2: sta->tx_rate = 55; break;
2729ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			case 3: sta->tx_rate = 110; break;
2730ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			default: sta->tx_rate = 0; break;
2731ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			}
2732e174961ca1a0b28f7abf0be47973ad57cb74e5f0Johannes Berg			PDEBUG(DEBUG_AP, "%s: STA %pM TX rate raised to %d\n",
2733e174961ca1a0b28f7abf0be47973ad57cb74e5f0Johannes Berg			       dev->name, sta->addr, sta->tx_rate);
2734ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		}
2735ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		sta->tx_since_last_failure = 0;
2736ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	}
2737ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
2738ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	return ret;
2739ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen}
2740ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
2741ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
2742ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen/* Called only from software IRQ. Called for each TX frame prior possible
2743ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen * encryption and transmit. */
2744ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinenap_tx_ret hostap_handle_sta_tx(local_info_t *local, struct hostap_tx_data *tx)
2745ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen{
2746ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	struct sta_info *sta = NULL;
2747ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	struct sk_buff *skb = tx->skb;
2748ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	int set_tim, ret;
27491ea893fde29d8cf1639da8989f4b843dc3283ca8Dan Williams	struct ieee80211_hdr *hdr;
2750ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	struct hostap_skb_tx_data *meta;
2751ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
2752ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	meta = (struct hostap_skb_tx_data *) skb->cb;
2753ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	ret = AP_TX_CONTINUE;
2754ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	if (local->ap == NULL || skb->len < 10 ||
2755ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	    meta->iface->type == HOSTAP_INTERFACE_STA)
2756ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		goto out;
2757ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
27581ea893fde29d8cf1639da8989f4b843dc3283ca8Dan Williams	hdr = (struct ieee80211_hdr *) skb->data;
2759ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
2760ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	if (hdr->addr1[0] & 0x01) {
2761ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		/* broadcast/multicast frame - no AP related processing */
2762b918099030fe6cc093a7d60a88039bd98f16538ePavel Roskin		if (local->ap->num_sta <= 0)
2763b918099030fe6cc093a7d60a88039bd98f16538ePavel Roskin			ret = AP_TX_DROP;
2764ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		goto out;
2765ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	}
2766ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
2767ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	/* unicast packet - check whether destination STA is associated */
2768ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	spin_lock(&local->ap->sta_table_lock);
2769ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	sta = ap_get_sta(local->ap, hdr->addr1);
2770ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	if (sta)
2771ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		atomic_inc(&sta->users);
2772ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	spin_unlock(&local->ap->sta_table_lock);
2773ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
27745bee720fd7fa5ed4eade96058acd3a684da30932Jouni Malinen	if (local->iw_mode == IW_MODE_MASTER && sta == NULL &&
27755bee720fd7fa5ed4eade96058acd3a684da30932Jouni Malinen	    !(meta->flags & HOSTAP_TX_FLAGS_WDS) &&
2776ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	    meta->iface->type != HOSTAP_INTERFACE_MASTER &&
2777ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	    meta->iface->type != HOSTAP_INTERFACE_AP) {
2778ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen#if 0
2779ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		/* This can happen, e.g., when wlan0 is added to a bridge and
2780ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		 * bridging code does not know which port is the correct target
2781ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		 * for a unicast frame. In this case, the packet is send to all
2782ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		 * ports of the bridge. Since this is a valid scenario, do not
2783ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		 * print out any errors here. */
2784ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		if (net_ratelimit()) {
2785ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			printk(KERN_DEBUG "AP: drop packet to non-associated "
2786e174961ca1a0b28f7abf0be47973ad57cb74e5f0Johannes Berg			       "STA %pM\n", hdr->addr1);
2787ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		}
2788ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen#endif
2789ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		local->ap->tx_drop_nonassoc++;
2790ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		ret = AP_TX_DROP;
2791ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		goto out;
2792ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	}
2793ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
2794ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	if (sta == NULL)
2795ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		goto out;
2796ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
2797ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	if (!(sta->flags & WLAN_STA_AUTHORIZED))
2798ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		ret = AP_TX_CONTINUE_NOT_AUTHORIZED;
2799ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
2800ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	/* Set tx_rate if using host-based TX rate control */
2801ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	if (!local->fw_tx_rate_control)
2802ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		local->ap->last_tx_rate = meta->rate =
2803ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			ap_update_sta_tx_rate(sta, local->dev);
2804ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
2805ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	if (local->iw_mode != IW_MODE_MASTER)
2806ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		goto out;
2807ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
2808ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	if (!(sta->flags & WLAN_STA_PS))
2809ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		goto out;
2810ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
28115bee720fd7fa5ed4eade96058acd3a684da30932Jouni Malinen	if (meta->flags & HOSTAP_TX_FLAGS_ADD_MOREDATA) {
28125bee720fd7fa5ed4eade96058acd3a684da30932Jouni Malinen		/* indicate to STA that more frames follow */
28131ea893fde29d8cf1639da8989f4b843dc3283ca8Dan Williams		hdr->frame_control |=
2814c1b4aa3fb619782213af2af6652663c8f9cef373Harvey Harrison			cpu_to_le16(IEEE80211_FCTL_MOREDATA);
28155bee720fd7fa5ed4eade96058acd3a684da30932Jouni Malinen	}
2816ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
28175bee720fd7fa5ed4eade96058acd3a684da30932Jouni Malinen	if (meta->flags & HOSTAP_TX_FLAGS_BUFFERED_FRAME) {
28185bee720fd7fa5ed4eade96058acd3a684da30932Jouni Malinen		/* packet was already buffered and now send due to
28195bee720fd7fa5ed4eade96058acd3a684da30932Jouni Malinen		 * PS poll, so do not rebuffer it */
28205bee720fd7fa5ed4eade96058acd3a684da30932Jouni Malinen		goto out;
2821ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	}
2822ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
2823ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	if (skb_queue_len(&sta->tx_buf) >= STA_MAX_TX_BUFFER) {
2824e174961ca1a0b28f7abf0be47973ad57cb74e5f0Johannes Berg		PDEBUG(DEBUG_PS, "%s: No more space in STA (%pM)'s"
2825e174961ca1a0b28f7abf0be47973ad57cb74e5f0Johannes Berg		       "PS mode buffer\n",
2826e174961ca1a0b28f7abf0be47973ad57cb74e5f0Johannes Berg		       local->dev->name, sta->addr);
2827ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		/* Make sure that TIM is set for the station (it might not be
2828ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		 * after AP wlan hw reset). */
2829ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		/* FIX: should fix hw reset to restore bits based on STA
2830ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		 * buffer state.. */
2831ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		hostap_set_tim(local, sta->aid, 1);
2832ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		sta->flags |= WLAN_STA_TIM;
2833ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		ret = AP_TX_DROP;
2834ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		goto out;
2835ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	}
2836ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
2837ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	/* STA in PS mode, buffer frame for later delivery */
2838ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	set_tim = skb_queue_empty(&sta->tx_buf);
2839ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	skb_queue_tail(&sta->tx_buf, skb);
2840ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	/* FIX: could save RX time to skb and expire buffered frames after
2841ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	 * some time if STA does not poll for them */
2842ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
2843ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	if (set_tim) {
2844ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		if (sta->flags & WLAN_STA_TIM)
2845ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			PDEBUG(DEBUG_PS2, "Re-setting TIM for aid %d\n",
2846ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			       sta->aid);
2847ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		hostap_set_tim(local, sta->aid, 1);
2848ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		sta->flags |= WLAN_STA_TIM;
2849ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	}
2850ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
2851ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	ret = AP_TX_BUFFERED;
2852ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
2853ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen out:
2854ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	if (sta != NULL) {
2855ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		if (ret == AP_TX_CONTINUE ||
2856ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		    ret == AP_TX_CONTINUE_NOT_AUTHORIZED) {
2857ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			sta->tx_packets++;
2858ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			sta->tx_bytes += skb->len;
2859ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			sta->last_tx = jiffies;
2860ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		}
2861ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
2862ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		if ((ret == AP_TX_CONTINUE ||
2863ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		     ret == AP_TX_CONTINUE_NOT_AUTHORIZED) &&
2864ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		    sta->crypt && tx->host_encrypt) {
2865ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			tx->crypt = sta->crypt;
2866ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			tx->sta_ptr = sta; /* hostap_handle_sta_release() will
2867ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen					    * be called to release sta info
2868ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen					    * later */
2869ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		} else
2870ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			atomic_dec(&sta->users);
2871ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	}
2872ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
2873ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	return ret;
2874ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen}
2875ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
2876ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
2877ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinenvoid hostap_handle_sta_release(void *ptr)
2878ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen{
2879ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	struct sta_info *sta = ptr;
2880ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	atomic_dec(&sta->users);
2881ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen}
2882ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
2883ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
2884ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen/* Called only as a tasklet (software IRQ) */
2885ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinenvoid hostap_handle_sta_tx_exc(local_info_t *local, struct sk_buff *skb)
2886ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen{
2887ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	struct sta_info *sta;
28881ea893fde29d8cf1639da8989f4b843dc3283ca8Dan Williams	struct ieee80211_hdr *hdr;
2889ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	struct hostap_skb_tx_data *meta;
2890ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
28911ea893fde29d8cf1639da8989f4b843dc3283ca8Dan Williams	hdr = (struct ieee80211_hdr *) skb->data;
2892ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	meta = (struct hostap_skb_tx_data *) skb->cb;
2893ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
2894ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	spin_lock(&local->ap->sta_table_lock);
2895ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	sta = ap_get_sta(local->ap, hdr->addr1);
2896ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	if (!sta) {
2897ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		spin_unlock(&local->ap->sta_table_lock);
2898e174961ca1a0b28f7abf0be47973ad57cb74e5f0Johannes Berg		PDEBUG(DEBUG_AP, "%s: Could not find STA %pM"
28990795af5729b18218767fab27c44b1384f72dc9adJoe Perches		       " for this TX error (@%lu)\n",
2900e174961ca1a0b28f7abf0be47973ad57cb74e5f0Johannes Berg		       local->dev->name, hdr->addr1, jiffies);
2901ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		return;
2902ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	}
2903ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
2904ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	sta->tx_since_last_failure = 0;
2905ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	sta->tx_consecutive_exc++;
290674fae82c8bd5dd78365abe25506a9ba388d4a889Jeff Garzik
2907ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	if (sta->tx_consecutive_exc >= WLAN_RATE_DECREASE_THRESHOLD &&
2908ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	    sta->tx_rate_idx > 0 && meta->rate <= sta->tx_rate) {
2909ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		/* use next lower rate */
2910ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		int old, rate;
2911ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		old = rate = sta->tx_rate_idx;
2912ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		while (rate > 0) {
2913ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			rate--;
2914ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			if (ap_tx_rate_ok(rate, sta, local)) {
2915ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen				sta->tx_rate_idx = rate;
2916ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen				break;
2917ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			}
2918ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		}
2919ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		if (old != sta->tx_rate_idx) {
2920ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			switch (sta->tx_rate_idx) {
2921ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			case 0: sta->tx_rate = 10; break;
2922ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			case 1: sta->tx_rate = 20; break;
2923ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			case 2: sta->tx_rate = 55; break;
2924ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			case 3: sta->tx_rate = 110; break;
2925ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			default: sta->tx_rate = 0; break;
2926ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			}
2927e174961ca1a0b28f7abf0be47973ad57cb74e5f0Johannes Berg			PDEBUG(DEBUG_AP,
2928e174961ca1a0b28f7abf0be47973ad57cb74e5f0Johannes Berg			       "%s: STA %pM TX rate lowered to %d\n",
2929e174961ca1a0b28f7abf0be47973ad57cb74e5f0Johannes Berg			       local->dev->name, sta->addr, sta->tx_rate);
2930ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		}
2931ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		sta->tx_consecutive_exc = 0;
2932ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	}
2933ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	spin_unlock(&local->ap->sta_table_lock);
2934ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen}
2935ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
2936ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
2937ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinenstatic void hostap_update_sta_ps2(local_info_t *local, struct sta_info *sta,
2938ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen				  int pwrmgt, int type, int stype)
2939ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen{
2940ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	if (pwrmgt && !(sta->flags & WLAN_STA_PS)) {
2941ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		sta->flags |= WLAN_STA_PS;
2942e174961ca1a0b28f7abf0be47973ad57cb74e5f0Johannes Berg		PDEBUG(DEBUG_PS2, "STA %pM changed to use PS "
2943ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		       "mode (type=0x%02X, stype=0x%02X)\n",
2944e174961ca1a0b28f7abf0be47973ad57cb74e5f0Johannes Berg		       sta->addr, type >> 2, stype >> 4);
2945ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	} else if (!pwrmgt && (sta->flags & WLAN_STA_PS)) {
2946ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		sta->flags &= ~WLAN_STA_PS;
2947e174961ca1a0b28f7abf0be47973ad57cb74e5f0Johannes Berg		PDEBUG(DEBUG_PS2, "STA %pM changed to not use "
2948ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		       "PS mode (type=0x%02X, stype=0x%02X)\n",
2949e174961ca1a0b28f7abf0be47973ad57cb74e5f0Johannes Berg		       sta->addr, type >> 2, stype >> 4);
29504339d328631aa815fe2181b9164b3690ca2db4daJouni Malinen		if (type != IEEE80211_FTYPE_CTL ||
29514339d328631aa815fe2181b9164b3690ca2db4daJouni Malinen		    stype != IEEE80211_STYPE_PSPOLL)
2952ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			schedule_packet_send(local, sta);
2953ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	}
2954ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen}
2955ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
2956ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
2957ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen/* Called only as a tasklet (software IRQ). Called for each RX frame to update
29581ea893fde29d8cf1639da8989f4b843dc3283ca8Dan Williams * STA power saving state. pwrmgt is a flag from 802.11 frame_control field. */
29591ea893fde29d8cf1639da8989f4b843dc3283ca8Dan Williamsint hostap_update_sta_ps(local_info_t *local, struct ieee80211_hdr *hdr)
2960ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen{
2961ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	struct sta_info *sta;
2962ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	u16 fc;
2963ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
2964ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	spin_lock(&local->ap->sta_table_lock);
2965ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	sta = ap_get_sta(local->ap, hdr->addr2);
2966ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	if (sta)
2967ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		atomic_inc(&sta->users);
2968ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	spin_unlock(&local->ap->sta_table_lock);
2969ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
2970ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	if (!sta)
2971ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		return -1;
2972ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
29731ea893fde29d8cf1639da8989f4b843dc3283ca8Dan Williams	fc = le16_to_cpu(hdr->frame_control);
2974b2f4a2e3b1620667da9654f9e220c92ea52bdbe4Jouni Malinen	hostap_update_sta_ps2(local, sta, fc & IEEE80211_FCTL_PM,
29751ea893fde29d8cf1639da8989f4b843dc3283ca8Dan Williams			      fc & IEEE80211_FCTL_FTYPE,
29761ea893fde29d8cf1639da8989f4b843dc3283ca8Dan Williams			      fc & IEEE80211_FCTL_STYPE);
2977ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
2978ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	atomic_dec(&sta->users);
2979ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	return 0;
2980ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen}
2981ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
2982ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
2983ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen/* Called only as a tasklet (software IRQ). Called for each RX frame after
2984ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen * getting RX header and payload from hardware. */
2985ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinenap_rx_ret hostap_handle_sta_rx(local_info_t *local, struct net_device *dev,
2986ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			       struct sk_buff *skb,
2987ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			       struct hostap_80211_rx_status *rx_stats,
2988ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			       int wds)
2989ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen{
2990ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	int ret;
2991ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	struct sta_info *sta;
2992ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	u16 fc, type, stype;
29931ea893fde29d8cf1639da8989f4b843dc3283ca8Dan Williams	struct ieee80211_hdr *hdr;
2994ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
2995ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	if (local->ap == NULL)
2996ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		return AP_RX_CONTINUE;
2997ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
29981ea893fde29d8cf1639da8989f4b843dc3283ca8Dan Williams	hdr = (struct ieee80211_hdr *) skb->data;
2999ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
30001ea893fde29d8cf1639da8989f4b843dc3283ca8Dan Williams	fc = le16_to_cpu(hdr->frame_control);
30011ea893fde29d8cf1639da8989f4b843dc3283ca8Dan Williams	type = fc & IEEE80211_FCTL_FTYPE;
30021ea893fde29d8cf1639da8989f4b843dc3283ca8Dan Williams	stype = fc & IEEE80211_FCTL_STYPE;
3003ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
3004ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	spin_lock(&local->ap->sta_table_lock);
3005ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	sta = ap_get_sta(local->ap, hdr->addr2);
3006ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	if (sta)
3007ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		atomic_inc(&sta->users);
3008ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	spin_unlock(&local->ap->sta_table_lock);
3009ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
3010ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	if (sta && !(sta->flags & WLAN_STA_AUTHORIZED))
3011ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		ret = AP_RX_CONTINUE_NOT_AUTHORIZED;
3012ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	else
3013ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		ret = AP_RX_CONTINUE;
3014ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
3015ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
3016b2f4a2e3b1620667da9654f9e220c92ea52bdbe4Jouni Malinen	if (fc & IEEE80211_FCTL_TODS) {
3017ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		if (!wds && (sta == NULL || !(sta->flags & WLAN_STA_ASSOC))) {
3018ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			if (local->hostapd) {
3019ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen				prism2_rx_80211(local->apdev, skb, rx_stats,
3020ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen						PRISM2_RX_NON_ASSOC);
3021ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT
3022ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			} else {
3023ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen				printk(KERN_DEBUG "%s: dropped received packet"
3024e174961ca1a0b28f7abf0be47973ad57cb74e5f0Johannes Berg				       " from non-associated STA %pM"
3025ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen				       " (type=0x%02x, subtype=0x%02x)\n",
3026e174961ca1a0b28f7abf0be47973ad57cb74e5f0Johannes Berg				       dev->name, hdr->addr2,
30274339d328631aa815fe2181b9164b3690ca2db4daJouni Malinen				       type >> 2, stype >> 4);
3028ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen				hostap_rx(dev, skb, rx_stats);
3029ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */
3030ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			}
3031ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			ret = AP_RX_EXIT;
3032ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			goto out;
3033ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		}
3034b2f4a2e3b1620667da9654f9e220c92ea52bdbe4Jouni Malinen	} else if (fc & IEEE80211_FCTL_FROMDS) {
3035ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		if (!wds) {
3036ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			/* FromDS frame - not for us; probably
3037ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			 * broadcast/multicast in another BSS - drop */
3038d22fbd70c2770b5e3ba0d7bc5b29f6ee686cdba4dingtianhong			if (ether_addr_equal(hdr->addr1, dev->dev_addr)) {
3039ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen				printk(KERN_DEBUG "Odd.. FromDS packet "
3040ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen				       "received with own BSSID\n");
3041ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen				hostap_dump_rx_80211(dev->name, skb, rx_stats);
3042ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			}
3043ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			ret = AP_RX_DROP;
3044ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			goto out;
3045ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		}
30464339d328631aa815fe2181b9164b3690ca2db4daJouni Malinen	} else if (stype == IEEE80211_STYPE_NULLFUNC && sta == NULL &&
3047d22fbd70c2770b5e3ba0d7bc5b29f6ee686cdba4dingtianhong		   ether_addr_equal(hdr->addr1, dev->dev_addr)) {
3048ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
3049ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		if (local->hostapd) {
3050ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			prism2_rx_80211(local->apdev, skb, rx_stats,
3051ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen					PRISM2_RX_NON_ASSOC);
3052ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT
3053ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		} else {
3054ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			/* At least Lucent f/w seems to send data::nullfunc
3055ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			 * frames with no ToDS flag when the current AP returns
3056ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			 * after being unavailable for some time. Speed up
3057ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			 * re-association by informing the station about it not
3058ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			 * being associated. */
3059e174961ca1a0b28f7abf0be47973ad57cb74e5f0Johannes Berg			printk(KERN_DEBUG "%s: rejected received nullfunc frame"
3060e174961ca1a0b28f7abf0be47973ad57cb74e5f0Johannes Berg			       " without ToDS from not associated STA %pM\n",
3061e174961ca1a0b28f7abf0be47973ad57cb74e5f0Johannes Berg			       dev->name, hdr->addr2);
3062ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			hostap_rx(dev, skb, rx_stats);
3063ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */
3064ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		}
3065ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		ret = AP_RX_EXIT;
3066ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		goto out;
30674339d328631aa815fe2181b9164b3690ca2db4daJouni Malinen	} else if (stype == IEEE80211_STYPE_NULLFUNC) {
3068ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		/* At least Lucent cards seem to send periodic nullfunc
3069ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		 * frames with ToDS. Let these through to update SQ
3070ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		 * stats and PS state. Nullfunc frames do not contain
3071ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		 * any data and they will be dropped below. */
3072ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	} else {
3073ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		/* If BSSID (Addr3) is foreign, this frame is a normal
3074ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		 * broadcast frame from an IBSS network. Drop it silently.
3075ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		 * If BSSID is own, report the dropping of this frame. */
3076d22fbd70c2770b5e3ba0d7bc5b29f6ee686cdba4dingtianhong		if (ether_addr_equal(hdr->addr3, dev->dev_addr)) {
3077e174961ca1a0b28f7abf0be47973ad57cb74e5f0Johannes Berg			printk(KERN_DEBUG "%s: dropped received packet from %pM"
3078e174961ca1a0b28f7abf0be47973ad57cb74e5f0Johannes Berg			       " with no ToDS flag "
30790795af5729b18218767fab27c44b1384f72dc9adJoe Perches			       "(type=0x%02x, subtype=0x%02x)\n", dev->name,
3080e174961ca1a0b28f7abf0be47973ad57cb74e5f0Johannes Berg			       hdr->addr2, type >> 2, stype >> 4);
3081ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			hostap_dump_rx_80211(dev->name, skb, rx_stats);
3082ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		}
3083ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		ret = AP_RX_DROP;
3084ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		goto out;
3085ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	}
3086ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
3087ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	if (sta) {
3088b2f4a2e3b1620667da9654f9e220c92ea52bdbe4Jouni Malinen		hostap_update_sta_ps2(local, sta, fc & IEEE80211_FCTL_PM,
3089ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen				      type, stype);
3090ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
3091ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		sta->rx_packets++;
3092ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		sta->rx_bytes += skb->len;
3093ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		sta->last_rx = jiffies;
3094ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	}
3095ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
30964339d328631aa815fe2181b9164b3690ca2db4daJouni Malinen	if (local->ap->nullfunc_ack && stype == IEEE80211_STYPE_NULLFUNC &&
3097b2f4a2e3b1620667da9654f9e220c92ea52bdbe4Jouni Malinen	    fc & IEEE80211_FCTL_TODS) {
3098ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		if (local->hostapd) {
3099ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			prism2_rx_80211(local->apdev, skb, rx_stats,
3100ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen					PRISM2_RX_NULLFUNC_ACK);
3101ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT
3102ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		} else {
3103ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			/* some STA f/w's seem to require control::ACK frame
3104ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			 * for data::nullfunc, but Prism2 f/w 0.8.0 (at least
3105ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			 * from Compaq) does not send this.. Try to generate
3106ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			 * ACK for these frames from the host driver to make
3107ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			 * power saving work with, e.g., Lucent WaveLAN f/w */
3108ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			hostap_rx(dev, skb, rx_stats);
3109ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */
3110ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		}
3111ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		ret = AP_RX_EXIT;
3112ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		goto out;
3113ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	}
3114ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
3115ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen out:
3116ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	if (sta)
3117ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		atomic_dec(&sta->users);
3118ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
3119ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	return ret;
3120ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen}
3121ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
3122ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
3123ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen/* Called only as a tasklet (software IRQ) */
3124ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinenint hostap_handle_sta_crypto(local_info_t *local,
31251ea893fde29d8cf1639da8989f4b843dc3283ca8Dan Williams			     struct ieee80211_hdr *hdr,
3126274bfb8dc5ffa16cb073801bebe76ab7f4e2e73dJohn W. Linville			     struct lib80211_crypt_data **crypt,
312762fe7e378109537ff80971c5208e12d40bf88beeJouni Malinen			     void **sta_ptr)
3128ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen{
3129ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	struct sta_info *sta;
3130ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
3131ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	spin_lock(&local->ap->sta_table_lock);
3132ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	sta = ap_get_sta(local->ap, hdr->addr2);
3133ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	if (sta)
3134ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		atomic_inc(&sta->users);
3135ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	spin_unlock(&local->ap->sta_table_lock);
3136ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
3137ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	if (!sta)
3138ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		return -1;
3139ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
3140ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	if (sta->crypt) {
3141ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		*crypt = sta->crypt;
3142ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		*sta_ptr = sta;
3143ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		/* hostap_handle_sta_release() will be called to release STA
3144ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		 * info */
3145ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	} else
3146ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		atomic_dec(&sta->users);
3147ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
3148ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	return 0;
3149ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen}
3150ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
3151ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
3152ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen/* Called only as a tasklet (software IRQ) */
3153ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinenint hostap_is_sta_assoc(struct ap_data *ap, u8 *sta_addr)
3154ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen{
3155ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	struct sta_info *sta;
3156ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	int ret = 0;
3157ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
3158ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	spin_lock(&ap->sta_table_lock);
3159ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	sta = ap_get_sta(ap, sta_addr);
3160ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	if (sta != NULL && (sta->flags & WLAN_STA_ASSOC) && !sta->ap)
3161ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		ret = 1;
3162ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	spin_unlock(&ap->sta_table_lock);
3163ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
3164ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	return ret;
3165ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen}
3166ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
3167ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
3168ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen/* Called only as a tasklet (software IRQ) */
3169ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinenint hostap_is_sta_authorized(struct ap_data *ap, u8 *sta_addr)
3170ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen{
3171ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	struct sta_info *sta;
3172ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	int ret = 0;
3173ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
3174ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	spin_lock(&ap->sta_table_lock);
3175ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	sta = ap_get_sta(ap, sta_addr);
3176ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	if (sta != NULL && (sta->flags & WLAN_STA_ASSOC) && !sta->ap &&
3177ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	    ((sta->flags & WLAN_STA_AUTHORIZED) ||
3178ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	     ap->local->ieee_802_1x == 0))
3179ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		ret = 1;
3180ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	spin_unlock(&ap->sta_table_lock);
3181ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
3182ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	return ret;
3183ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen}
3184ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
3185ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
3186ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen/* Called only as a tasklet (software IRQ) */
3187ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinenint hostap_add_sta(struct ap_data *ap, u8 *sta_addr)
3188ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen{
3189ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	struct sta_info *sta;
3190ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	int ret = 1;
3191ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
3192ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	if (!ap)
3193ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		return -1;
3194ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
3195ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	spin_lock(&ap->sta_table_lock);
3196ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	sta = ap_get_sta(ap, sta_addr);
3197ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	if (sta)
3198ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		ret = 0;
3199ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	spin_unlock(&ap->sta_table_lock);
3200ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
3201ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	if (ret == 1) {
3202ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		sta = ap_add_sta(ap, sta_addr);
3203ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		if (!sta)
320454b85f489bdfafc9306dfcc21e0d2687c34c3b34Adrian Bunk			return -1;
3205ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		sta->flags = WLAN_STA_AUTH | WLAN_STA_ASSOC;
3206ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		sta->ap = 1;
3207ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		memset(sta->supported_rates, 0, sizeof(sta->supported_rates));
3208ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		/* No way of knowing which rates are supported since we did not
3209ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		 * get supported rates element from beacon/assoc req. Assume
3210ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		 * that remote end supports all 802.11b rates. */
3211ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		sta->supported_rates[0] = 0x82;
3212ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		sta->supported_rates[1] = 0x84;
3213ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		sta->supported_rates[2] = 0x0b;
3214ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		sta->supported_rates[3] = 0x16;
3215ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		sta->tx_supp_rates = WLAN_RATE_1M | WLAN_RATE_2M |
3216ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			WLAN_RATE_5M5 | WLAN_RATE_11M;
3217ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		sta->tx_rate = 110;
3218ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		sta->tx_max_rate = sta->tx_rate_idx = 3;
3219ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	}
3220ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
3221ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	return ret;
3222ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen}
3223ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
3224ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
3225ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen/* Called only as a tasklet (software IRQ) */
3226ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinenint hostap_update_rx_stats(struct ap_data *ap,
32271ea893fde29d8cf1639da8989f4b843dc3283ca8Dan Williams			   struct ieee80211_hdr *hdr,
3228ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			   struct hostap_80211_rx_status *rx_stats)
3229ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen{
3230ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	struct sta_info *sta;
3231ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
3232ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	if (!ap)
3233ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		return -1;
3234ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
3235ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	spin_lock(&ap->sta_table_lock);
3236ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	sta = ap_get_sta(ap, hdr->addr2);
3237ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	if (sta) {
3238ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		sta->last_rx_silence = rx_stats->noise;
3239ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		sta->last_rx_signal = rx_stats->signal;
3240ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		sta->last_rx_rate = rx_stats->rate;
3241c28df16ed70d1b6cefd12135e3c68bfccd1bb635Jean Tourrilhes		sta->last_rx_updated = IW_QUAL_ALL_UPDATED | IW_QUAL_DBM;
3242ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		if (rx_stats->rate == 10)
3243ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			sta->rx_count[0]++;
3244ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		else if (rx_stats->rate == 20)
3245ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			sta->rx_count[1]++;
3246ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		else if (rx_stats->rate == 55)
3247ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			sta->rx_count[2]++;
3248ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		else if (rx_stats->rate == 110)
3249ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			sta->rx_count[3]++;
3250ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	}
3251ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	spin_unlock(&ap->sta_table_lock);
3252ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
3253ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	return sta ? 0 : -1;
3254ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen}
3255ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
3256ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
3257ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinenvoid hostap_update_rates(local_info_t *local)
3258ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen{
3259c15057313725942c6af8dcb60b4d4322101316d9Matthias Kaehlcke	struct sta_info *sta;
3260ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	struct ap_data *ap = local->ap;
3261ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
3262ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	if (!ap)
3263ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		return;
3264ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
3265ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	spin_lock_bh(&ap->sta_table_lock);
3266c15057313725942c6af8dcb60b4d4322101316d9Matthias Kaehlcke	list_for_each_entry(sta, &ap->sta_list, list) {
3267ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		prism2_check_tx_rates(sta);
3268ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	}
3269ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	spin_unlock_bh(&ap->sta_table_lock);
3270ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen}
3271ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
3272ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
32735fad5a2e1f34b333a801b749c4e143c2ac3e8a4fAdrian Bunkvoid * ap_crypt_get_ptrs(struct ap_data *ap, u8 *addr, int permanent,
3274274bfb8dc5ffa16cb073801bebe76ab7f4e2e73dJohn W. Linville			 struct lib80211_crypt_data ***crypt)
3275ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen{
3276ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	struct sta_info *sta;
3277ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
3278ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	spin_lock_bh(&ap->sta_table_lock);
3279ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	sta = ap_get_sta(ap, addr);
3280ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	if (sta)
3281ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		atomic_inc(&sta->users);
3282ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	spin_unlock_bh(&ap->sta_table_lock);
3283ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
3284ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	if (!sta && permanent)
3285ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		sta = ap_add_sta(ap, addr);
3286ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
3287ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	if (!sta)
3288ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		return NULL;
3289ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
3290ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	if (permanent)
3291ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		sta->flags |= WLAN_STA_PERM;
3292ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
3293ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	*crypt = &sta->crypt;
3294ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
3295ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	return sta;
3296ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen}
3297ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
3298ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
3299ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinenvoid hostap_add_wds_links(local_info_t *local)
3300ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen{
3301ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	struct ap_data *ap = local->ap;
3302c15057313725942c6af8dcb60b4d4322101316d9Matthias Kaehlcke	struct sta_info *sta;
3303ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
3304ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	spin_lock_bh(&ap->sta_table_lock);
3305c15057313725942c6af8dcb60b4d4322101316d9Matthias Kaehlcke	list_for_each_entry(sta, &ap->sta_list, list) {
3306ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		if (sta->ap)
3307ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen			hostap_wds_link_oper(local, sta->addr, WDS_ADD);
3308ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	}
3309ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	spin_unlock_bh(&ap->sta_table_lock);
3310ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
3311ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	schedule_work(&local->ap->wds_oper_queue);
3312ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen}
3313ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
3314ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
3315ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinenvoid hostap_wds_link_oper(local_info_t *local, u8 *addr, wds_oper_type type)
3316ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen{
3317ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	struct wds_oper_data *entry;
3318ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
3319ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	entry = kmalloc(sizeof(*entry), GFP_ATOMIC);
3320ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	if (!entry)
3321ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen		return;
3322ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	memcpy(entry->addr, addr, ETH_ALEN);
3323ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	entry->type = type;
3324ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	spin_lock_bh(&local->lock);
3325ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	entry->next = local->ap->wds_oper_entries;
3326ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	local->ap->wds_oper_entries = entry;
3327ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	spin_unlock_bh(&local->lock);
3328ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
3329ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen	schedule_work(&local->ap->wds_oper_queue);
3330ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen}
3331ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
3332ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen
3333ff1d2767d5a43c85f944e86a45284b721f66196cJouni MalinenEXPORT_SYMBOL(hostap_init_data);
3334ff1d2767d5a43c85f944e86a45284b721f66196cJouni MalinenEXPORT_SYMBOL(hostap_init_ap_proc);
3335ff1d2767d5a43c85f944e86a45284b721f66196cJouni MalinenEXPORT_SYMBOL(hostap_free_data);
3336ff1d2767d5a43c85f944e86a45284b721f66196cJouni MalinenEXPORT_SYMBOL(hostap_check_sta_fw_version);
3337ff1d2767d5a43c85f944e86a45284b721f66196cJouni MalinenEXPORT_SYMBOL(hostap_handle_sta_tx_exc);
3338ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT
3339ff1d2767d5a43c85f944e86a45284b721f66196cJouni Malinen#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */
3340