work.c
Go to the documentation of this file.
00001 /*
00002  * mac80211 work implementation
00003  *
00004  * Copyright 2003-2008, Jouni Malinen <j@w1.fi>
00005  * Copyright 2004, Instant802 Networks, Inc.
00006  * Copyright 2005, Devicescape Software, Inc.
00007  * Copyright 2006-2007  Jiri Benc <jbenc@suse.cz>
00008  * Copyright 2007, Michael Wu <flamingice@sourmilk.net>
00009  * Copyright 2009, Johannes Berg <johannes@sipsolutions.net>
00010  *
00011  * This program is free software; you can redistribute it and/or modify
00012  * it under the terms of the GNU General Public License version 2 as
00013  * published by the Free Software Foundation.
00014  */
00015 
00016 #include <linux/delay.h>
00017 #include <linux/if_ether.h>
00018 #include <linux/skbuff.h>
00019 #include <linux/if_arp.h>
00020 #include <linux/etherdevice.h>
00021 #include <linux/crc32.h>
00022 #include <linux/slab.h>
00023 #include <net/mac80211.h>
00024 #include <asm/unaligned.h>
00025 
00026 #include "ieee80211_i.h"
00027 #include "rate.h"
00028 #include "driver-ops.h"
00029 
00030 #define IEEE80211_AUTH_TIMEOUT (HZ / 5)
00031 #define IEEE80211_AUTH_MAX_TRIES 3
00032 #define IEEE80211_ASSOC_TIMEOUT (HZ / 5)
00033 #define IEEE80211_ASSOC_MAX_TRIES 3
00034 
00035 enum work_action {
00036         WORK_ACT_MISMATCH,
00037         WORK_ACT_NONE,
00038         WORK_ACT_TIMEOUT,
00039         WORK_ACT_DONE,
00040 };
00041 
00042 
00043 /* utils */
00044 static inline void ASSERT_WORK_MTX(struct ieee80211_local *local)
00045 {
00046         lockdep_assert_held(&local->mtx);
00047 }
00048 
00049 /*
00050  * We can have multiple work items (and connection probing)
00051  * scheduling this timer, but we need to take care to only
00052  * reschedule it when it should fire _earlier_ than it was
00053  * asked for before, or if it's not pending right now. This
00054  * function ensures that. Note that it then is required to
00055  * run this function for all timeouts after the first one
00056  * has happened -- the work that runs from this timer will
00057  * do that.
00058  */
00059 static void run_again(struct ieee80211_local *local,
00060                       unsigned long timeout)
00061 {
00062         ASSERT_WORK_MTX(local);
00063 
00064         if (!timer_pending(&local->work_timer) ||
00065             time_before(timeout, local->work_timer.expires))
00066                 mod_timer(&local->work_timer, timeout);
00067 }
00068 
00069 void free_work(struct ieee80211_work *wk)
00070 {
00071         kfree_rcu(wk, rcu_head);
00072 }
00073 
00074 static int ieee80211_compatible_rates(const u8 *supp_rates, int supp_rates_len,
00075                                       struct ieee80211_supported_band *sband,
00076                                       u32 *rates)
00077 {
00078         int i, j, count;
00079         *rates = 0;
00080         count = 0;
00081         for (i = 0; i < supp_rates_len; i++) {
00082                 int rate = (supp_rates[i] & 0x7F) * 5;
00083 
00084                 for (j = 0; j < sband->n_bitrates; j++)
00085                         if (sband->bitrates[j].bitrate == rate) {
00086                                 *rates |= BIT(j);
00087                                 count++;
00088                                 break;
00089                         }
00090         }
00091 
00092         return count;
00093 }
00094 
00095 /* frame sending functions */
00096 
00097 static void ieee80211_add_ht_ie(struct sk_buff *skb, const u8 *ht_info_ie,
00098                                 struct ieee80211_supported_band *sband,
00099                                 struct ieee80211_channel *channel,
00100                                 enum ieee80211_smps_mode smps)
00101 {
00102         struct ieee80211_ht_info *ht_info;
00103         u8 *pos;
00104         u32 flags = channel->flags;
00105         u16 cap = sband->ht_cap.cap;
00106         __le16 tmp;
00107 
00108         if (!sband->ht_cap.ht_supported)
00109                 return;
00110 
00111         if (!ht_info_ie)
00112                 return;
00113 
00114         if (ht_info_ie[1] < sizeof(struct ieee80211_ht_info))
00115                 return;
00116 
00117         ht_info = (struct ieee80211_ht_info *)(ht_info_ie + 2);
00118 
00119         /* determine capability flags */
00120 
00121         switch (ht_info->ht_param & IEEE80211_HT_PARAM_CHA_SEC_OFFSET) {
00122         case IEEE80211_HT_PARAM_CHA_SEC_ABOVE:
00123                 if (flags & IEEE80211_CHAN_NO_HT40PLUS) {
00124                         cap &= ~IEEE80211_HT_CAP_SUP_WIDTH_20_40;
00125                         cap &= ~IEEE80211_HT_CAP_SGI_40;
00126                 }
00127                 break;
00128         case IEEE80211_HT_PARAM_CHA_SEC_BELOW:
00129                 if (flags & IEEE80211_CHAN_NO_HT40MINUS) {
00130                         cap &= ~IEEE80211_HT_CAP_SUP_WIDTH_20_40;
00131                         cap &= ~IEEE80211_HT_CAP_SGI_40;
00132                 }
00133                 break;
00134         }
00135 
00136         /* set SM PS mode properly */
00137         cap &= ~IEEE80211_HT_CAP_SM_PS;
00138         switch (smps) {
00139         case IEEE80211_SMPS_AUTOMATIC:
00140         case IEEE80211_SMPS_NUM_MODES:
00141                 WARN_ON(1);
00142         case IEEE80211_SMPS_OFF:
00143                 cap |= WLAN_HT_CAP_SM_PS_DISABLED <<
00144                         IEEE80211_HT_CAP_SM_PS_SHIFT;
00145                 break;
00146         case IEEE80211_SMPS_STATIC:
00147                 cap |= WLAN_HT_CAP_SM_PS_STATIC <<
00148                         IEEE80211_HT_CAP_SM_PS_SHIFT;
00149                 break;
00150         case IEEE80211_SMPS_DYNAMIC:
00151                 cap |= WLAN_HT_CAP_SM_PS_DYNAMIC <<
00152                         IEEE80211_HT_CAP_SM_PS_SHIFT;
00153                 break;
00154         }
00155 
00156         /* reserve and fill IE */
00157 
00158         pos = skb_put(skb, sizeof(struct ieee80211_ht_cap) + 2);
00159         *pos++ = WLAN_EID_HT_CAPABILITY;
00160         *pos++ = sizeof(struct ieee80211_ht_cap);
00161         memset(pos, 0, sizeof(struct ieee80211_ht_cap));
00162 
00163         /* capability flags */
00164         tmp = cpu_to_le16(cap);
00165         memcpy(pos, &tmp, sizeof(u16));
00166         pos += sizeof(u16);
00167 
00168         /* AMPDU parameters */
00169         *pos++ = sband->ht_cap.ampdu_factor |
00170                  (sband->ht_cap.ampdu_density <<
00171                         IEEE80211_HT_AMPDU_PARM_DENSITY_SHIFT);
00172 
00173         /* MCS set */
00174         memcpy(pos, &sband->ht_cap.mcs, sizeof(sband->ht_cap.mcs));
00175         pos += sizeof(sband->ht_cap.mcs);
00176 
00177         /* extended capabilities */
00178         pos += sizeof(__le16);
00179 
00180         /* BF capabilities */
00181         pos += sizeof(__le32);
00182 
00183         /* antenna selection */
00184         pos += sizeof(u8);
00185 }
00186 
00187 static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata,
00188                                  struct ieee80211_work *wk)
00189 {
00190         struct ieee80211_local *local = sdata->local;
00191         struct sk_buff *skb;
00192         struct ieee80211_mgmt *mgmt;
00193         u8 *pos, qos_info;
00194         size_t offset = 0, noffset;
00195         int i, count, rates_len, supp_rates_len;
00196         u16 capab;
00197         struct ieee80211_supported_band *sband;
00198         u32 rates = 0;
00199 
00200         sband = local->hw.wiphy->bands[wk->chan->band];
00201 
00202         if (wk->assoc.supp_rates_len) {
00203                 /*
00204                  * Get all rates supported by the device and the AP as
00205                  * some APs don't like getting a superset of their rates
00206                  * in the association request (e.g. D-Link DAP 1353 in
00207                  * b-only mode)...
00208                  */
00209                 rates_len = ieee80211_compatible_rates(wk->assoc.supp_rates,
00210                                                        wk->assoc.supp_rates_len,
00211                                                        sband, &rates);
00212         } else {
00213                 /*
00214                  * In case AP not provide any supported rates information
00215                  * before association, we send information element(s) with
00216                  * all rates that we support.
00217                  */
00218                 rates = ~0;
00219                 rates_len = sband->n_bitrates;
00220         }
00221 
00222         skb = alloc_skb(local->hw.extra_tx_headroom +
00223                         sizeof(*mgmt) + /* bit too much but doesn't matter */
00224                         2 + wk->assoc.ssid_len + /* SSID */
00225                         4 + rates_len + /* (extended) rates */
00226                         4 + /* power capability */
00227                         2 + 2 * sband->n_channels + /* supported channels */
00228                         2 + sizeof(struct ieee80211_ht_cap) + /* HT */
00229                         wk->ie_len + /* extra IEs */
00230                         9, /* WMM */
00231                         GFP_KERNEL);
00232         if (!skb)
00233                 return;
00234 
00235         skb_reserve(skb, local->hw.extra_tx_headroom);
00236 
00237         capab = WLAN_CAPABILITY_ESS;
00238 
00239         if (sband->band == IEEE80211_BAND_2GHZ) {
00240                 if (!(local->hw.flags & IEEE80211_HW_2GHZ_SHORT_SLOT_INCAPABLE))
00241                         capab |= WLAN_CAPABILITY_SHORT_SLOT_TIME;
00242                 if (!(local->hw.flags & IEEE80211_HW_2GHZ_SHORT_PREAMBLE_INCAPABLE))
00243                         capab |= WLAN_CAPABILITY_SHORT_PREAMBLE;
00244         }
00245 
00246         if (wk->assoc.capability & WLAN_CAPABILITY_PRIVACY)
00247                 capab |= WLAN_CAPABILITY_PRIVACY;
00248 
00249         if ((wk->assoc.capability & WLAN_CAPABILITY_SPECTRUM_MGMT) &&
00250             (local->hw.flags & IEEE80211_HW_SPECTRUM_MGMT))
00251                 capab |= WLAN_CAPABILITY_SPECTRUM_MGMT;
00252 
00253         mgmt = (struct ieee80211_mgmt *) skb_put(skb, 24);
00254         memset(mgmt, 0, 24);
00255         memcpy(mgmt->da, wk->filter_ta, ETH_ALEN);
00256         memcpy(mgmt->sa, sdata->vif.addr, ETH_ALEN);
00257         memcpy(mgmt->bssid, wk->filter_ta, ETH_ALEN);
00258 
00259         if (!is_zero_ether_addr(wk->assoc.prev_bssid)) {
00260                 skb_put(skb, 10);
00261                 mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
00262                                                   IEEE80211_STYPE_REASSOC_REQ);
00263                 mgmt->u.reassoc_req.capab_info = cpu_to_le16(capab);
00264                 mgmt->u.reassoc_req.listen_interval =
00265                                 cpu_to_le16(local->hw.conf.listen_interval);
00266                 memcpy(mgmt->u.reassoc_req.current_ap, wk->assoc.prev_bssid,
00267                        ETH_ALEN);
00268         } else {
00269                 skb_put(skb, 4);
00270                 mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
00271                                                   IEEE80211_STYPE_ASSOC_REQ);
00272                 mgmt->u.assoc_req.capab_info = cpu_to_le16(capab);
00273                 mgmt->u.assoc_req.listen_interval =
00274                                 cpu_to_le16(local->hw.conf.listen_interval);
00275         }
00276 
00277         /* SSID */
00278         pos = skb_put(skb, 2 + wk->assoc.ssid_len);
00279         *pos++ = WLAN_EID_SSID;
00280         *pos++ = wk->assoc.ssid_len;
00281         memcpy(pos, wk->assoc.ssid, wk->assoc.ssid_len);
00282 
00283         /* add all rates which were marked to be used above */
00284         supp_rates_len = rates_len;
00285         if (supp_rates_len > 8)
00286                 supp_rates_len = 8;
00287 
00288         pos = skb_put(skb, supp_rates_len + 2);
00289         *pos++ = WLAN_EID_SUPP_RATES;
00290         *pos++ = supp_rates_len;
00291 
00292         count = 0;
00293         for (i = 0; i < sband->n_bitrates; i++) {
00294                 if (BIT(i) & rates) {
00295                         int rate = sband->bitrates[i].bitrate;
00296                         *pos++ = (u8) (rate / 5);
00297                         if (++count == 8)
00298                                 break;
00299                 }
00300         }
00301 
00302         if (rates_len > count) {
00303                 pos = skb_put(skb, rates_len - count + 2);
00304                 *pos++ = WLAN_EID_EXT_SUPP_RATES;
00305                 *pos++ = rates_len - count;
00306 
00307                 for (i++; i < sband->n_bitrates; i++) {
00308                         if (BIT(i) & rates) {
00309                                 int rate = sband->bitrates[i].bitrate;
00310                                 *pos++ = (u8) (rate / 5);
00311                         }
00312                 }
00313         }
00314 
00315         if (capab & WLAN_CAPABILITY_SPECTRUM_MGMT) {
00316                 /* 1. power capabilities */
00317                 pos = skb_put(skb, 4);
00318                 *pos++ = WLAN_EID_PWR_CAPABILITY;
00319                 *pos++ = 2;
00320                 *pos++ = 0; /* min tx power */
00321                 *pos++ = wk->chan->max_power; /* max tx power */
00322 
00323                 /* 2. supported channels */
00324                 /* TODO: get this in reg domain format */
00325                 pos = skb_put(skb, 2 * sband->n_channels + 2);
00326                 *pos++ = WLAN_EID_SUPPORTED_CHANNELS;
00327                 *pos++ = 2 * sband->n_channels;
00328                 for (i = 0; i < sband->n_channels; i++) {
00329                         *pos++ = ieee80211_frequency_to_channel(
00330                                         sband->channels[i].center_freq);
00331                         *pos++ = 1; /* one channel in the subband*/
00332                 }
00333         }
00334 
00335         /* if present, add any custom IEs that go before HT */
00336         if (wk->ie_len && wk->ie) {
00337                 static const u8 before_ht[] = {
00338                         WLAN_EID_SSID,
00339                         WLAN_EID_SUPP_RATES,
00340                         WLAN_EID_EXT_SUPP_RATES,
00341                         WLAN_EID_PWR_CAPABILITY,
00342                         WLAN_EID_SUPPORTED_CHANNELS,
00343                         WLAN_EID_RSN,
00344                         WLAN_EID_QOS_CAPA,
00345                         WLAN_EID_RRM_ENABLED_CAPABILITIES,
00346                         WLAN_EID_MOBILITY_DOMAIN,
00347                         WLAN_EID_SUPPORTED_REGULATORY_CLASSES,
00348                 };
00349                 noffset = ieee80211_ie_split(wk->ie, wk->ie_len,
00350                                              before_ht, ARRAY_SIZE(before_ht),
00351                                              offset);
00352                 pos = skb_put(skb, noffset - offset);
00353                 memcpy(pos, wk->ie + offset, noffset - offset);
00354                 offset = noffset;
00355         }
00356 
00357         if (wk->assoc.use_11n && wk->assoc.wmm_used &&
00358             local->hw.queues >= 4)
00359                 ieee80211_add_ht_ie(skb, wk->assoc.ht_information_ie,
00360                                     sband, wk->chan, wk->assoc.smps);
00361 
00362         /* if present, add any custom non-vendor IEs that go after HT */
00363         if (wk->ie_len && wk->ie) {
00364                 noffset = ieee80211_ie_split_vendor(wk->ie, wk->ie_len,
00365                                                     offset);
00366                 pos = skb_put(skb, noffset - offset);
00367                 memcpy(pos, wk->ie + offset, noffset - offset);
00368                 offset = noffset;
00369         }
00370 
00371         if (wk->assoc.wmm_used && local->hw.queues >= 4) {
00372                 if (wk->assoc.uapsd_used) {
00373                         qos_info = local->uapsd_queues;
00374                         qos_info |= (local->uapsd_max_sp_len <<
00375                                      IEEE80211_WMM_IE_STA_QOSINFO_SP_SHIFT);
00376                 } else {
00377                         qos_info = 0;
00378                 }
00379 
00380                 pos = skb_put(skb, 9);
00381                 *pos++ = WLAN_EID_VENDOR_SPECIFIC;
00382                 *pos++ = 7; /* len */
00383                 *pos++ = 0x00; /* Microsoft OUI 00:50:F2 */
00384                 *pos++ = 0x50;
00385                 *pos++ = 0xf2;
00386                 *pos++ = 2; /* WME */
00387                 *pos++ = 0; /* WME info */
00388                 *pos++ = 1; /* WME ver */
00389                 *pos++ = qos_info;
00390         }
00391 
00392         /* add any remaining custom (i.e. vendor specific here) IEs */
00393         if (wk->ie_len && wk->ie) {
00394                 noffset = wk->ie_len;
00395                 pos = skb_put(skb, noffset - offset);
00396                 memcpy(pos, wk->ie + offset, noffset - offset);
00397         }
00398 
00399         IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT;
00400         ieee80211_tx_skb(sdata, skb);
00401 }
00402 
00403 static void ieee80211_remove_auth_bss(struct ieee80211_local *local,
00404                                       struct ieee80211_work *wk)
00405 {
00406         struct cfg80211_bss *cbss;
00407         u16 capa_val = WLAN_CAPABILITY_ESS;
00408 
00409         if (wk->probe_auth.privacy)
00410                 capa_val |= WLAN_CAPABILITY_PRIVACY;
00411 
00412         cbss = cfg80211_get_bss(local->hw.wiphy, wk->chan, wk->filter_ta,
00413                                 wk->probe_auth.ssid, wk->probe_auth.ssid_len,
00414                                 WLAN_CAPABILITY_ESS | WLAN_CAPABILITY_PRIVACY,
00415                                 capa_val);
00416         if (!cbss)
00417                 return;
00418 
00419         cfg80211_unlink_bss(local->hw.wiphy, cbss);
00420         cfg80211_put_bss(cbss);
00421 }
00422 
00423 static enum work_action __must_check
00424 ieee80211_direct_probe(struct ieee80211_work *wk)
00425 {
00426         struct ieee80211_sub_if_data *sdata = wk->sdata;
00427         struct ieee80211_local *local = sdata->local;
00428 
00429         if (!wk->probe_auth.synced) {
00430                 int ret = drv_tx_sync(local, sdata, wk->filter_ta,
00431                                       IEEE80211_TX_SYNC_AUTH);
00432                 if (ret)
00433                         return WORK_ACT_TIMEOUT;
00434         }
00435         wk->probe_auth.synced = true;
00436 
00437         wk->probe_auth.tries++;
00438         if (wk->probe_auth.tries > IEEE80211_AUTH_MAX_TRIES) {
00439                 printk(KERN_DEBUG "%s: direct probe to %pM timed out\n",
00440                        sdata->name, wk->filter_ta);
00441 
00442                 /*
00443                  * Most likely AP is not in the range so remove the
00444                  * bss struct for that AP.
00445                  */
00446                 ieee80211_remove_auth_bss(local, wk);
00447 
00448                 return WORK_ACT_TIMEOUT;
00449         }
00450 
00451         printk(KERN_DEBUG "%s: direct probe to %pM (try %d/%i)\n",
00452                sdata->name, wk->filter_ta, wk->probe_auth.tries,
00453                IEEE80211_AUTH_MAX_TRIES);
00454 
00455         /*
00456          * Direct probe is sent to broadcast address as some APs
00457          * will not answer to direct packet in unassociated state.
00458          */
00459         ieee80211_send_probe_req(sdata, NULL, wk->probe_auth.ssid,
00460                                  wk->probe_auth.ssid_len, NULL, 0,
00461                                  (u32) -1, true, false);
00462 
00463         wk->timeout = jiffies + IEEE80211_AUTH_TIMEOUT;
00464         run_again(local, wk->timeout);
00465 
00466         return WORK_ACT_NONE;
00467 }
00468 
00469 
00470 static enum work_action __must_check
00471 ieee80211_authenticate(struct ieee80211_work *wk)
00472 {
00473         struct ieee80211_sub_if_data *sdata = wk->sdata;
00474         struct ieee80211_local *local = sdata->local;
00475 
00476         if (!wk->probe_auth.synced) {
00477                 int ret = drv_tx_sync(local, sdata, wk->filter_ta,
00478                                       IEEE80211_TX_SYNC_AUTH);
00479                 if (ret)
00480                         return WORK_ACT_TIMEOUT;
00481         }
00482         wk->probe_auth.synced = true;
00483 
00484         wk->probe_auth.tries++;
00485         if (wk->probe_auth.tries > IEEE80211_AUTH_MAX_TRIES) {
00486                 printk(KERN_DEBUG "%s: authentication with %pM"
00487                        " timed out\n", sdata->name, wk->filter_ta);
00488 
00489                 /*
00490                  * Most likely AP is not in the range so remove the
00491                  * bss struct for that AP.
00492                  */
00493                 ieee80211_remove_auth_bss(local, wk);
00494 
00495                 return WORK_ACT_TIMEOUT;
00496         }
00497 
00498         printk(KERN_DEBUG "%s: authenticate with %pM (try %d)\n",
00499                sdata->name, wk->filter_ta, wk->probe_auth.tries);
00500 
00501         ieee80211_send_auth(sdata, 1, wk->probe_auth.algorithm, wk->ie,
00502                             wk->ie_len, wk->filter_ta, NULL, 0, 0);
00503         wk->probe_auth.transaction = 2;
00504 
00505         wk->timeout = jiffies + IEEE80211_AUTH_TIMEOUT;
00506         run_again(local, wk->timeout);
00507 
00508         return WORK_ACT_NONE;
00509 }
00510 
00511 static enum work_action __must_check
00512 ieee80211_associate(struct ieee80211_work *wk)
00513 {
00514         struct ieee80211_sub_if_data *sdata = wk->sdata;
00515         struct ieee80211_local *local = sdata->local;
00516 
00517         if (!wk->assoc.synced) {
00518                 int ret = drv_tx_sync(local, sdata, wk->filter_ta,
00519                                       IEEE80211_TX_SYNC_ASSOC);
00520                 if (ret)
00521                         return WORK_ACT_TIMEOUT;
00522         }
00523         wk->assoc.synced = true;
00524 
00525         wk->assoc.tries++;
00526         if (wk->assoc.tries > IEEE80211_ASSOC_MAX_TRIES) {
00527                 printk(KERN_DEBUG "%s: association with %pM"
00528                        " timed out\n",
00529                        sdata->name, wk->filter_ta);
00530 
00531                 /*
00532                  * Most likely AP is not in the range so remove the
00533                  * bss struct for that AP.
00534                  */
00535                 if (wk->assoc.bss)
00536                         cfg80211_unlink_bss(local->hw.wiphy, wk->assoc.bss);
00537 
00538                 return WORK_ACT_TIMEOUT;
00539         }
00540 
00541         printk(KERN_DEBUG "%s: associate with %pM (try %d)\n",
00542                sdata->name, wk->filter_ta, wk->assoc.tries);
00543         ieee80211_send_assoc(sdata, wk);
00544 
00545         wk->timeout = jiffies + IEEE80211_ASSOC_TIMEOUT;
00546         run_again(local, wk->timeout);
00547 
00548         return WORK_ACT_NONE;
00549 }
00550 
00551 static enum work_action __must_check
00552 ieee80211_remain_on_channel_timeout(struct ieee80211_work *wk)
00553 {
00554         /*
00555          * First time we run, do nothing -- the generic code will
00556          * have switched to the right channel etc.
00557          */
00558         if (!wk->started) {
00559                 wk->timeout = jiffies + msecs_to_jiffies(wk->remain.duration);
00560 
00561                 cfg80211_ready_on_channel(wk->sdata->dev, (unsigned long) wk,
00562                                           wk->chan, wk->chan_type,
00563                                           wk->remain.duration, GFP_KERNEL);
00564 
00565                 return WORK_ACT_NONE;
00566         }
00567 
00568         return WORK_ACT_TIMEOUT;
00569 }
00570 
00571 static enum work_action __must_check
00572 ieee80211_offchannel_tx(struct ieee80211_work *wk)
00573 {
00574         if (!wk->started) {
00575                 wk->timeout = jiffies + msecs_to_jiffies(wk->offchan_tx.wait);
00576 
00577                 /*
00578                  * After this, offchan_tx.frame remains but now is no
00579                  * longer a valid pointer -- we still need it as the
00580                  * cookie for canceling this work/status matching.
00581                  */
00582                 ieee80211_tx_skb(wk->sdata, wk->offchan_tx.frame);
00583 
00584                 return WORK_ACT_NONE;
00585         }
00586 
00587         return WORK_ACT_TIMEOUT;
00588 }
00589 
00590 static enum work_action __must_check
00591 ieee80211_assoc_beacon_wait(struct ieee80211_work *wk)
00592 {
00593         if (wk->started)
00594                 return WORK_ACT_TIMEOUT;
00595 
00596         /*
00597          * Wait up to one beacon interval ...
00598          * should this be more if we miss one?
00599          */
00600         printk(KERN_DEBUG "%s: waiting for beacon from %pM\n",
00601                wk->sdata->name, wk->filter_ta);
00602         wk->timeout = TU_TO_EXP_TIME(wk->assoc.bss->beacon_interval);
00603         return WORK_ACT_NONE;
00604 }
00605 
00606 static void ieee80211_auth_challenge(struct ieee80211_work *wk,
00607                                      struct ieee80211_mgmt *mgmt,
00608                                      size_t len)
00609 {
00610         struct ieee80211_sub_if_data *sdata = wk->sdata;
00611         u8 *pos;
00612         struct ieee802_11_elems elems;
00613 
00614         pos = mgmt->u.auth.variable;
00615         ieee802_11_parse_elems(pos, len - (pos - (u8 *) mgmt), &elems);
00616         if (!elems.challenge)
00617                 return;
00618         ieee80211_send_auth(sdata, 3, wk->probe_auth.algorithm,
00619                             elems.challenge - 2, elems.challenge_len + 2,
00620                             wk->filter_ta, wk->probe_auth.key,
00621                             wk->probe_auth.key_len, wk->probe_auth.key_idx);
00622         wk->probe_auth.transaction = 4;
00623 }
00624 
00625 static enum work_action __must_check
00626 ieee80211_rx_mgmt_auth(struct ieee80211_work *wk,
00627                        struct ieee80211_mgmt *mgmt, size_t len)
00628 {
00629         u16 auth_alg, auth_transaction, status_code;
00630 
00631         if (wk->type != IEEE80211_WORK_AUTH)
00632                 return WORK_ACT_MISMATCH;
00633 
00634         if (len < 24 + 6)
00635                 return WORK_ACT_NONE;
00636 
00637         auth_alg = le16_to_cpu(mgmt->u.auth.auth_alg);
00638         auth_transaction = le16_to_cpu(mgmt->u.auth.auth_transaction);
00639         status_code = le16_to_cpu(mgmt->u.auth.status_code);
00640 
00641         if (auth_alg != wk->probe_auth.algorithm ||
00642             auth_transaction != wk->probe_auth.transaction)
00643                 return WORK_ACT_NONE;
00644 
00645         if (status_code != WLAN_STATUS_SUCCESS) {
00646                 printk(KERN_DEBUG "%s: %pM denied authentication (status %d)\n",
00647                        wk->sdata->name, mgmt->sa, status_code);
00648                 return WORK_ACT_DONE;
00649         }
00650 
00651         switch (wk->probe_auth.algorithm) {
00652         case WLAN_AUTH_OPEN:
00653         case WLAN_AUTH_LEAP:
00654         case WLAN_AUTH_FT:
00655                 break;
00656         case WLAN_AUTH_SHARED_KEY:
00657                 if (wk->probe_auth.transaction != 4) {
00658                         ieee80211_auth_challenge(wk, mgmt, len);
00659                         /* need another frame */
00660                         return WORK_ACT_NONE;
00661                 }
00662                 break;
00663         default:
00664                 WARN_ON(1);
00665                 return WORK_ACT_NONE;
00666         }
00667 
00668         printk(KERN_DEBUG "%s: authenticated\n", wk->sdata->name);
00669         return WORK_ACT_DONE;
00670 }
00671 
00672 static enum work_action __must_check
00673 ieee80211_rx_mgmt_assoc_resp(struct ieee80211_work *wk,
00674                              struct ieee80211_mgmt *mgmt, size_t len,
00675                              bool reassoc)
00676 {
00677         struct ieee80211_sub_if_data *sdata = wk->sdata;
00678         struct ieee80211_local *local = sdata->local;
00679         u16 capab_info, status_code, aid;
00680         struct ieee802_11_elems elems;
00681         u8 *pos;
00682 
00683         if (wk->type != IEEE80211_WORK_ASSOC)
00684                 return WORK_ACT_MISMATCH;
00685 
00686         /*
00687          * AssocResp and ReassocResp have identical structure, so process both
00688          * of them in this function.
00689          */
00690 
00691         if (len < 24 + 6)
00692                 return WORK_ACT_NONE;
00693 
00694         capab_info = le16_to_cpu(mgmt->u.assoc_resp.capab_info);
00695         status_code = le16_to_cpu(mgmt->u.assoc_resp.status_code);
00696         aid = le16_to_cpu(mgmt->u.assoc_resp.aid);
00697 
00698         printk(KERN_DEBUG "%s: RX %sssocResp from %pM (capab=0x%x "
00699                "status=%d aid=%d)\n",
00700                sdata->name, reassoc ? "Rea" : "A", mgmt->sa,
00701                capab_info, status_code, (u16)(aid & ~(BIT(15) | BIT(14))));
00702 
00703         pos = mgmt->u.assoc_resp.variable;
00704         ieee802_11_parse_elems(pos, len - (pos - (u8 *) mgmt), &elems);
00705 
00706         if (status_code == WLAN_STATUS_ASSOC_REJECTED_TEMPORARILY &&
00707             elems.timeout_int && elems.timeout_int_len == 5 &&
00708             elems.timeout_int[0] == WLAN_TIMEOUT_ASSOC_COMEBACK) {
00709                 u32 tu, ms;
00710                 tu = get_unaligned_le32(elems.timeout_int + 1);
00711                 ms = tu * 1024 / 1000;
00712                 printk(KERN_DEBUG "%s: %pM rejected association temporarily; "
00713                        "comeback duration %u TU (%u ms)\n",
00714                        sdata->name, mgmt->sa, tu, ms);
00715                 wk->timeout = jiffies + msecs_to_jiffies(ms);
00716                 if (ms > IEEE80211_ASSOC_TIMEOUT)
00717                         run_again(local, wk->timeout);
00718                 return WORK_ACT_NONE;
00719         }
00720 
00721         if (status_code != WLAN_STATUS_SUCCESS)
00722                 printk(KERN_DEBUG "%s: %pM denied association (code=%d)\n",
00723                        sdata->name, mgmt->sa, status_code);
00724         else
00725                 printk(KERN_DEBUG "%s: associated\n", sdata->name);
00726 
00727         return WORK_ACT_DONE;
00728 }
00729 
00730 static enum work_action __must_check
00731 ieee80211_rx_mgmt_probe_resp(struct ieee80211_work *wk,
00732                              struct ieee80211_mgmt *mgmt, size_t len,
00733                              struct ieee80211_rx_status *rx_status)
00734 {
00735         struct ieee80211_sub_if_data *sdata = wk->sdata;
00736         struct ieee80211_local *local = sdata->local;
00737         size_t baselen;
00738 
00739         ASSERT_WORK_MTX(local);
00740 
00741         if (wk->type != IEEE80211_WORK_DIRECT_PROBE)
00742                 return WORK_ACT_MISMATCH;
00743 
00744         if (len < 24 + 12)
00745                 return WORK_ACT_NONE;
00746 
00747         baselen = (u8 *) mgmt->u.probe_resp.variable - (u8 *) mgmt;
00748         if (baselen > len)
00749                 return WORK_ACT_NONE;
00750 
00751         printk(KERN_DEBUG "%s: direct probe responded\n", sdata->name);
00752         return WORK_ACT_DONE;
00753 }
00754 
00755 static enum work_action __must_check
00756 ieee80211_rx_mgmt_beacon(struct ieee80211_work *wk,
00757                          struct ieee80211_mgmt *mgmt, size_t len)
00758 {
00759         struct ieee80211_sub_if_data *sdata = wk->sdata;
00760         struct ieee80211_local *local = sdata->local;
00761 
00762         ASSERT_WORK_MTX(local);
00763 
00764         if (wk->type != IEEE80211_WORK_ASSOC_BEACON_WAIT)
00765                 return WORK_ACT_MISMATCH;
00766 
00767         if (len < 24 + 12)
00768                 return WORK_ACT_NONE;
00769 
00770         printk(KERN_DEBUG "%s: beacon received\n", sdata->name);
00771         return WORK_ACT_DONE;
00772 }
00773 
00774 static void ieee80211_work_rx_queued_mgmt(struct ieee80211_local *local,
00775                                           struct sk_buff *skb)
00776 {
00777         struct ieee80211_rx_status *rx_status;
00778         struct ieee80211_mgmt *mgmt;
00779         struct ieee80211_work *wk;
00780         enum work_action rma = WORK_ACT_NONE;
00781         u16 fc;
00782 
00783         rx_status = (struct ieee80211_rx_status *) skb->cb;
00784         mgmt = (struct ieee80211_mgmt *) skb->data;
00785         fc = le16_to_cpu(mgmt->frame_control);
00786 
00787         mutex_lock(&local->mtx);
00788 
00789         list_for_each_entry(wk, &local->work_list, list) {
00790                 const u8 *bssid = NULL;
00791 
00792                 switch (wk->type) {
00793                 case IEEE80211_WORK_DIRECT_PROBE:
00794                 case IEEE80211_WORK_AUTH:
00795                 case IEEE80211_WORK_ASSOC:
00796                 case IEEE80211_WORK_ASSOC_BEACON_WAIT:
00797                         bssid = wk->filter_ta;
00798                         break;
00799                 default:
00800                         continue;
00801                 }
00802 
00803                 /*
00804                  * Before queuing, we already verified mgmt->sa,
00805                  * so this is needed just for matching.
00806                  */
00807                 if (compare_ether_addr(bssid, mgmt->bssid))
00808                         continue;
00809 
00810                 switch (fc & IEEE80211_FCTL_STYPE) {
00811                 case IEEE80211_STYPE_BEACON:
00812                         rma = ieee80211_rx_mgmt_beacon(wk, mgmt, skb->len);
00813                         break;
00814                 case IEEE80211_STYPE_PROBE_RESP:
00815                         rma = ieee80211_rx_mgmt_probe_resp(wk, mgmt, skb->len,
00816                                                            rx_status);
00817                         break;
00818                 case IEEE80211_STYPE_AUTH:
00819                         rma = ieee80211_rx_mgmt_auth(wk, mgmt, skb->len);
00820                         break;
00821                 case IEEE80211_STYPE_ASSOC_RESP:
00822                         rma = ieee80211_rx_mgmt_assoc_resp(wk, mgmt,
00823                                                            skb->len, false);
00824                         break;
00825                 case IEEE80211_STYPE_REASSOC_RESP:
00826                         rma = ieee80211_rx_mgmt_assoc_resp(wk, mgmt,
00827                                                            skb->len, true);
00828                         break;
00829                 default:
00830                         WARN_ON(1);
00831                         rma = WORK_ACT_NONE;
00832                 }
00833 
00834                 /*
00835                  * We've either received an unexpected frame, or we have
00836                  * multiple work items and need to match the frame to the
00837                  * right one.
00838                  */
00839                 if (rma == WORK_ACT_MISMATCH)
00840                         continue;
00841 
00842                 /*
00843                  * We've processed this frame for that work, so it can't
00844                  * belong to another work struct.
00845                  * NB: this is also required for correctness for 'rma'!
00846                  */
00847                 break;
00848         }
00849 
00850         switch (rma) {
00851         case WORK_ACT_MISMATCH:
00852                 /* ignore this unmatched frame */
00853                 break;
00854         case WORK_ACT_NONE:
00855                 break;
00856         case WORK_ACT_DONE:
00857                 list_del_rcu(&wk->list);
00858                 break;
00859         default:
00860                 WARN(1, "unexpected: %d", rma);
00861         }
00862 
00863         mutex_unlock(&local->mtx);
00864 
00865         if (rma != WORK_ACT_DONE)
00866                 goto out;
00867 
00868         switch (wk->done(wk, skb)) {
00869         case WORK_DONE_DESTROY:
00870                 free_work(wk);
00871                 break;
00872         case WORK_DONE_REQUEUE:
00873                 synchronize_rcu();
00874                 wk->started = false; /* restart */
00875                 mutex_lock(&local->mtx);
00876                 list_add_tail(&wk->list, &local->work_list);
00877                 mutex_unlock(&local->mtx);
00878         }
00879 
00880  out:
00881         kfree_skb(skb);
00882 }
00883 
00884 static bool ieee80211_work_ct_coexists(enum nl80211_channel_type wk_ct,
00885                                        enum nl80211_channel_type oper_ct)
00886 {
00887         switch (wk_ct) {
00888         case NL80211_CHAN_NO_HT:
00889                 return true;
00890         case NL80211_CHAN_HT20:
00891                 if (oper_ct != NL80211_CHAN_NO_HT)
00892                         return true;
00893                 return false;
00894         case NL80211_CHAN_HT40MINUS:
00895         case NL80211_CHAN_HT40PLUS:
00896                 return (wk_ct == oper_ct);
00897         }
00898         WARN_ON(1); /* shouldn't get here */
00899         return false;
00900 }
00901 
00902 static void ieee80211_work_timer(unsigned long data)
00903 {
00904         struct ieee80211_local *local = (void *) data;
00905 
00906         if (local->quiescing)
00907                 return;
00908 
00909         ieee80211_queue_work(&local->hw, &local->work_work);
00910 }
00911 
00912 static void ieee80211_work_work(struct work_struct *work)
00913 {
00914         struct ieee80211_local *local =
00915                 container_of(work, struct ieee80211_local, work_work);
00916         struct sk_buff *skb;
00917         struct ieee80211_work *wk, *tmp;
00918         LIST_HEAD(free_work);
00919         enum work_action rma;
00920         bool remain_off_channel = false;
00921 
00922         if (local->scanning)
00923                 return;
00924 
00925         /*
00926          * ieee80211_queue_work() should have picked up most cases,
00927          * here we'll pick the rest.
00928          */
00929         if (WARN(local->suspended, "work scheduled while going to suspend\n"))
00930                 return;
00931 
00932         /* first process frames to avoid timing out while a frame is pending */
00933         while ((skb = skb_dequeue(&local->work_skb_queue)))
00934                 ieee80211_work_rx_queued_mgmt(local, skb);
00935 
00936         mutex_lock(&local->mtx);
00937 
00938         ieee80211_recalc_idle(local);
00939 
00940         list_for_each_entry_safe(wk, tmp, &local->work_list, list) {
00941                 bool started = wk->started;
00942 
00943                 /* mark work as started if it's on the current off-channel */
00944                 if (!started && local->tmp_channel &&
00945                     wk->chan == local->tmp_channel &&
00946                     wk->chan_type == local->tmp_channel_type) {
00947                         started = true;
00948                         wk->timeout = jiffies;
00949                 }
00950 
00951                 if (!started && !local->tmp_channel) {
00952                         /*
00953                          * TODO: could optimize this by leaving the
00954                          *       station vifs in awake mode if they
00955                          *       happen to be on the same channel as
00956                          *       the requested channel
00957                          */
00958                         ieee80211_offchannel_stop_beaconing(local);
00959                         ieee80211_offchannel_stop_station(local);
00960 
00961                         local->tmp_channel = wk->chan;
00962                         local->tmp_channel_type = wk->chan_type;
00963                         ieee80211_hw_config(local, 0);
00964                         started = true;
00965                         wk->timeout = jiffies;
00966                 }
00967 
00968                 /* don't try to work with items that aren't started */
00969                 if (!started)
00970                         continue;
00971 
00972                 if (time_is_after_jiffies(wk->timeout)) {
00973                         /*
00974                          * This work item isn't supposed to be worked on
00975                          * right now, but take care to adjust the timer
00976                          * properly.
00977                          */
00978                         run_again(local, wk->timeout);
00979                         continue;
00980                 }
00981 
00982                 switch (wk->type) {
00983                 default:
00984                         WARN_ON(1);
00985                         /* nothing */
00986                         rma = WORK_ACT_NONE;
00987                         break;
00988                 case IEEE80211_WORK_ABORT:
00989                         rma = WORK_ACT_TIMEOUT;
00990                         break;
00991                 case IEEE80211_WORK_DIRECT_PROBE:
00992                         rma = ieee80211_direct_probe(wk);
00993                         break;
00994                 case IEEE80211_WORK_AUTH:
00995                         rma = ieee80211_authenticate(wk);
00996                         break;
00997                 case IEEE80211_WORK_ASSOC:
00998                         rma = ieee80211_associate(wk);
00999                         break;
01000                 case IEEE80211_WORK_REMAIN_ON_CHANNEL:
01001                         rma = ieee80211_remain_on_channel_timeout(wk);
01002                         break;
01003                 case IEEE80211_WORK_OFFCHANNEL_TX:
01004                         rma = ieee80211_offchannel_tx(wk);
01005                         break;
01006                 case IEEE80211_WORK_ASSOC_BEACON_WAIT:
01007                         rma = ieee80211_assoc_beacon_wait(wk);
01008                         break;
01009                 }
01010 
01011                 wk->started = started;
01012 
01013                 switch (rma) {
01014                 case WORK_ACT_NONE:
01015                         /* might have changed the timeout */
01016                         run_again(local, wk->timeout);
01017                         break;
01018                 case WORK_ACT_TIMEOUT:
01019                         list_del_rcu(&wk->list);
01020                         synchronize_rcu();
01021                         list_add(&wk->list, &free_work);
01022                         break;
01023                 default:
01024                         WARN(1, "unexpected: %d", rma);
01025                 }
01026         }
01027 
01028         list_for_each_entry(wk, &local->work_list, list) {
01029                 if (!wk->started)
01030                         continue;
01031                 if (wk->chan != local->tmp_channel)
01032                         continue;
01033                 if (!ieee80211_work_ct_coexists(wk->chan_type,
01034                                                 local->tmp_channel_type))
01035                         continue;
01036                 remain_off_channel = true;
01037         }
01038 
01039         if (!remain_off_channel && local->tmp_channel) {
01040                 local->tmp_channel = NULL;
01041                 /* If tmp_channel wasn't operating channel, then
01042                  * we need to go back on-channel.
01043                  * NOTE:  If we can ever be here while scannning,
01044                  * or if the hw_config() channel config logic changes,
01045                  * then we may need to do a more thorough check to see if
01046                  * we still need to do a hardware config.  Currently,
01047                  * we cannot be here while scanning, however.
01048                  */
01049                 ieee80211_hw_config(local, 0);
01050 
01051                 /* At the least, we need to disable offchannel_ps,
01052                  * so just go ahead and run the entire offchannel
01053                  * return logic here.  We *could* skip enabling
01054                  * beaconing if we were already on-oper-channel
01055                  * as a future optimization.
01056                  */
01057                 ieee80211_offchannel_return(local, true);
01058 
01059                 /* give connection some time to breathe */
01060                 run_again(local, jiffies + HZ/2);
01061         }
01062 
01063         if (list_empty(&local->work_list) && local->scan_req &&
01064             !local->scanning)
01065                 ieee80211_queue_delayed_work(&local->hw,
01066                                              &local->scan_work,
01067                                              round_jiffies_relative(0));
01068 
01069         ieee80211_recalc_idle(local);
01070 
01071         mutex_unlock(&local->mtx);
01072 
01073         list_for_each_entry_safe(wk, tmp, &free_work, list) {
01074                 wk->done(wk, NULL);
01075                 list_del(&wk->list);
01076                 kfree(wk);
01077         }
01078 }
01079 
01080 void ieee80211_add_work(struct ieee80211_work *wk)
01081 {
01082         struct ieee80211_local *local;
01083 
01084         if (WARN_ON(!wk->chan))
01085                 return;
01086 
01087         if (WARN_ON(!wk->sdata))
01088                 return;
01089 
01090         if (WARN_ON(!wk->done))
01091                 return;
01092 
01093         if (WARN_ON(!ieee80211_sdata_running(wk->sdata)))
01094                 return;
01095 
01096         wk->started = false;
01097 
01098         local = wk->sdata->local;
01099         mutex_lock(&local->mtx);
01100         list_add_tail(&wk->list, &local->work_list);
01101         mutex_unlock(&local->mtx);
01102 
01103         ieee80211_queue_work(&local->hw, &local->work_work);
01104 }
01105 
01106 void ieee80211_work_init(struct ieee80211_local *local)
01107 {
01108         INIT_LIST_HEAD(&local->work_list);
01109         setup_timer(&local->work_timer, ieee80211_work_timer,
01110                     (unsigned long)local);
01111         INIT_WORK(&local->work_work, ieee80211_work_work);
01112         skb_queue_head_init(&local->work_skb_queue);
01113 }
01114 
01115 void ieee80211_work_purge(struct ieee80211_sub_if_data *sdata)
01116 {
01117         struct ieee80211_local *local = sdata->local;
01118         struct ieee80211_work *wk;
01119         bool cleanup = false;
01120 
01121         mutex_lock(&local->mtx);
01122         list_for_each_entry(wk, &local->work_list, list) {
01123                 if (wk->sdata != sdata)
01124                         continue;
01125                 cleanup = true;
01126                 wk->type = IEEE80211_WORK_ABORT;
01127                 wk->started = true;
01128                 wk->timeout = jiffies;
01129         }
01130         mutex_unlock(&local->mtx);
01131 
01132         /* run cleanups etc. */
01133         if (cleanup)
01134                 ieee80211_work_work(&local->work_work);
01135 
01136         mutex_lock(&local->mtx);
01137         list_for_each_entry(wk, &local->work_list, list) {
01138                 if (wk->sdata != sdata)
01139                         continue;
01140                 WARN_ON(1);
01141                 break;
01142         }
01143         mutex_unlock(&local->mtx);
01144 }
01145 
01146 ieee80211_rx_result ieee80211_work_rx_mgmt(struct ieee80211_sub_if_data *sdata,
01147                                            struct sk_buff *skb)
01148 {
01149         struct ieee80211_local *local = sdata->local;
01150         struct ieee80211_mgmt *mgmt;
01151         struct ieee80211_work *wk;
01152         u16 fc;
01153 
01154         if (skb->len < 24)
01155                 return RX_DROP_MONITOR;
01156 
01157         mgmt = (struct ieee80211_mgmt *) skb->data;
01158         fc = le16_to_cpu(mgmt->frame_control);
01159 
01160         list_for_each_entry_rcu(wk, &local->work_list, list) {
01161                 if (sdata != wk->sdata)
01162                         continue;
01163                 if (compare_ether_addr(wk->filter_ta, mgmt->sa))
01164                         continue;
01165                 if (compare_ether_addr(wk->filter_ta, mgmt->bssid))
01166                         continue;
01167 
01168                 switch (fc & IEEE80211_FCTL_STYPE) {
01169                 case IEEE80211_STYPE_AUTH:
01170                 case IEEE80211_STYPE_PROBE_RESP:
01171                 case IEEE80211_STYPE_ASSOC_RESP:
01172                 case IEEE80211_STYPE_REASSOC_RESP:
01173                 case IEEE80211_STYPE_BEACON:
01174                         skb_queue_tail(&local->work_skb_queue, skb);
01175                         ieee80211_queue_work(&local->hw, &local->work_work);
01176                         return RX_QUEUED;
01177                 }
01178         }
01179 
01180         return RX_CONTINUE;
01181 }
01182 
01183 static enum work_done_result ieee80211_remain_done(struct ieee80211_work *wk,
01184                                                    struct sk_buff *skb)
01185 {
01186         /*
01187          * We are done serving the remain-on-channel command.
01188          */
01189         cfg80211_remain_on_channel_expired(wk->sdata->dev, (unsigned long) wk,
01190                                            wk->chan, wk->chan_type,
01191                                            GFP_KERNEL);
01192 
01193         return WORK_DONE_DESTROY;
01194 }
01195 
01196 int ieee80211_wk_remain_on_channel(struct ieee80211_sub_if_data *sdata,
01197                                    struct ieee80211_channel *chan,
01198                                    enum nl80211_channel_type channel_type,
01199                                    unsigned int duration, u64 *cookie)
01200 {
01201         struct ieee80211_work *wk;
01202 
01203         wk = kzalloc(sizeof(*wk), GFP_KERNEL);
01204         if (!wk)
01205                 return -ENOMEM;
01206 
01207         wk->type = IEEE80211_WORK_REMAIN_ON_CHANNEL;
01208         wk->chan = chan;
01209         wk->chan_type = channel_type;
01210         wk->sdata = sdata;
01211         wk->done = ieee80211_remain_done;
01212 
01213         wk->remain.duration = duration;
01214 
01215         *cookie = (unsigned long) wk;
01216 
01217         ieee80211_add_work(wk);
01218 
01219         return 0;
01220 }
01221 
01222 int ieee80211_wk_cancel_remain_on_channel(struct ieee80211_sub_if_data *sdata,
01223                                           u64 cookie)
01224 {
01225         struct ieee80211_local *local = sdata->local;
01226         struct ieee80211_work *wk, *tmp;
01227         bool found = false;
01228 
01229         mutex_lock(&local->mtx);
01230         list_for_each_entry_safe(wk, tmp, &local->work_list, list) {
01231                 if ((unsigned long) wk == cookie) {
01232                         wk->timeout = jiffies;
01233                         found = true;
01234                         break;
01235                 }
01236         }
01237         mutex_unlock(&local->mtx);
01238 
01239         if (!found)
01240                 return -ENOENT;
01241 
01242         ieee80211_queue_work(&local->hw, &local->work_work);
01243 
01244         return 0;
01245 }


ros_rt_wmp
Author(s): Danilo Tardioli, dantard@unizar.es
autogenerated on Mon Oct 6 2014 08:27:11