Merge branch 'for_paulus' of master.kernel.org:/pub/scm/linux/kernel/git/galak/powerpc
[pandora-kernel.git] / net / ieee80211 / softmac / ieee80211softmac_event.c
1 /*
2  * Event system
3  * Also see comments in public header file and longer explanation below.
4  *
5  * Copyright (c) 2005, 2006 Johannes Berg <johannes@sipsolutions.net>
6  *                          Joseph Jezak <josejx@gentoo.org>
7  *                          Larry Finger <Larry.Finger@lwfinger.net>
8  *                          Danny van Dyk <kugelfang@gentoo.org>
9  *                          Michael Buesch <mbuesch@freenet.de>
10  *
11  * This program is free software; you can redistribute it and/or modify it
12  * under the terms of version 2 of the GNU General Public License as
13  * published by the Free Software Foundation.
14  *
15  * This program is distributed in the hope that it will be useful, but WITHOUT
16  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
17  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
18  * more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
23  *
24  * The full GNU General Public License is included in this distribution in the
25  * file called COPYING.
26  */
27
28 #include "ieee80211softmac_priv.h"
29
30 /*
31  * Each event has associated to it
32  *  - an event type (see constants in public header)
33  *  - an event context (see below)
34  *  - the function to be called
35  *  - a context (extra parameter to call the function with)
36  *  - and the softmac struct
37  *
38  * The event context is private and can only be used from
39  * within this module. Its meaning varies with the event
40  * type:
41  *  SCAN_FINISHED:      no special meaning
42  *  ASSOCIATED,
43  *  ASSOCIATE_FAILED,
44  *  ASSOCIATE_TIMEOUT,
45  *  AUTHENTICATED,
46  *  AUTH_FAILED,
47  *  AUTH_TIMEOUT:       a pointer to the network struct
48  * ...
49  * Code within this module can use the event context to be only
50  * called when the event is true for that specific context
51  * as per above table.
52  * If the event context is NULL, then the notification is always called,
53  * regardless of the event context. The event context is not passed to
54  * the callback, it is assumed that the context suffices.
55  *
56  * You can also use the event context only by setting the event type
57  * to -1 (private use only), in which case you'll be notified
58  * whenever the event context matches.
59  */
60
61 static char *event_descriptions[IEEE80211SOFTMAC_EVENT_LAST+1] = {
62         "scan finished",
63         "associated",
64         "associating failed",
65         "associating timed out",
66         "authenticated",
67         "authenticating failed",
68         "authenticating timed out",
69         "associating failed because no suitable network was found",
70         "disassociated",
71 };
72
73
74 static void
75 ieee80211softmac_notify_callback(void *d)
76 {
77         struct ieee80211softmac_event event = *(struct ieee80211softmac_event*) d;
78         kfree(d);
79         
80         event.fun(event.mac->dev, event.context);
81 }
82
83 int
84 ieee80211softmac_notify_internal(struct ieee80211softmac_device *mac,
85         int event, void *event_context, notify_function_ptr fun, void *context, gfp_t gfp_mask)
86 {
87         struct ieee80211softmac_event *eventptr;
88         unsigned long flags;
89
90         if (event < -1 || event > IEEE80211SOFTMAC_EVENT_LAST)
91                 return -ENOSYS;
92         
93         if (!fun)
94                 return -EINVAL;
95         
96         eventptr = kmalloc(sizeof(struct ieee80211softmac_event), gfp_mask);
97         if (!eventptr)
98                 return -ENOMEM;
99         
100         eventptr->event_type = event;
101         INIT_WORK(&eventptr->work, ieee80211softmac_notify_callback, eventptr);
102         eventptr->fun = fun;
103         eventptr->context = context;
104         eventptr->mac = mac;
105         eventptr->event_context = event_context;
106
107         spin_lock_irqsave(&mac->lock, flags);
108         list_add(&eventptr->list, &mac->events);
109         spin_unlock_irqrestore(&mac->lock, flags);
110
111         return 0;
112 }
113
114 int
115 ieee80211softmac_notify_gfp(struct net_device *dev,
116         int event, notify_function_ptr fun, void *context, gfp_t gfp_mask)
117 {
118         struct ieee80211softmac_device *mac = ieee80211_priv(dev);
119
120         if (event < 0 || event > IEEE80211SOFTMAC_EVENT_LAST)
121                 return -ENOSYS;
122         
123         return ieee80211softmac_notify_internal(mac, event, NULL, fun, context, gfp_mask);
124 }
125 EXPORT_SYMBOL_GPL(ieee80211softmac_notify_gfp);
126
127 /* private -- calling all callbacks that were specified */
128 void
129 ieee80211softmac_call_events_locked(struct ieee80211softmac_device *mac, int event, void *event_ctx)
130 {
131         struct ieee80211softmac_event *eventptr, *tmp;
132         struct ieee80211softmac_network *network;
133         
134         if (event >= 0) {
135                 union iwreq_data wrqu;
136                 int we_event;
137                 char *msg = NULL;
138
139                 switch(event) {
140                 case IEEE80211SOFTMAC_EVENT_ASSOCIATED:
141                         network = (struct ieee80211softmac_network *)event_ctx;
142                         wrqu.data.length = 0;
143                         wrqu.data.flags = 0;
144                         memcpy(wrqu.ap_addr.sa_data, &network->bssid[0], ETH_ALEN);
145                         wrqu.ap_addr.sa_family = ARPHRD_ETHER;
146                         we_event = SIOCGIWAP;
147                         break;
148                 case IEEE80211SOFTMAC_EVENT_DISASSOCIATED:
149                         wrqu.data.length = 0;
150                         wrqu.data.flags = 0;
151                         memset(&wrqu, '\0', sizeof (union iwreq_data));
152                         wrqu.ap_addr.sa_family = ARPHRD_ETHER;
153                         we_event = SIOCGIWAP;
154                         break;
155                 case IEEE80211SOFTMAC_EVENT_SCAN_FINISHED:
156                         wrqu.data.length = 0;
157                         wrqu.data.flags = 0;
158                         memset(&wrqu, '\0', sizeof (union iwreq_data));
159                         we_event = SIOCGIWSCAN;
160                         break;
161                 default:
162                         msg = event_descriptions[event];
163                         wrqu.data.length = strlen(msg);
164                         we_event = IWEVCUSTOM;
165                         break;
166                 }
167                 wireless_send_event(mac->dev, we_event, &wrqu, msg);
168         }
169
170         if (!list_empty(&mac->events))
171                 list_for_each_entry_safe(eventptr, tmp, &mac->events, list) {
172                         if ((eventptr->event_type == event || eventptr->event_type == -1)
173                                 && (eventptr->event_context == NULL || eventptr->event_context == event_ctx)) {
174                                 list_del(&eventptr->list);
175                                 schedule_work(&eventptr->work);
176                         }
177                 }
178 }
179
180 void
181 ieee80211softmac_call_events(struct ieee80211softmac_device *mac, int event, void *event_ctx)
182 {
183         unsigned long flags;
184
185         spin_lock_irqsave(&mac->lock, flags);
186         ieee80211softmac_call_events_locked(mac, event, event_ctx);
187
188         spin_unlock_irqrestore(&mac->lock, flags);
189 }