| /* | 
 |  * 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; | 
 | 				schedule_delayed_work(&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); | 
 | } |