aboutsummaryrefslogtreecommitdiffstats
path: root/net/ieee80211/softmac/ieee80211softmac_event.c
blob: 8cef05b60f16403dc9f3c9b3cfca1436683edf1c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
/*
 * Event system
 * Also see comments in public header file and longer explanation below.
 *
 * Copyright (c) 2005, 2006 Johannes Berg <johannes@sipsolutions.net>
 *                          Joseph Jezak <josejx@gentoo.org>
 *                          Larry Finger <Larry.Finger@lwfinger.net>
 *                          Danny van Dyk <kugelfang@gentoo.org>
 *                          Michael Buesch <mbuesch@freenet.de>
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of version 2 of the GNU General Public License as
 * published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
 * more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
 *
 * The full GNU General Public License is included in this distribution in the
 * file called COPYING.
 */

#include "ieee80211softmac_priv.h"

/*
 * Each event has associated to it
 *  - an event type (see constants in public header)
 *  - an event context (see below)
 *  - the function to be called
 *  - a context (extra parameter to call the function with)
 *  - and the softmac struct
 *
 * The event context is private and can only be used from
 * within this module. Its meaning varies with the event
 * type:
 *  SCAN_FINISHED,
 *  DISASSOCIATED:	NULL
 *  ASSOCIATED,
 *  ASSOCIATE_FAILED,
 *  ASSOCIATE_TIMEOUT,
 *  AUTHENTICATED,
 *  AUTH_FAILED,
 *  AUTH_TIMEOUT:	a pointer to the network struct
 * ...
 * Code within this module can use the event context to be only
 * called when the event is true for that specific context
 * as per above table.
 * If the event context is NULL, then the notification is always called,
 * regardless of the event context. The event context is not passed to
 * the callback, it is assumed that the context suffices.
 *
 * You can also use the event context only by setting the event type
 * to -1 (private use only), in which case you'll be notified
 * whenever the event context matches.
 */

static char *event_descriptions[IEEE80211SOFTMAC_EVENT_LAST+1] = {
	NULL, /* scan finished */
	NULL, /* associated */
	"associating failed",
	"associating timed out",
	"authenticated",
	"authenticating failed",
	"authenticating timed out",
	"associating failed because no suitable network was found",
	NULL, /* disassociated */
};


static void
ieee80211softmac_notify_callback(struct work_struct *work)
{
	struct ieee80211softmac_event *pevent =
		container_of(work, struct ieee80211softmac_event, work.work);
	struct ieee80211softmac_event event = *pevent;
	kfree(pevent);

	event.fun(event.mac->dev, event.event_type, event.context);
}

int
ieee80211softmac_notify_internal(struct ieee80211softmac_device *mac,
	int event, void *event_context, notify_function_ptr fun, void *context, gfp_t gfp_mask)
{
	struct ieee80211softmac_event *eventptr;
	unsigned long flags;

	if (event < -1 || event > IEEE80211SOFTMAC_EVENT_LAST)
		return -ENOSYS;

	if (!fun)
		return -EINVAL;

	eventptr = kmalloc(sizeof(struct ieee80211softmac_event), gfp_mask);
	if (!eventptr)
		return -ENOMEM;

	eventptr->event_type = event;
	INIT_DELAYED_WORK(&eventptr->work, ieee80211softmac_notify_callback);
	eventptr->fun = fun;
	eventptr->context = context;
	eventptr->mac = mac;
	eventptr->event_context = event_context;

	spin_lock_irqsave(&mac->lock, flags);
	list_add(&eventptr->list, &mac->events);
	spin_unlock_irqrestore(&mac->lock, flags);

	return 0;
}

int
ieee80211softmac_notify_gfp(struct net_device *dev,
	int event, notify_function_ptr fun, void *context, gfp_t gfp_mask)
{
	struct ieee80211softmac_device *mac = ieee80211_priv(dev);

	if (event < 0 || event > IEEE80211SOFTMAC_EVENT_LAST)
		return -ENOSYS;

	return ieee80211softmac_notify_internal(mac, event, NULL, fun, context, gfp_mask);
}
EXPORT_SYMBOL_GPL(ieee80211softmac_notify_gfp);

/* private -- calling all callbacks that were specified */
void
ieee80211softmac_call_events_locked(struct ieee80211softmac_device *mac, int event, void *event_ctx)
{
	struct ieee80211softmac_event *eventptr, *tmp;
	struct ieee80211softmac_network *network;

	if (event >= 0) {
		union iwreq_data wrqu;
		int we_event;
		char *msg = NULL;

		memset(&wrqu, '\0', sizeof (union iwreq_data));

		switch(event) {
		case IEEE80211SOFTMAC_EVENT_ASSOCIATED:
			network = (struct ieee80211softmac_network *)event_ctx;
			memcpy(wrqu.ap_addr.sa_data, &network->bssid[0], ETH_ALEN);
			/* fall through */
		case IEEE80211SOFTMAC_EVENT_DISASSOCIATED:
			wrqu.ap_addr.sa_family = ARPHRD_ETHER;
			we_event = SIOCGIWAP;
			break;
		case IEEE80211SOFTMAC_EVENT_SCAN_FINISHED:
			we_event = SIOCGIWSCAN;
			break;
		default:
			msg = event_descriptions[event];
			if (!msg)
				msg = "SOFTMAC EVENT BUG";
			wrqu.data.length = strlen(msg);
			we_event = IWEVCUSTOM;
			break;
		}
		wireless_send_event(mac->dev, we_event, &wrqu, msg);
	}

	if (!list_empty(&mac->events))
		list_for_each_entry_safe(eventptr, tmp, &mac->events, list) {
			if ((eventptr->event_type == event || eventptr->event_type == -1)
				&& (eventptr->event_context == NULL || eventptr->event_context == event_ctx)) {
				list_del(&eventptr->list);
				/* User may have subscribed to ANY event, so
				 * we tell them which event triggered it. */
				eventptr->event_type = event;
				queue_delayed_work(mac->wq, &eventptr->work, 0);
			}
		}
}

void
ieee80211softmac_call_events(struct ieee80211softmac_device *mac, int event, void *event_ctx)
{
	unsigned long flags;

	spin_lock_irqsave(&mac->lock, flags);
	ieee80211softmac_call_events_locked(mac, event, event_ctx);

	spin_unlock_irqrestore(&mac->lock, flags);
}