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