| Jouni Malinen | 8ca21f0 | 2009-03-03 19:23:27 +0200 | [diff] [blame] | 1 | /* | 
 | 2 |  * Copyright (c) 2008-2009 Atheros Communications Inc. | 
 | 3 |  * | 
 | 4 |  * Permission to use, copy, modify, and/or distribute this software for any | 
 | 5 |  * purpose with or without fee is hereby granted, provided that the above | 
 | 6 |  * copyright notice and this permission notice appear in all copies. | 
 | 7 |  * | 
 | 8 |  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | 
 | 9 |  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | 
 | 10 |  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | 
 | 11 |  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | 
 | 12 |  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | 
 | 13 |  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | 
 | 14 |  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | 
 | 15 |  */ | 
 | 16 |  | 
| Tejun Heo | 5a0e3ad | 2010-03-24 17:04:11 +0900 | [diff] [blame] | 17 | #include <linux/slab.h> | 
 | 18 |  | 
| Jouni Malinen | 8ca21f0 | 2009-03-03 19:23:27 +0200 | [diff] [blame] | 19 | #include "ath9k.h" | 
 | 20 |  | 
 | 21 | struct ath9k_vif_iter_data { | 
| Felix Fietkau | 31a0164 | 2010-09-14 18:37:19 +0200 | [diff] [blame] | 22 | 	const u8 *hw_macaddr; | 
 | 23 | 	u8 mask[ETH_ALEN]; | 
| Jouni Malinen | 8ca21f0 | 2009-03-03 19:23:27 +0200 | [diff] [blame] | 24 | }; | 
 | 25 |  | 
 | 26 | static void ath9k_vif_iter(void *data, u8 *mac, struct ieee80211_vif *vif) | 
 | 27 | { | 
 | 28 | 	struct ath9k_vif_iter_data *iter_data = data; | 
| Felix Fietkau | 31a0164 | 2010-09-14 18:37:19 +0200 | [diff] [blame] | 29 | 	int i; | 
| Jouni Malinen | 8ca21f0 | 2009-03-03 19:23:27 +0200 | [diff] [blame] | 30 |  | 
| Felix Fietkau | 31a0164 | 2010-09-14 18:37:19 +0200 | [diff] [blame] | 31 | 	for (i = 0; i < ETH_ALEN; i++) | 
 | 32 | 		iter_data->mask[i] &= ~(iter_data->hw_macaddr[i] ^ mac[i]); | 
| Jouni Malinen | 8ca21f0 | 2009-03-03 19:23:27 +0200 | [diff] [blame] | 33 | } | 
 | 34 |  | 
| Felix Fietkau | 31a0164 | 2010-09-14 18:37:19 +0200 | [diff] [blame] | 35 | void ath9k_set_bssid_mask(struct ieee80211_hw *hw, struct ieee80211_vif *vif) | 
| Jouni Malinen | 8ca21f0 | 2009-03-03 19:23:27 +0200 | [diff] [blame] | 36 | { | 
| Jouni Malinen | bce048d | 2009-03-03 19:23:28 +0200 | [diff] [blame] | 37 | 	struct ath_wiphy *aphy = hw->priv; | 
 | 38 | 	struct ath_softc *sc = aphy->sc; | 
| Luis R. Rodriguez | 1510718 | 2009-09-10 09:22:37 -0700 | [diff] [blame] | 39 | 	struct ath_common *common = ath9k_hw_common(sc->sc_ah); | 
| Jouni Malinen | 8ca21f0 | 2009-03-03 19:23:27 +0200 | [diff] [blame] | 40 | 	struct ath9k_vif_iter_data iter_data; | 
| Felix Fietkau | 31a0164 | 2010-09-14 18:37:19 +0200 | [diff] [blame] | 41 | 	int i; | 
| Jouni Malinen | 8ca21f0 | 2009-03-03 19:23:27 +0200 | [diff] [blame] | 42 |  | 
 | 43 | 	/* | 
| Felix Fietkau | 31a0164 | 2010-09-14 18:37:19 +0200 | [diff] [blame] | 44 | 	 * Use the hardware MAC address as reference, the hardware uses it | 
 | 45 | 	 * together with the BSSID mask when matching addresses. | 
| Jouni Malinen | 8ca21f0 | 2009-03-03 19:23:27 +0200 | [diff] [blame] | 46 | 	 */ | 
| Felix Fietkau | 31a0164 | 2010-09-14 18:37:19 +0200 | [diff] [blame] | 47 | 	iter_data.hw_macaddr = common->macaddr; | 
 | 48 | 	memset(&iter_data.mask, 0xff, ETH_ALEN); | 
 | 49 |  | 
 | 50 | 	if (vif) | 
 | 51 | 		ath9k_vif_iter(&iter_data, vif->addr, vif); | 
| Jouni Malinen | 8ca21f0 | 2009-03-03 19:23:27 +0200 | [diff] [blame] | 52 |  | 
 | 53 | 	/* Get list of all active MAC addresses */ | 
| Jouni Malinen | c52f33d | 2009-03-03 19:23:29 +0200 | [diff] [blame] | 54 | 	spin_lock_bh(&sc->wiphy_lock); | 
 | 55 | 	ieee80211_iterate_active_interfaces_atomic(sc->hw, ath9k_vif_iter, | 
| Jouni Malinen | 8ca21f0 | 2009-03-03 19:23:27 +0200 | [diff] [blame] | 56 | 						   &iter_data); | 
| Jouni Malinen | c52f33d | 2009-03-03 19:23:29 +0200 | [diff] [blame] | 57 | 	for (i = 0; i < sc->num_sec_wiphy; i++) { | 
 | 58 | 		if (sc->sec_wiphy[i] == NULL) | 
 | 59 | 			continue; | 
 | 60 | 		ieee80211_iterate_active_interfaces_atomic( | 
 | 61 | 			sc->sec_wiphy[i]->hw, ath9k_vif_iter, &iter_data); | 
 | 62 | 	} | 
 | 63 | 	spin_unlock_bh(&sc->wiphy_lock); | 
| Jouni Malinen | 8ca21f0 | 2009-03-03 19:23:27 +0200 | [diff] [blame] | 64 |  | 
| Felix Fietkau | 31a0164 | 2010-09-14 18:37:19 +0200 | [diff] [blame] | 65 | 	memcpy(common->bssidmask, iter_data.mask, ETH_ALEN); | 
| Luis R. Rodriguez | 13b8155 | 2009-09-10 17:52:45 -0700 | [diff] [blame] | 66 | 	ath_hw_setbssidmask(common); | 
| Jouni Malinen | 8ca21f0 | 2009-03-03 19:23:27 +0200 | [diff] [blame] | 67 | } | 
| Jouni Malinen | c52f33d | 2009-03-03 19:23:29 +0200 | [diff] [blame] | 68 |  | 
 | 69 | int ath9k_wiphy_add(struct ath_softc *sc) | 
 | 70 | { | 
 | 71 | 	int i, error; | 
 | 72 | 	struct ath_wiphy *aphy; | 
| Luis R. Rodriguez | 1510718 | 2009-09-10 09:22:37 -0700 | [diff] [blame] | 73 | 	struct ath_common *common = ath9k_hw_common(sc->sc_ah); | 
| Jouni Malinen | c52f33d | 2009-03-03 19:23:29 +0200 | [diff] [blame] | 74 | 	struct ieee80211_hw *hw; | 
 | 75 | 	u8 addr[ETH_ALEN]; | 
 | 76 |  | 
 | 77 | 	hw = ieee80211_alloc_hw(sizeof(struct ath_wiphy), &ath9k_ops); | 
 | 78 | 	if (hw == NULL) | 
 | 79 | 		return -ENOMEM; | 
 | 80 |  | 
 | 81 | 	spin_lock_bh(&sc->wiphy_lock); | 
 | 82 | 	for (i = 0; i < sc->num_sec_wiphy; i++) { | 
 | 83 | 		if (sc->sec_wiphy[i] == NULL) | 
 | 84 | 			break; | 
 | 85 | 	} | 
 | 86 |  | 
 | 87 | 	if (i == sc->num_sec_wiphy) { | 
 | 88 | 		/* No empty slot available; increase array length */ | 
 | 89 | 		struct ath_wiphy **n; | 
 | 90 | 		n = krealloc(sc->sec_wiphy, | 
 | 91 | 			     (sc->num_sec_wiphy + 1) * | 
 | 92 | 			     sizeof(struct ath_wiphy *), | 
 | 93 | 			     GFP_ATOMIC); | 
 | 94 | 		if (n == NULL) { | 
 | 95 | 			spin_unlock_bh(&sc->wiphy_lock); | 
 | 96 | 			ieee80211_free_hw(hw); | 
 | 97 | 			return -ENOMEM; | 
 | 98 | 		} | 
 | 99 | 		n[i] = NULL; | 
 | 100 | 		sc->sec_wiphy = n; | 
 | 101 | 		sc->num_sec_wiphy++; | 
 | 102 | 	} | 
 | 103 |  | 
 | 104 | 	SET_IEEE80211_DEV(hw, sc->dev); | 
 | 105 |  | 
 | 106 | 	aphy = hw->priv; | 
 | 107 | 	aphy->sc = sc; | 
 | 108 | 	aphy->hw = hw; | 
 | 109 | 	sc->sec_wiphy[i] = aphy; | 
 | 110 | 	spin_unlock_bh(&sc->wiphy_lock); | 
 | 111 |  | 
| Luis R. Rodriguez | 1510718 | 2009-09-10 09:22:37 -0700 | [diff] [blame] | 112 | 	memcpy(addr, common->macaddr, ETH_ALEN); | 
| Jouni Malinen | c52f33d | 2009-03-03 19:23:29 +0200 | [diff] [blame] | 113 | 	addr[0] |= 0x02; /* Locally managed address */ | 
 | 114 | 	/* | 
 | 115 | 	 * XOR virtual wiphy index into the least significant bits to generate | 
 | 116 | 	 * a different MAC address for each virtual wiphy. | 
 | 117 | 	 */ | 
 | 118 | 	addr[5] ^= i & 0xff; | 
 | 119 | 	addr[4] ^= (i & 0xff00) >> 8; | 
 | 120 | 	addr[3] ^= (i & 0xff0000) >> 16; | 
 | 121 |  | 
 | 122 | 	SET_IEEE80211_PERM_ADDR(hw, addr); | 
 | 123 |  | 
| Sujith | 285f2dd | 2010-01-08 10:36:07 +0530 | [diff] [blame] | 124 | 	ath9k_set_hw_capab(sc, hw); | 
| Jouni Malinen | c52f33d | 2009-03-03 19:23:29 +0200 | [diff] [blame] | 125 |  | 
 | 126 | 	error = ieee80211_register_hw(hw); | 
 | 127 |  | 
| Jouni Malinen | f98c3bd | 2009-03-03 19:23:39 +0200 | [diff] [blame] | 128 | 	if (error == 0) { | 
 | 129 | 		/* Make sure wiphy scheduler is started (if enabled) */ | 
 | 130 | 		ath9k_wiphy_set_scheduler(sc, sc->wiphy_scheduler_int); | 
 | 131 | 	} | 
 | 132 |  | 
| Jouni Malinen | c52f33d | 2009-03-03 19:23:29 +0200 | [diff] [blame] | 133 | 	return error; | 
 | 134 | } | 
 | 135 |  | 
 | 136 | int ath9k_wiphy_del(struct ath_wiphy *aphy) | 
 | 137 | { | 
 | 138 | 	struct ath_softc *sc = aphy->sc; | 
 | 139 | 	int i; | 
 | 140 |  | 
 | 141 | 	spin_lock_bh(&sc->wiphy_lock); | 
 | 142 | 	for (i = 0; i < sc->num_sec_wiphy; i++) { | 
 | 143 | 		if (aphy == sc->sec_wiphy[i]) { | 
 | 144 | 			sc->sec_wiphy[i] = NULL; | 
 | 145 | 			spin_unlock_bh(&sc->wiphy_lock); | 
 | 146 | 			ieee80211_unregister_hw(aphy->hw); | 
 | 147 | 			ieee80211_free_hw(aphy->hw); | 
 | 148 | 			return 0; | 
 | 149 | 		} | 
 | 150 | 	} | 
 | 151 | 	spin_unlock_bh(&sc->wiphy_lock); | 
 | 152 | 	return -ENOENT; | 
 | 153 | } | 
| Jouni Malinen | f0ed85c | 2009-03-03 19:23:31 +0200 | [diff] [blame] | 154 |  | 
 | 155 | static int ath9k_send_nullfunc(struct ath_wiphy *aphy, | 
 | 156 | 			       struct ieee80211_vif *vif, const u8 *bssid, | 
 | 157 | 			       int ps) | 
 | 158 | { | 
 | 159 | 	struct ath_softc *sc = aphy->sc; | 
 | 160 | 	struct ath_tx_control txctl; | 
 | 161 | 	struct sk_buff *skb; | 
 | 162 | 	struct ieee80211_hdr *hdr; | 
 | 163 | 	__le16 fc; | 
 | 164 | 	struct ieee80211_tx_info *info; | 
 | 165 |  | 
 | 166 | 	skb = dev_alloc_skb(24); | 
 | 167 | 	if (skb == NULL) | 
 | 168 | 		return -ENOMEM; | 
 | 169 | 	hdr = (struct ieee80211_hdr *) skb_put(skb, 24); | 
 | 170 | 	memset(hdr, 0, 24); | 
 | 171 | 	fc = cpu_to_le16(IEEE80211_FTYPE_DATA | IEEE80211_STYPE_NULLFUNC | | 
 | 172 | 			 IEEE80211_FCTL_TODS); | 
 | 173 | 	if (ps) | 
 | 174 | 		fc |= cpu_to_le16(IEEE80211_FCTL_PM); | 
 | 175 | 	hdr->frame_control = fc; | 
 | 176 | 	memcpy(hdr->addr1, bssid, ETH_ALEN); | 
 | 177 | 	memcpy(hdr->addr2, aphy->hw->wiphy->perm_addr, ETH_ALEN); | 
 | 178 | 	memcpy(hdr->addr3, bssid, ETH_ALEN); | 
 | 179 |  | 
 | 180 | 	info = IEEE80211_SKB_CB(skb); | 
 | 181 | 	memset(info, 0, sizeof(*info)); | 
 | 182 | 	info->flags = IEEE80211_TX_CTL_REQ_TX_STATUS; | 
 | 183 | 	info->control.vif = vif; | 
 | 184 | 	info->control.rates[0].idx = 0; | 
 | 185 | 	info->control.rates[0].count = 4; | 
 | 186 | 	info->control.rates[1].idx = -1; | 
 | 187 |  | 
 | 188 | 	memset(&txctl, 0, sizeof(struct ath_tx_control)); | 
| Felix Fietkau | 1d2231e | 2010-06-12 00:33:51 -0400 | [diff] [blame] | 189 | 	txctl.txq = &sc->tx.txq[sc->tx.hwq_map[WME_AC_VO]]; | 
| Pavel Roskin | c81494d | 2010-03-31 18:05:25 -0400 | [diff] [blame] | 190 | 	txctl.frame_type = ps ? ATH9K_IFT_PAUSE : ATH9K_IFT_UNPAUSE; | 
| Jouni Malinen | f0ed85c | 2009-03-03 19:23:31 +0200 | [diff] [blame] | 191 |  | 
 | 192 | 	if (ath_tx_start(aphy->hw, skb, &txctl) != 0) | 
 | 193 | 		goto exit; | 
 | 194 |  | 
 | 195 | 	return 0; | 
 | 196 | exit: | 
 | 197 | 	dev_kfree_skb_any(skb); | 
 | 198 | 	return -1; | 
 | 199 | } | 
 | 200 |  | 
| Jouni Malinen | 0e2dedf | 2009-03-03 19:23:32 +0200 | [diff] [blame] | 201 | static bool __ath9k_wiphy_pausing(struct ath_softc *sc) | 
 | 202 | { | 
 | 203 | 	int i; | 
 | 204 | 	if (sc->pri_wiphy->state == ATH_WIPHY_PAUSING) | 
 | 205 | 		return true; | 
 | 206 | 	for (i = 0; i < sc->num_sec_wiphy; i++) { | 
 | 207 | 		if (sc->sec_wiphy[i] && | 
 | 208 | 		    sc->sec_wiphy[i]->state == ATH_WIPHY_PAUSING) | 
 | 209 | 			return true; | 
 | 210 | 	} | 
 | 211 | 	return false; | 
 | 212 | } | 
 | 213 |  | 
 | 214 | static bool ath9k_wiphy_pausing(struct ath_softc *sc) | 
 | 215 | { | 
 | 216 | 	bool ret; | 
 | 217 | 	spin_lock_bh(&sc->wiphy_lock); | 
 | 218 | 	ret = __ath9k_wiphy_pausing(sc); | 
 | 219 | 	spin_unlock_bh(&sc->wiphy_lock); | 
 | 220 | 	return ret; | 
 | 221 | } | 
 | 222 |  | 
| Jouni Malinen | 8089cc4 | 2009-03-03 19:23:38 +0200 | [diff] [blame] | 223 | static bool __ath9k_wiphy_scanning(struct ath_softc *sc) | 
 | 224 | { | 
 | 225 | 	int i; | 
 | 226 | 	if (sc->pri_wiphy->state == ATH_WIPHY_SCAN) | 
 | 227 | 		return true; | 
 | 228 | 	for (i = 0; i < sc->num_sec_wiphy; i++) { | 
 | 229 | 		if (sc->sec_wiphy[i] && | 
 | 230 | 		    sc->sec_wiphy[i]->state == ATH_WIPHY_SCAN) | 
 | 231 | 			return true; | 
 | 232 | 	} | 
 | 233 | 	return false; | 
 | 234 | } | 
 | 235 |  | 
 | 236 | bool ath9k_wiphy_scanning(struct ath_softc *sc) | 
 | 237 | { | 
 | 238 | 	bool ret; | 
 | 239 | 	spin_lock_bh(&sc->wiphy_lock); | 
 | 240 | 	ret = __ath9k_wiphy_scanning(sc); | 
 | 241 | 	spin_unlock_bh(&sc->wiphy_lock); | 
 | 242 | 	return ret; | 
 | 243 | } | 
 | 244 |  | 
| Jouni Malinen | 0e2dedf | 2009-03-03 19:23:32 +0200 | [diff] [blame] | 245 | static int __ath9k_wiphy_unpause(struct ath_wiphy *aphy); | 
 | 246 |  | 
 | 247 | /* caller must hold wiphy_lock */ | 
 | 248 | static void __ath9k_wiphy_unpause_ch(struct ath_wiphy *aphy) | 
 | 249 | { | 
 | 250 | 	if (aphy == NULL) | 
 | 251 | 		return; | 
 | 252 | 	if (aphy->chan_idx != aphy->sc->chan_idx) | 
 | 253 | 		return; /* wiphy not on the selected channel */ | 
 | 254 | 	__ath9k_wiphy_unpause(aphy); | 
 | 255 | } | 
 | 256 |  | 
 | 257 | static void ath9k_wiphy_unpause_channel(struct ath_softc *sc) | 
 | 258 | { | 
 | 259 | 	int i; | 
 | 260 | 	spin_lock_bh(&sc->wiphy_lock); | 
 | 261 | 	__ath9k_wiphy_unpause_ch(sc->pri_wiphy); | 
 | 262 | 	for (i = 0; i < sc->num_sec_wiphy; i++) | 
 | 263 | 		__ath9k_wiphy_unpause_ch(sc->sec_wiphy[i]); | 
 | 264 | 	spin_unlock_bh(&sc->wiphy_lock); | 
 | 265 | } | 
 | 266 |  | 
 | 267 | void ath9k_wiphy_chan_work(struct work_struct *work) | 
 | 268 | { | 
 | 269 | 	struct ath_softc *sc = container_of(work, struct ath_softc, chan_work); | 
| Luis R. Rodriguez | 1bdf6c3 | 2009-10-28 13:39:40 -0700 | [diff] [blame] | 270 | 	struct ath_common *common = ath9k_hw_common(sc->sc_ah); | 
| Jouni Malinen | 0e2dedf | 2009-03-03 19:23:32 +0200 | [diff] [blame] | 271 | 	struct ath_wiphy *aphy = sc->next_wiphy; | 
 | 272 |  | 
 | 273 | 	if (aphy == NULL) | 
 | 274 | 		return; | 
 | 275 |  | 
 | 276 | 	/* | 
 | 277 | 	 * All pending interfaces paused; ready to change | 
 | 278 | 	 * channels. | 
 | 279 | 	 */ | 
 | 280 |  | 
 | 281 | 	/* Change channels */ | 
 | 282 | 	mutex_lock(&sc->mutex); | 
 | 283 | 	/* XXX: remove me eventually */ | 
 | 284 | 	ath9k_update_ichannel(sc, aphy->hw, | 
 | 285 | 			      &sc->sc_ah->channels[sc->chan_idx]); | 
| Luis R. Rodriguez | 1bdf6c3 | 2009-10-28 13:39:40 -0700 | [diff] [blame] | 286 |  | 
 | 287 | 	/* sync hw configuration for hw code */ | 
 | 288 | 	common->hw = aphy->hw; | 
 | 289 |  | 
| Jouni Malinen | 0e2dedf | 2009-03-03 19:23:32 +0200 | [diff] [blame] | 290 | 	ath_update_chainmask(sc, sc->chan_is_ht); | 
 | 291 | 	if (ath_set_channel(sc, aphy->hw, | 
 | 292 | 			    &sc->sc_ah->channels[sc->chan_idx]) < 0) { | 
 | 293 | 		printk(KERN_DEBUG "ath9k: Failed to set channel for new " | 
 | 294 | 		       "virtual wiphy\n"); | 
 | 295 | 		mutex_unlock(&sc->mutex); | 
 | 296 | 		return; | 
 | 297 | 	} | 
 | 298 | 	mutex_unlock(&sc->mutex); | 
 | 299 |  | 
 | 300 | 	ath9k_wiphy_unpause_channel(sc); | 
 | 301 | } | 
 | 302 |  | 
| Jouni Malinen | f0ed85c | 2009-03-03 19:23:31 +0200 | [diff] [blame] | 303 | /* | 
 | 304 |  * ath9k version of ieee80211_tx_status() for TX frames that are generated | 
 | 305 |  * internally in the driver. | 
 | 306 |  */ | 
 | 307 | void ath9k_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb) | 
 | 308 | { | 
 | 309 | 	struct ath_wiphy *aphy = hw->priv; | 
| Jouni Malinen | f0ed85c | 2009-03-03 19:23:31 +0200 | [diff] [blame] | 310 | 	struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb); | 
| Jouni Malinen | f0ed85c | 2009-03-03 19:23:31 +0200 | [diff] [blame] | 311 |  | 
| Felix Fietkau | 827e69b | 2009-11-15 23:09:25 +0100 | [diff] [blame] | 312 | 	if ((tx_info->pad[0] & ATH_TX_INFO_FRAME_TYPE_PAUSE) && | 
| Jouni Malinen | f0ed85c | 2009-03-03 19:23:31 +0200 | [diff] [blame] | 313 | 	    aphy->state == ATH_WIPHY_PAUSING) { | 
| Felix Fietkau | 827e69b | 2009-11-15 23:09:25 +0100 | [diff] [blame] | 314 | 		if (!(tx_info->flags & IEEE80211_TX_STAT_ACK)) { | 
| Jouni Malinen | f0ed85c | 2009-03-03 19:23:31 +0200 | [diff] [blame] | 315 | 			printk(KERN_DEBUG "ath9k: %s: no ACK for pause " | 
 | 316 | 			       "frame\n", wiphy_name(hw->wiphy)); | 
 | 317 | 			/* | 
 | 318 | 			 * The AP did not reply; ignore this to allow us to | 
 | 319 | 			 * continue. | 
 | 320 | 			 */ | 
 | 321 | 		} | 
 | 322 | 		aphy->state = ATH_WIPHY_PAUSED; | 
| Jouni Malinen | 0e2dedf | 2009-03-03 19:23:32 +0200 | [diff] [blame] | 323 | 		if (!ath9k_wiphy_pausing(aphy->sc)) { | 
 | 324 | 			/* | 
 | 325 | 			 * Drop from tasklet to work to allow mutex for channel | 
 | 326 | 			 * change. | 
 | 327 | 			 */ | 
| Luis R. Rodriguez | 42935ec | 2009-07-29 20:08:07 -0400 | [diff] [blame] | 328 | 			ieee80211_queue_work(aphy->sc->hw, | 
| Jouni Malinen | 0e2dedf | 2009-03-03 19:23:32 +0200 | [diff] [blame] | 329 | 				   &aphy->sc->chan_work); | 
 | 330 | 		} | 
| Jouni Malinen | f0ed85c | 2009-03-03 19:23:31 +0200 | [diff] [blame] | 331 | 	} | 
 | 332 |  | 
| Jouni Malinen | f0ed85c | 2009-03-03 19:23:31 +0200 | [diff] [blame] | 333 | 	dev_kfree_skb(skb); | 
 | 334 | } | 
 | 335 |  | 
| Jouni Malinen | 0e2dedf | 2009-03-03 19:23:32 +0200 | [diff] [blame] | 336 | static void ath9k_mark_paused(struct ath_wiphy *aphy) | 
 | 337 | { | 
 | 338 | 	struct ath_softc *sc = aphy->sc; | 
 | 339 | 	aphy->state = ATH_WIPHY_PAUSED; | 
 | 340 | 	if (!__ath9k_wiphy_pausing(sc)) | 
| Luis R. Rodriguez | 42935ec | 2009-07-29 20:08:07 -0400 | [diff] [blame] | 341 | 		ieee80211_queue_work(sc->hw, &sc->chan_work); | 
| Jouni Malinen | 0e2dedf | 2009-03-03 19:23:32 +0200 | [diff] [blame] | 342 | } | 
 | 343 |  | 
| Jouni Malinen | f0ed85c | 2009-03-03 19:23:31 +0200 | [diff] [blame] | 344 | static void ath9k_pause_iter(void *data, u8 *mac, struct ieee80211_vif *vif) | 
 | 345 | { | 
 | 346 | 	struct ath_wiphy *aphy = data; | 
 | 347 | 	struct ath_vif *avp = (void *) vif->drv_priv; | 
 | 348 |  | 
 | 349 | 	switch (vif->type) { | 
 | 350 | 	case NL80211_IFTYPE_STATION: | 
 | 351 | 		if (!vif->bss_conf.assoc) { | 
| Jouni Malinen | 0e2dedf | 2009-03-03 19:23:32 +0200 | [diff] [blame] | 352 | 			ath9k_mark_paused(aphy); | 
| Jouni Malinen | f0ed85c | 2009-03-03 19:23:31 +0200 | [diff] [blame] | 353 | 			break; | 
 | 354 | 		} | 
 | 355 | 		/* TODO: could avoid this if already in PS mode */ | 
| Jouni Malinen | 0e2dedf | 2009-03-03 19:23:32 +0200 | [diff] [blame] | 356 | 		if (ath9k_send_nullfunc(aphy, vif, avp->bssid, 1)) { | 
 | 357 | 			printk(KERN_DEBUG "%s: failed to send PS nullfunc\n", | 
 | 358 | 			       __func__); | 
 | 359 | 			ath9k_mark_paused(aphy); | 
 | 360 | 		} | 
| Jouni Malinen | f0ed85c | 2009-03-03 19:23:31 +0200 | [diff] [blame] | 361 | 		break; | 
 | 362 | 	case NL80211_IFTYPE_AP: | 
 | 363 | 		/* Beacon transmission is paused by aphy->state change */ | 
| Jouni Malinen | 0e2dedf | 2009-03-03 19:23:32 +0200 | [diff] [blame] | 364 | 		ath9k_mark_paused(aphy); | 
| Jouni Malinen | f0ed85c | 2009-03-03 19:23:31 +0200 | [diff] [blame] | 365 | 		break; | 
 | 366 | 	default: | 
 | 367 | 		break; | 
 | 368 | 	} | 
 | 369 | } | 
 | 370 |  | 
 | 371 | /* caller must hold wiphy_lock */ | 
 | 372 | static int __ath9k_wiphy_pause(struct ath_wiphy *aphy) | 
 | 373 | { | 
 | 374 | 	ieee80211_stop_queues(aphy->hw); | 
 | 375 | 	aphy->state = ATH_WIPHY_PAUSING; | 
 | 376 | 	/* | 
 | 377 | 	 * TODO: handle PAUSING->PAUSED for the case where there are multiple | 
 | 378 | 	 * active vifs (now we do it on the first vif getting ready; should be | 
 | 379 | 	 * on the last) | 
 | 380 | 	 */ | 
 | 381 | 	ieee80211_iterate_active_interfaces_atomic(aphy->hw, ath9k_pause_iter, | 
 | 382 | 						   aphy); | 
 | 383 | 	return 0; | 
 | 384 | } | 
 | 385 |  | 
 | 386 | int ath9k_wiphy_pause(struct ath_wiphy *aphy) | 
 | 387 | { | 
 | 388 | 	int ret; | 
 | 389 | 	spin_lock_bh(&aphy->sc->wiphy_lock); | 
 | 390 | 	ret = __ath9k_wiphy_pause(aphy); | 
 | 391 | 	spin_unlock_bh(&aphy->sc->wiphy_lock); | 
 | 392 | 	return ret; | 
 | 393 | } | 
 | 394 |  | 
 | 395 | static void ath9k_unpause_iter(void *data, u8 *mac, struct ieee80211_vif *vif) | 
 | 396 | { | 
 | 397 | 	struct ath_wiphy *aphy = data; | 
 | 398 | 	struct ath_vif *avp = (void *) vif->drv_priv; | 
 | 399 |  | 
 | 400 | 	switch (vif->type) { | 
 | 401 | 	case NL80211_IFTYPE_STATION: | 
 | 402 | 		if (!vif->bss_conf.assoc) | 
 | 403 | 			break; | 
 | 404 | 		ath9k_send_nullfunc(aphy, vif, avp->bssid, 0); | 
 | 405 | 		break; | 
 | 406 | 	case NL80211_IFTYPE_AP: | 
 | 407 | 		/* Beacon transmission is re-enabled by aphy->state change */ | 
 | 408 | 		break; | 
 | 409 | 	default: | 
 | 410 | 		break; | 
 | 411 | 	} | 
 | 412 | } | 
 | 413 |  | 
 | 414 | /* caller must hold wiphy_lock */ | 
 | 415 | static int __ath9k_wiphy_unpause(struct ath_wiphy *aphy) | 
 | 416 | { | 
 | 417 | 	ieee80211_iterate_active_interfaces_atomic(aphy->hw, | 
 | 418 | 						   ath9k_unpause_iter, aphy); | 
 | 419 | 	aphy->state = ATH_WIPHY_ACTIVE; | 
 | 420 | 	ieee80211_wake_queues(aphy->hw); | 
 | 421 | 	return 0; | 
 | 422 | } | 
 | 423 |  | 
 | 424 | int ath9k_wiphy_unpause(struct ath_wiphy *aphy) | 
 | 425 | { | 
 | 426 | 	int ret; | 
 | 427 | 	spin_lock_bh(&aphy->sc->wiphy_lock); | 
 | 428 | 	ret = __ath9k_wiphy_unpause(aphy); | 
 | 429 | 	spin_unlock_bh(&aphy->sc->wiphy_lock); | 
 | 430 | 	return ret; | 
 | 431 | } | 
| Jouni Malinen | 0e2dedf | 2009-03-03 19:23:32 +0200 | [diff] [blame] | 432 |  | 
| Jouni Malinen | 7ec3e51 | 2009-03-03 19:23:37 +0200 | [diff] [blame] | 433 | static void __ath9k_wiphy_mark_all_paused(struct ath_softc *sc) | 
 | 434 | { | 
 | 435 | 	int i; | 
 | 436 | 	if (sc->pri_wiphy->state != ATH_WIPHY_INACTIVE) | 
 | 437 | 		sc->pri_wiphy->state = ATH_WIPHY_PAUSED; | 
 | 438 | 	for (i = 0; i < sc->num_sec_wiphy; i++) { | 
 | 439 | 		if (sc->sec_wiphy[i] && | 
 | 440 | 		    sc->sec_wiphy[i]->state != ATH_WIPHY_INACTIVE) | 
 | 441 | 			sc->sec_wiphy[i]->state = ATH_WIPHY_PAUSED; | 
 | 442 | 	} | 
 | 443 | } | 
 | 444 |  | 
| Jouni Malinen | 0e2dedf | 2009-03-03 19:23:32 +0200 | [diff] [blame] | 445 | /* caller must hold wiphy_lock */ | 
 | 446 | static void __ath9k_wiphy_pause_all(struct ath_softc *sc) | 
 | 447 | { | 
 | 448 | 	int i; | 
 | 449 | 	if (sc->pri_wiphy->state == ATH_WIPHY_ACTIVE) | 
 | 450 | 		__ath9k_wiphy_pause(sc->pri_wiphy); | 
 | 451 | 	for (i = 0; i < sc->num_sec_wiphy; i++) { | 
 | 452 | 		if (sc->sec_wiphy[i] && | 
 | 453 | 		    sc->sec_wiphy[i]->state == ATH_WIPHY_ACTIVE) | 
 | 454 | 			__ath9k_wiphy_pause(sc->sec_wiphy[i]); | 
 | 455 | 	} | 
 | 456 | } | 
 | 457 |  | 
 | 458 | int ath9k_wiphy_select(struct ath_wiphy *aphy) | 
 | 459 | { | 
 | 460 | 	struct ath_softc *sc = aphy->sc; | 
 | 461 | 	bool now; | 
 | 462 |  | 
 | 463 | 	spin_lock_bh(&sc->wiphy_lock); | 
| Jouni Malinen | 8089cc4 | 2009-03-03 19:23:38 +0200 | [diff] [blame] | 464 | 	if (__ath9k_wiphy_scanning(sc)) { | 
 | 465 | 		/* | 
 | 466 | 		 * For now, we are using mac80211 sw scan and it expects to | 
 | 467 | 		 * have full control over channel changes, so avoid wiphy | 
 | 468 | 		 * scheduling during a scan. This could be optimized if the | 
 | 469 | 		 * scanning control were moved into the driver. | 
 | 470 | 		 */ | 
 | 471 | 		spin_unlock_bh(&sc->wiphy_lock); | 
 | 472 | 		return -EBUSY; | 
 | 473 | 	} | 
| Jouni Malinen | 0e2dedf | 2009-03-03 19:23:32 +0200 | [diff] [blame] | 474 | 	if (__ath9k_wiphy_pausing(sc)) { | 
| Jouni Malinen | 7ec3e51 | 2009-03-03 19:23:37 +0200 | [diff] [blame] | 475 | 		if (sc->wiphy_select_failures == 0) | 
 | 476 | 			sc->wiphy_select_first_fail = jiffies; | 
 | 477 | 		sc->wiphy_select_failures++; | 
 | 478 | 		if (time_after(jiffies, sc->wiphy_select_first_fail + HZ / 2)) | 
 | 479 | 		{ | 
 | 480 | 			printk(KERN_DEBUG "ath9k: Previous wiphy select timed " | 
 | 481 | 			       "out; disable/enable hw to recover\n"); | 
 | 482 | 			__ath9k_wiphy_mark_all_paused(sc); | 
 | 483 | 			/* | 
 | 484 | 			 * TODO: this workaround to fix hardware is unlikely to | 
 | 485 | 			 * be specific to virtual wiphy changes. It can happen | 
 | 486 | 			 * on normal channel change, too, and as such, this | 
 | 487 | 			 * should really be made more generic. For example, | 
 | 488 | 			 * tricker radio disable/enable on GTT interrupt burst | 
 | 489 | 			 * (say, 10 GTT interrupts received without any TX | 
 | 490 | 			 * frame being completed) | 
 | 491 | 			 */ | 
 | 492 | 			spin_unlock_bh(&sc->wiphy_lock); | 
| Luis R. Rodriguez | 68a8911 | 2009-11-02 14:35:42 -0800 | [diff] [blame] | 493 | 			ath_radio_disable(sc, aphy->hw); | 
 | 494 | 			ath_radio_enable(sc, aphy->hw); | 
 | 495 | 			/* Only the primary wiphy hw is used for queuing work */ | 
| Luis R. Rodriguez | 42935ec | 2009-07-29 20:08:07 -0400 | [diff] [blame] | 496 | 			ieee80211_queue_work(aphy->sc->hw, | 
| Jouni Malinen | 7ec3e51 | 2009-03-03 19:23:37 +0200 | [diff] [blame] | 497 | 				   &aphy->sc->chan_work); | 
 | 498 | 			return -EBUSY; /* previous select still in progress */ | 
 | 499 | 		} | 
| Jouni Malinen | 0e2dedf | 2009-03-03 19:23:32 +0200 | [diff] [blame] | 500 | 		spin_unlock_bh(&sc->wiphy_lock); | 
 | 501 | 		return -EBUSY; /* previous select still in progress */ | 
 | 502 | 	} | 
| Jouni Malinen | 7ec3e51 | 2009-03-03 19:23:37 +0200 | [diff] [blame] | 503 | 	sc->wiphy_select_failures = 0; | 
| Jouni Malinen | 0e2dedf | 2009-03-03 19:23:32 +0200 | [diff] [blame] | 504 |  | 
 | 505 | 	/* Store the new channel */ | 
 | 506 | 	sc->chan_idx = aphy->chan_idx; | 
 | 507 | 	sc->chan_is_ht = aphy->chan_is_ht; | 
 | 508 | 	sc->next_wiphy = aphy; | 
 | 509 |  | 
 | 510 | 	__ath9k_wiphy_pause_all(sc); | 
 | 511 | 	now = !__ath9k_wiphy_pausing(aphy->sc); | 
 | 512 | 	spin_unlock_bh(&sc->wiphy_lock); | 
 | 513 |  | 
 | 514 | 	if (now) { | 
 | 515 | 		/* Ready to request channel change immediately */ | 
| Luis R. Rodriguez | 42935ec | 2009-07-29 20:08:07 -0400 | [diff] [blame] | 516 | 		ieee80211_queue_work(aphy->sc->hw, &aphy->sc->chan_work); | 
| Jouni Malinen | 0e2dedf | 2009-03-03 19:23:32 +0200 | [diff] [blame] | 517 | 	} | 
 | 518 |  | 
 | 519 | 	/* | 
 | 520 | 	 * wiphys will be unpaused in ath9k_tx_status() once channel has been | 
 | 521 | 	 * changed if any wiphy needs time to become paused. | 
 | 522 | 	 */ | 
 | 523 |  | 
 | 524 | 	return 0; | 
 | 525 | } | 
| Jouni Malinen | 9580a22 | 2009-03-03 19:23:33 +0200 | [diff] [blame] | 526 |  | 
 | 527 | bool ath9k_wiphy_started(struct ath_softc *sc) | 
 | 528 | { | 
 | 529 | 	int i; | 
 | 530 | 	spin_lock_bh(&sc->wiphy_lock); | 
 | 531 | 	if (sc->pri_wiphy->state != ATH_WIPHY_INACTIVE) { | 
 | 532 | 		spin_unlock_bh(&sc->wiphy_lock); | 
 | 533 | 		return true; | 
 | 534 | 	} | 
 | 535 | 	for (i = 0; i < sc->num_sec_wiphy; i++) { | 
 | 536 | 		if (sc->sec_wiphy[i] && | 
 | 537 | 		    sc->sec_wiphy[i]->state != ATH_WIPHY_INACTIVE) { | 
 | 538 | 			spin_unlock_bh(&sc->wiphy_lock); | 
 | 539 | 			return true; | 
 | 540 | 		} | 
 | 541 | 	} | 
 | 542 | 	spin_unlock_bh(&sc->wiphy_lock); | 
 | 543 | 	return false; | 
 | 544 | } | 
| Jouni Malinen | 18eb62f | 2009-03-03 19:23:35 +0200 | [diff] [blame] | 545 |  | 
 | 546 | static void ath9k_wiphy_pause_chan(struct ath_wiphy *aphy, | 
 | 547 | 				   struct ath_wiphy *selected) | 
 | 548 | { | 
| Jouni Malinen | 8089cc4 | 2009-03-03 19:23:38 +0200 | [diff] [blame] | 549 | 	if (selected->state == ATH_WIPHY_SCAN) { | 
 | 550 | 		if (aphy == selected) | 
 | 551 | 			return; | 
 | 552 | 		/* | 
 | 553 | 		 * Pause all other wiphys for the duration of the scan even if | 
 | 554 | 		 * they are on the current channel now. | 
 | 555 | 		 */ | 
 | 556 | 	} else if (aphy->chan_idx == selected->chan_idx) | 
| Jouni Malinen | 18eb62f | 2009-03-03 19:23:35 +0200 | [diff] [blame] | 557 | 		return; | 
 | 558 | 	aphy->state = ATH_WIPHY_PAUSED; | 
 | 559 | 	ieee80211_stop_queues(aphy->hw); | 
 | 560 | } | 
 | 561 |  | 
 | 562 | void ath9k_wiphy_pause_all_forced(struct ath_softc *sc, | 
 | 563 | 				  struct ath_wiphy *selected) | 
 | 564 | { | 
 | 565 | 	int i; | 
 | 566 | 	spin_lock_bh(&sc->wiphy_lock); | 
 | 567 | 	if (sc->pri_wiphy->state == ATH_WIPHY_ACTIVE) | 
 | 568 | 		ath9k_wiphy_pause_chan(sc->pri_wiphy, selected); | 
 | 569 | 	for (i = 0; i < sc->num_sec_wiphy; i++) { | 
 | 570 | 		if (sc->sec_wiphy[i] && | 
 | 571 | 		    sc->sec_wiphy[i]->state == ATH_WIPHY_ACTIVE) | 
 | 572 | 			ath9k_wiphy_pause_chan(sc->sec_wiphy[i], selected); | 
 | 573 | 	} | 
 | 574 | 	spin_unlock_bh(&sc->wiphy_lock); | 
 | 575 | } | 
| Jouni Malinen | f98c3bd | 2009-03-03 19:23:39 +0200 | [diff] [blame] | 576 |  | 
 | 577 | void ath9k_wiphy_work(struct work_struct *work) | 
 | 578 | { | 
 | 579 | 	struct ath_softc *sc = container_of(work, struct ath_softc, | 
 | 580 | 					    wiphy_work.work); | 
 | 581 | 	struct ath_wiphy *aphy = NULL; | 
 | 582 | 	bool first = true; | 
 | 583 |  | 
 | 584 | 	spin_lock_bh(&sc->wiphy_lock); | 
 | 585 |  | 
 | 586 | 	if (sc->wiphy_scheduler_int == 0) { | 
 | 587 | 		/* wiphy scheduler is disabled */ | 
 | 588 | 		spin_unlock_bh(&sc->wiphy_lock); | 
 | 589 | 		return; | 
 | 590 | 	} | 
 | 591 |  | 
 | 592 | try_again: | 
 | 593 | 	sc->wiphy_scheduler_index++; | 
 | 594 | 	while (sc->wiphy_scheduler_index <= sc->num_sec_wiphy) { | 
 | 595 | 		aphy = sc->sec_wiphy[sc->wiphy_scheduler_index - 1]; | 
 | 596 | 		if (aphy && aphy->state != ATH_WIPHY_INACTIVE) | 
 | 597 | 			break; | 
 | 598 |  | 
 | 599 | 		sc->wiphy_scheduler_index++; | 
 | 600 | 		aphy = NULL; | 
 | 601 | 	} | 
 | 602 | 	if (aphy == NULL) { | 
 | 603 | 		sc->wiphy_scheduler_index = 0; | 
 | 604 | 		if (sc->pri_wiphy->state == ATH_WIPHY_INACTIVE) { | 
 | 605 | 			if (first) { | 
 | 606 | 				first = false; | 
 | 607 | 				goto try_again; | 
 | 608 | 			} | 
 | 609 | 			/* No wiphy is ready to be scheduled */ | 
 | 610 | 		} else | 
 | 611 | 			aphy = sc->pri_wiphy; | 
 | 612 | 	} | 
 | 613 |  | 
 | 614 | 	spin_unlock_bh(&sc->wiphy_lock); | 
 | 615 |  | 
 | 616 | 	if (aphy && | 
 | 617 | 	    aphy->state != ATH_WIPHY_ACTIVE && aphy->state != ATH_WIPHY_SCAN && | 
 | 618 | 	    ath9k_wiphy_select(aphy)) { | 
 | 619 | 		printk(KERN_DEBUG "ath9k: Failed to schedule virtual wiphy " | 
 | 620 | 		       "change\n"); | 
 | 621 | 	} | 
 | 622 |  | 
| Luis R. Rodriguez | 42935ec | 2009-07-29 20:08:07 -0400 | [diff] [blame] | 623 | 	ieee80211_queue_delayed_work(sc->hw, | 
 | 624 | 				     &sc->wiphy_work, | 
 | 625 | 				     sc->wiphy_scheduler_int); | 
| Jouni Malinen | f98c3bd | 2009-03-03 19:23:39 +0200 | [diff] [blame] | 626 | } | 
 | 627 |  | 
 | 628 | void ath9k_wiphy_set_scheduler(struct ath_softc *sc, unsigned int msec_int) | 
 | 629 | { | 
 | 630 | 	cancel_delayed_work_sync(&sc->wiphy_work); | 
 | 631 | 	sc->wiphy_scheduler_int = msecs_to_jiffies(msec_int); | 
 | 632 | 	if (sc->wiphy_scheduler_int) | 
| Luis R. Rodriguez | 42935ec | 2009-07-29 20:08:07 -0400 | [diff] [blame] | 633 | 		ieee80211_queue_delayed_work(sc->hw, &sc->wiphy_work, | 
 | 634 | 					     sc->wiphy_scheduler_int); | 
| Jouni Malinen | f98c3bd | 2009-03-03 19:23:39 +0200 | [diff] [blame] | 635 | } | 
| Luis R. Rodriguez | 6483917 | 2009-07-14 20:22:53 -0400 | [diff] [blame] | 636 |  | 
 | 637 | /* caller must hold wiphy_lock */ | 
 | 638 | bool ath9k_all_wiphys_idle(struct ath_softc *sc) | 
 | 639 | { | 
 | 640 | 	unsigned int i; | 
| Luis R. Rodriguez | 194b7c1 | 2009-10-29 10:41:15 -0700 | [diff] [blame] | 641 | 	if (!sc->pri_wiphy->idle) | 
| Luis R. Rodriguez | 6483917 | 2009-07-14 20:22:53 -0400 | [diff] [blame] | 642 | 		return false; | 
| Luis R. Rodriguez | 6483917 | 2009-07-14 20:22:53 -0400 | [diff] [blame] | 643 | 	for (i = 0; i < sc->num_sec_wiphy; i++) { | 
 | 644 | 		struct ath_wiphy *aphy = sc->sec_wiphy[i]; | 
 | 645 | 		if (!aphy) | 
 | 646 | 			continue; | 
| Luis R. Rodriguez | 194b7c1 | 2009-10-29 10:41:15 -0700 | [diff] [blame] | 647 | 		if (!aphy->idle) | 
| Luis R. Rodriguez | 6483917 | 2009-07-14 20:22:53 -0400 | [diff] [blame] | 648 | 			return false; | 
 | 649 | 	} | 
 | 650 | 	return true; | 
 | 651 | } | 
| Luis R. Rodriguez | 194b7c1 | 2009-10-29 10:41:15 -0700 | [diff] [blame] | 652 |  | 
 | 653 | /* caller must hold wiphy_lock */ | 
 | 654 | void ath9k_set_wiphy_idle(struct ath_wiphy *aphy, bool idle) | 
 | 655 | { | 
 | 656 | 	struct ath_softc *sc = aphy->sc; | 
 | 657 |  | 
 | 658 | 	aphy->idle = idle; | 
 | 659 | 	ath_print(ath9k_hw_common(sc->sc_ah), ATH_DBG_CONFIG, | 
 | 660 | 		  "Marking %s as %s\n", | 
 | 661 | 		  wiphy_name(aphy->hw->wiphy), | 
 | 662 | 		  idle ? "idle" : "not-idle"); | 
 | 663 | } | 
| Luis R. Rodriguez | f52de03 | 2009-11-02 17:09:12 -0800 | [diff] [blame] | 664 | /* Only bother starting a queue on an active virtual wiphy */ | 
| Vasanthakumar Thiagarajan | 68e8f2f | 2010-07-22 02:24:11 -0700 | [diff] [blame] | 665 | bool ath_mac80211_start_queue(struct ath_softc *sc, u16 skb_queue) | 
| Luis R. Rodriguez | f52de03 | 2009-11-02 17:09:12 -0800 | [diff] [blame] | 666 | { | 
 | 667 | 	struct ieee80211_hw *hw = sc->pri_wiphy->hw; | 
 | 668 | 	unsigned int i; | 
| Vasanthakumar Thiagarajan | 68e8f2f | 2010-07-22 02:24:11 -0700 | [diff] [blame] | 669 | 	bool txq_started = false; | 
| Luis R. Rodriguez | f52de03 | 2009-11-02 17:09:12 -0800 | [diff] [blame] | 670 |  | 
 | 671 | 	spin_lock_bh(&sc->wiphy_lock); | 
 | 672 |  | 
 | 673 | 	/* Start the primary wiphy */ | 
 | 674 | 	if (sc->pri_wiphy->state == ATH_WIPHY_ACTIVE) { | 
 | 675 | 		ieee80211_wake_queue(hw, skb_queue); | 
| Vasanthakumar Thiagarajan | 68e8f2f | 2010-07-22 02:24:11 -0700 | [diff] [blame] | 676 | 		txq_started = true; | 
| Luis R. Rodriguez | f52de03 | 2009-11-02 17:09:12 -0800 | [diff] [blame] | 677 | 		goto unlock; | 
 | 678 | 	} | 
 | 679 |  | 
 | 680 | 	/* Now start the secondary wiphy queues */ | 
 | 681 | 	for (i = 0; i < sc->num_sec_wiphy; i++) { | 
 | 682 | 		struct ath_wiphy *aphy = sc->sec_wiphy[i]; | 
 | 683 | 		if (!aphy) | 
 | 684 | 			continue; | 
 | 685 | 		if (aphy->state != ATH_WIPHY_ACTIVE) | 
 | 686 | 			continue; | 
 | 687 |  | 
 | 688 | 		hw = aphy->hw; | 
 | 689 | 		ieee80211_wake_queue(hw, skb_queue); | 
| Vasanthakumar Thiagarajan | 68e8f2f | 2010-07-22 02:24:11 -0700 | [diff] [blame] | 690 | 		txq_started = true; | 
| Luis R. Rodriguez | f52de03 | 2009-11-02 17:09:12 -0800 | [diff] [blame] | 691 | 		break; | 
 | 692 | 	} | 
 | 693 |  | 
 | 694 | unlock: | 
 | 695 | 	spin_unlock_bh(&sc->wiphy_lock); | 
| Vasanthakumar Thiagarajan | 68e8f2f | 2010-07-22 02:24:11 -0700 | [diff] [blame] | 696 | 	return txq_started; | 
| Luis R. Rodriguez | f52de03 | 2009-11-02 17:09:12 -0800 | [diff] [blame] | 697 | } | 
 | 698 |  | 
 | 699 | /* Go ahead and propagate information to all virtual wiphys, it won't hurt */ | 
 | 700 | void ath_mac80211_stop_queue(struct ath_softc *sc, u16 skb_queue) | 
 | 701 | { | 
 | 702 | 	struct ieee80211_hw *hw = sc->pri_wiphy->hw; | 
 | 703 | 	unsigned int i; | 
 | 704 |  | 
 | 705 | 	spin_lock_bh(&sc->wiphy_lock); | 
 | 706 |  | 
 | 707 | 	/* Stop the primary wiphy */ | 
 | 708 | 	ieee80211_stop_queue(hw, skb_queue); | 
 | 709 |  | 
 | 710 | 	/* Now stop the secondary wiphy queues */ | 
 | 711 | 	for (i = 0; i < sc->num_sec_wiphy; i++) { | 
 | 712 | 		struct ath_wiphy *aphy = sc->sec_wiphy[i]; | 
 | 713 | 		if (!aphy) | 
 | 714 | 			continue; | 
 | 715 | 		hw = aphy->hw; | 
 | 716 | 		ieee80211_stop_queue(hw, skb_queue); | 
 | 717 | 	} | 
 | 718 | 	spin_unlock_bh(&sc->wiphy_lock); | 
 | 719 | } |