Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit b1ae1edf authored by Ben Greear's avatar Ben Greear Committed by John W. Linville
Browse files

ath5k: Allow ath5k to support virtual STA and AP interfaces.



Support up to 4 virtual APs and as many virtual STA interfaces
as desired.

This patch is ported forward from a patch that Patrick McHardy
did for me against 2.6.31.

Signed-off-by: default avatarBen Greear <greearb@candelatech.com>
Acked-by: default avatarBruno Randolf <br1@einfach.org>
Signed-off-by: default avatarJohn W. Linville <linville@tuxdriver.com>
parent ea229e68
Loading
Loading
Loading
Loading
+232 −43
Original line number Original line Diff line number Diff line
@@ -52,6 +52,7 @@
#include <linux/ethtool.h>
#include <linux/ethtool.h>
#include <linux/uaccess.h>
#include <linux/uaccess.h>
#include <linux/slab.h>
#include <linux/slab.h>
#include <linux/etherdevice.h>


#include <net/ieee80211_radiotap.h>
#include <net/ieee80211_radiotap.h>


@@ -509,8 +510,71 @@ ath5k_setcurmode(struct ath5k_softc *sc, unsigned int mode)
	}
	}
}
}


struct ath_vif_iter_data {
	const u8	*hw_macaddr;
	u8		mask[ETH_ALEN];
	u8		active_mac[ETH_ALEN]; /* first active MAC */
	bool		need_set_hw_addr;
	bool		found_active;
	bool		any_assoc;
};

static void ath_vif_iter(void *data, u8 *mac, struct ieee80211_vif *vif)
{
	struct ath_vif_iter_data *iter_data = data;
	int i;

	if (iter_data->hw_macaddr)
		for (i = 0; i < ETH_ALEN; i++)
			iter_data->mask[i] &=
				~(iter_data->hw_macaddr[i] ^ mac[i]);

	if (!iter_data->found_active) {
		iter_data->found_active = true;
		memcpy(iter_data->active_mac, mac, ETH_ALEN);
	}

	if (iter_data->need_set_hw_addr && iter_data->hw_macaddr)
		if (compare_ether_addr(iter_data->hw_macaddr, mac) == 0)
			iter_data->need_set_hw_addr = false;

	if (!iter_data->any_assoc) {
		struct ath5k_vif *avf = (void *)vif->drv_priv;
		if (avf->assoc)
			iter_data->any_assoc = true;
	}
}

void ath5k_update_bssid_mask(struct ath5k_softc *sc, struct ieee80211_vif *vif)
{
	struct ath_common *common = ath5k_hw_common(sc->ah);
	struct ath_vif_iter_data iter_data;

	/*
	 * Use the hardware MAC address as reference, the hardware uses it
	 * together with the BSSID mask when matching addresses.
	 */
	iter_data.hw_macaddr = common->macaddr;
	memset(&iter_data.mask, 0xff, ETH_ALEN);
	iter_data.found_active = false;
	iter_data.need_set_hw_addr = true;

	if (vif)
		ath_vif_iter(&iter_data, vif->addr, vif);

	/* Get list of all active MAC addresses */
	ieee80211_iterate_active_interfaces_atomic(sc->hw, ath_vif_iter,
						   &iter_data);
	memcpy(sc->bssidmask, iter_data.mask, ETH_ALEN);

	if (iter_data.need_set_hw_addr && iter_data.found_active)
		ath5k_hw_set_lladdr(sc->ah, iter_data.active_mac);

	ath5k_hw_set_bssid_mask(sc->ah, sc->bssidmask);
}

static void
static void
ath5k_mode_setup(struct ath5k_softc *sc)
ath5k_mode_setup(struct ath5k_softc *sc, struct ieee80211_vif *vif)
{
{
	struct ath5k_hw *ah = sc->ah;
	struct ath5k_hw *ah = sc->ah;
	u32 rfilt;
	u32 rfilt;
@@ -520,7 +584,7 @@ ath5k_mode_setup(struct ath5k_softc *sc)
	ath5k_hw_set_rx_filter(ah, rfilt);
	ath5k_hw_set_rx_filter(ah, rfilt);


	if (ath5k_hw_hasbssidmask(ah))
	if (ath5k_hw_hasbssidmask(ah))
		ath5k_hw_set_bssid_mask(ah, sc->bssidmask);
		ath5k_update_bssid_mask(sc, vif);


	/* configure operational mode */
	/* configure operational mode */
	ath5k_hw_set_opmode(ah, sc->opmode);
	ath5k_hw_set_opmode(ah, sc->opmode);
@@ -698,13 +762,13 @@ ath5k_txbuf_setup(struct ath5k_softc *sc, struct ath5k_buf *bf,
		flags |= AR5K_TXDESC_RTSENA;
		flags |= AR5K_TXDESC_RTSENA;
		cts_rate = ieee80211_get_rts_cts_rate(sc->hw, info)->hw_value;
		cts_rate = ieee80211_get_rts_cts_rate(sc->hw, info)->hw_value;
		duration = le16_to_cpu(ieee80211_rts_duration(sc->hw,
		duration = le16_to_cpu(ieee80211_rts_duration(sc->hw,
			sc->vif, pktlen, info));
			info->control.vif, pktlen, info));
	}
	}
	if (rc_flags & IEEE80211_TX_RC_USE_CTS_PROTECT) {
	if (rc_flags & IEEE80211_TX_RC_USE_CTS_PROTECT) {
		flags |= AR5K_TXDESC_CTSENA;
		flags |= AR5K_TXDESC_CTSENA;
		cts_rate = ieee80211_get_rts_cts_rate(sc->hw, info)->hw_value;
		cts_rate = ieee80211_get_rts_cts_rate(sc->hw, info)->hw_value;
		duration = le16_to_cpu(ieee80211_ctstoself_duration(sc->hw,
		duration = le16_to_cpu(ieee80211_ctstoself_duration(sc->hw,
			sc->vif, pktlen, info));
			info->control.vif, pktlen, info));
	}
	}
	ret = ah->ah_setup_tx_desc(ah, ds, pktlen,
	ret = ah->ah_setup_tx_desc(ah, ds, pktlen,
		ieee80211_get_hdrlen_from_skb(skb), padsize,
		ieee80211_get_hdrlen_from_skb(skb), padsize,
@@ -806,10 +870,13 @@ ath5k_desc_alloc(struct ath5k_softc *sc, struct pci_dev *pdev)
		list_add_tail(&bf->list, &sc->txbuf);
		list_add_tail(&bf->list, &sc->txbuf);
	}
	}


	/* beacon buffer */
	/* beacon buffers */
	INIT_LIST_HEAD(&sc->bcbuf);
	for (i = 0; i < ATH_BCBUF; i++, bf++, ds++, da += sizeof(*ds)) {
		bf->desc = ds;
		bf->desc = ds;
		bf->daddr = da;
		bf->daddr = da;
	sc->bbuf = bf;
		list_add_tail(&bf->list, &sc->bcbuf);
	}


	return 0;
	return 0;
err_free:
err_free:
@@ -824,11 +891,12 @@ ath5k_desc_free(struct ath5k_softc *sc, struct pci_dev *pdev)
{
{
	struct ath5k_buf *bf;
	struct ath5k_buf *bf;


	ath5k_txbuf_free_skb(sc, sc->bbuf);
	list_for_each_entry(bf, &sc->txbuf, list)
	list_for_each_entry(bf, &sc->txbuf, list)
		ath5k_txbuf_free_skb(sc, bf);
		ath5k_txbuf_free_skb(sc, bf);
	list_for_each_entry(bf, &sc->rxbuf, list)
	list_for_each_entry(bf, &sc->rxbuf, list)
		ath5k_rxbuf_free_skb(sc, bf);
		ath5k_rxbuf_free_skb(sc, bf);
	list_for_each_entry(bf, &sc->bcbuf, list)
		ath5k_txbuf_free_skb(sc, bf);


	/* Free memory associated with all descriptors */
	/* Free memory associated with all descriptors */
	pci_free_consistent(pdev, sc->desc_len, sc->desc, sc->desc_daddr);
	pci_free_consistent(pdev, sc->desc_len, sc->desc, sc->desc_daddr);
@@ -837,7 +905,6 @@ ath5k_desc_free(struct ath5k_softc *sc, struct pci_dev *pdev)


	kfree(sc->bufptr);
	kfree(sc->bufptr);
	sc->bufptr = NULL;
	sc->bufptr = NULL;
	sc->bbuf = NULL;
}
}




@@ -1083,7 +1150,7 @@ ath5k_rx_start(struct ath5k_softc *sc)
	spin_unlock_bh(&sc->rxbuflock);
	spin_unlock_bh(&sc->rxbuflock);


	ath5k_hw_start_rx_dma(ah);	/* enable recv descriptors */
	ath5k_hw_start_rx_dma(ah);	/* enable recv descriptors */
	ath5k_mode_setup(sc);		/* set filters, etc. */
	ath5k_mode_setup(sc, NULL);		/* set filters, etc. */
	ath5k_hw_start_rx_pcu(ah);	/* re-enable PCU/DMA engine */
	ath5k_hw_start_rx_pcu(ah);	/* re-enable PCU/DMA engine */


	return 0;
	return 0;
@@ -1750,6 +1817,7 @@ ath5k_beacon_update(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
{
{
	int ret;
	int ret;
	struct ath5k_softc *sc = hw->priv;
	struct ath5k_softc *sc = hw->priv;
	struct ath5k_vif *avf = (void *)vif->drv_priv;
	struct sk_buff *skb;
	struct sk_buff *skb;


	if (WARN_ON(!vif)) {
	if (WARN_ON(!vif)) {
@@ -1766,11 +1834,11 @@ ath5k_beacon_update(struct ieee80211_hw *hw, struct ieee80211_vif *vif)


	ath5k_debug_dump_skb(sc, skb, "BC  ", 1);
	ath5k_debug_dump_skb(sc, skb, "BC  ", 1);


	ath5k_txbuf_free_skb(sc, sc->bbuf);
	ath5k_txbuf_free_skb(sc, avf->bbuf);
	sc->bbuf->skb = skb;
	avf->bbuf->skb = skb;
	ret = ath5k_beacon_setup(sc, sc->bbuf);
	ret = ath5k_beacon_setup(sc, avf->bbuf);
	if (ret)
	if (ret)
		sc->bbuf->skb = NULL;
		avf->bbuf->skb = NULL;
out:
out:
	return ret;
	return ret;
}
}
@@ -1786,16 +1854,14 @@ ath5k_beacon_update(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
static void
static void
ath5k_beacon_send(struct ath5k_softc *sc)
ath5k_beacon_send(struct ath5k_softc *sc)
{
{
	struct ath5k_buf *bf = sc->bbuf;
	struct ath5k_hw *ah = sc->ah;
	struct ath5k_hw *ah = sc->ah;
	struct ieee80211_vif *vif;
	struct ath5k_vif *avf;
	struct ath5k_buf *bf;
	struct sk_buff *skb;
	struct sk_buff *skb;


	ATH5K_DBG_UNLIMIT(sc, ATH5K_DEBUG_BEACON, "in beacon_send\n");
	ATH5K_DBG_UNLIMIT(sc, ATH5K_DEBUG_BEACON, "in beacon_send\n");


	if (unlikely(bf->skb == NULL || sc->opmode == NL80211_IFTYPE_STATION)) {
		ATH5K_WARN(sc, "bf=%p bf_skb=%p\n", bf, bf ? bf->skb : NULL);
		return;
	}
	/*
	/*
	 * Check if the previous beacon has gone out.  If
	 * Check if the previous beacon has gone out.  If
	 * not, don't don't try to post another: skip this
	 * not, don't don't try to post another: skip this
@@ -1824,6 +1890,28 @@ ath5k_beacon_send(struct ath5k_softc *sc)
		sc->bmisscount = 0;
		sc->bmisscount = 0;
	}
	}


	if (sc->opmode == NL80211_IFTYPE_AP && sc->num_ap_vifs > 1) {
		u64 tsf = ath5k_hw_get_tsf64(ah);
		u32 tsftu = TSF_TO_TU(tsf);
		int slot = ((tsftu % sc->bintval) * ATH_BCBUF) / sc->bintval;
		vif = sc->bslot[(slot + 1) % ATH_BCBUF];
		ATH5K_DBG(sc, ATH5K_DEBUG_BEACON,
			"tsf %llx tsftu %x intval %u slot %u vif %p\n",
			(unsigned long long)tsf, tsftu, sc->bintval, slot, vif);
	} else /* only one interface */
		vif = sc->bslot[0];

	if (!vif)
		return;

	avf = (void *)vif->drv_priv;
	bf = avf->bbuf;
	if (unlikely(bf->skb == NULL || sc->opmode == NL80211_IFTYPE_STATION ||
			sc->opmode == NL80211_IFTYPE_MONITOR)) {
		ATH5K_WARN(sc, "bf=%p bf_skb=%p\n", bf, bf ? bf->skb : NULL);
		return;
	}

	/*
	/*
	 * Stop any current dma and put the new frame on the queue.
	 * Stop any current dma and put the new frame on the queue.
	 * This should never fail since we check above that no frames
	 * This should never fail since we check above that no frames
@@ -1836,17 +1924,17 @@ ath5k_beacon_send(struct ath5k_softc *sc)


	/* refresh the beacon for AP mode */
	/* refresh the beacon for AP mode */
	if (sc->opmode == NL80211_IFTYPE_AP)
	if (sc->opmode == NL80211_IFTYPE_AP)
		ath5k_beacon_update(sc->hw, sc->vif);
		ath5k_beacon_update(sc->hw, vif);


	ath5k_hw_set_txdp(ah, sc->bhalq, bf->daddr);
	ath5k_hw_set_txdp(ah, sc->bhalq, bf->daddr);
	ath5k_hw_start_tx_dma(ah, sc->bhalq);
	ath5k_hw_start_tx_dma(ah, sc->bhalq);
	ATH5K_DBG(sc, ATH5K_DEBUG_BEACON, "TXDP[%u] = %llx (%p)\n",
	ATH5K_DBG(sc, ATH5K_DEBUG_BEACON, "TXDP[%u] = %llx (%p)\n",
		sc->bhalq, (unsigned long long)bf->daddr, bf->desc);
		sc->bhalq, (unsigned long long)bf->daddr, bf->desc);


	skb = ieee80211_get_buffered_bc(sc->hw, sc->vif);
	skb = ieee80211_get_buffered_bc(sc->hw, vif);
	while (skb) {
	while (skb) {
		ath5k_tx_queue(sc->hw, skb, sc->cabq);
		ath5k_tx_queue(sc->hw, skb, sc->cabq);
		skb = ieee80211_get_buffered_bc(sc->hw, sc->vif);
		skb = ieee80211_get_buffered_bc(sc->hw, vif);
	}
	}


	sc->bsent++;
	sc->bsent++;
@@ -1876,6 +1964,12 @@ ath5k_beacon_update_timers(struct ath5k_softc *sc, u64 bc_tsf)
	u64 hw_tsf;
	u64 hw_tsf;


	intval = sc->bintval & AR5K_BEACON_PERIOD;
	intval = sc->bintval & AR5K_BEACON_PERIOD;
	if (sc->opmode == NL80211_IFTYPE_AP && sc->num_ap_vifs > 1) {
		intval /= ATH_BCBUF;	/* staggered multi-bss beacons */
		if (intval < 15)
			ATH5K_WARN(sc, "intval %u is too low, min 15\n",
				   intval);
	}
	if (WARN_ON(!intval))
	if (WARN_ON(!intval))
		return;
		return;


@@ -2323,6 +2417,10 @@ ath5k_init(struct ath5k_softc *sc)
		ath_hw_keyreset(common, (u16) i);
		ath_hw_keyreset(common, (u16) i);


	ath5k_hw_set_ack_bitrate_high(ah, true);
	ath5k_hw_set_ack_bitrate_high(ah, true);

	for (i = 0; i < ARRAY_SIZE(sc->bslot); i++)
		sc->bslot[i] = NULL;

	ret = 0;
	ret = 0;
done:
done:
	mmiowb();
	mmiowb();
@@ -2382,7 +2480,6 @@ ath5k_stop_hw(struct ath5k_softc *sc)
		ATH5K_DBG(sc, ATH5K_DEBUG_RESET,
		ATH5K_DBG(sc, ATH5K_DEBUG_RESET,
				"putting device to sleep\n");
				"putting device to sleep\n");
	}
	}
	ath5k_txbuf_free_skb(sc, sc->bbuf);


	mmiowb();
	mmiowb();
	mutex_unlock(&sc->lock);
	mutex_unlock(&sc->lock);
@@ -2587,9 +2684,9 @@ ath5k_attach(struct pci_dev *pdev, struct ieee80211_hw *hw)
	}
	}


	SET_IEEE80211_PERM_ADDR(hw, mac);
	SET_IEEE80211_PERM_ADDR(hw, mac);
	memcpy(&sc->lladdr, mac, ETH_ALEN);
	/* All MAC address bits matter for ACKs */
	/* All MAC address bits matter for ACKs */
	memcpy(sc->bssidmask, ath_bcast_mac, ETH_ALEN);
	ath5k_update_bssid_mask(sc, NULL);
	ath5k_hw_set_bssid_mask(sc->ah, sc->bssidmask);


	regulatory->current_rd = ah->ah_capabilities.cap_eeprom.ee_regdomain;
	regulatory->current_rd = ah->ah_capabilities.cap_eeprom.ee_regdomain;
	ret = ath_regd_init(regulatory, hw->wiphy, ath5k_reg_notifier);
	ret = ath_regd_init(regulatory, hw->wiphy, ath5k_reg_notifier);
@@ -2687,31 +2784,91 @@ static int ath5k_add_interface(struct ieee80211_hw *hw,
{
{
	struct ath5k_softc *sc = hw->priv;
	struct ath5k_softc *sc = hw->priv;
	int ret;
	int ret;
	struct ath5k_hw *ah = sc->ah;
	struct ath5k_vif *avf = (void *)vif->drv_priv;


	mutex_lock(&sc->lock);
	mutex_lock(&sc->lock);
	if (sc->vif) {

		ret = 0;
	if ((vif->type == NL80211_IFTYPE_AP ||
	     vif->type == NL80211_IFTYPE_ADHOC)
	    && (sc->num_ap_vifs + sc->num_adhoc_vifs) >= ATH_BCBUF) {
		ret = -ELNRNG;
		goto end;
		goto end;
	}
	}


	sc->vif = vif;
	/* Don't allow other interfaces if one ad-hoc is configured.
	 * TODO: Fix the problems with ad-hoc and multiple other interfaces.
	 * We would need to operate the HW in ad-hoc mode to allow TSF updates
	 * for the IBSS, but this breaks with additional AP or STA interfaces
	 * at the moment. */
	if (sc->num_adhoc_vifs ||
	    (sc->nvifs && vif->type == NL80211_IFTYPE_ADHOC)) {
		ATH5K_ERR(sc, "Only one single ad-hoc interface is allowed.\n");
		ret = -ELNRNG;
		goto end;
	}


	switch (vif->type) {
	switch (vif->type) {
	case NL80211_IFTYPE_AP:
	case NL80211_IFTYPE_AP:
	case NL80211_IFTYPE_STATION:
	case NL80211_IFTYPE_STATION:
	case NL80211_IFTYPE_ADHOC:
	case NL80211_IFTYPE_ADHOC:
	case NL80211_IFTYPE_MESH_POINT:
	case NL80211_IFTYPE_MESH_POINT:
		sc->opmode = vif->type;
		avf->opmode = vif->type;
		break;
		break;
	default:
	default:
		ret = -EOPNOTSUPP;
		ret = -EOPNOTSUPP;
		goto end;
		goto end;
	}
	}


	ATH5K_DBG(sc, ATH5K_DEBUG_MODE, "add interface mode %d\n", sc->opmode);
	sc->nvifs++;
	ATH5K_DBG(sc, ATH5K_DEBUG_MODE, "add interface mode %d\n", avf->opmode);

	/* Assign the vap/adhoc to a beacon xmit slot. */
	if ((avf->opmode == NL80211_IFTYPE_AP) ||
	    (avf->opmode == NL80211_IFTYPE_ADHOC)) {
		int slot;

		WARN_ON(list_empty(&sc->bcbuf));
		avf->bbuf = list_first_entry(&sc->bcbuf, struct ath5k_buf,
					     list);
		list_del(&avf->bbuf->list);

		avf->bslot = 0;
		for (slot = 0; slot < ATH_BCBUF; slot++) {
			if (!sc->bslot[slot]) {
				avf->bslot = slot;
				break;
			}
		}
		BUG_ON(sc->bslot[avf->bslot] != NULL);
		sc->bslot[avf->bslot] = vif;
		if (avf->opmode == NL80211_IFTYPE_AP)
			sc->num_ap_vifs++;
		else
			sc->num_adhoc_vifs++;
	}

	/* Set combined mode - when APs are configured, operate in AP mode.
	 * Otherwise use the mode of the new interface. This can currently
	 * only deal with combinations of APs and STAs. Only one ad-hoc
	 * interfaces is allowed above.
	 */
	if (sc->num_ap_vifs)
		sc->opmode = NL80211_IFTYPE_AP;
	else
		sc->opmode = vif->type;

	ath5k_hw_set_opmode(ah, sc->opmode);


	/* Any MAC address is fine, all others are included through the
	 * filter.
	 */
	memcpy(&sc->lladdr, vif->addr, ETH_ALEN);
	ath5k_hw_set_lladdr(sc->ah, vif->addr);
	ath5k_hw_set_lladdr(sc->ah, vif->addr);
	ath5k_mode_setup(sc);

	memcpy(&avf->lladdr, vif->addr, ETH_ALEN);

	ath5k_mode_setup(sc, vif);


	ret = 0;
	ret = 0;
end:
end:
@@ -2724,15 +2881,29 @@ ath5k_remove_interface(struct ieee80211_hw *hw,
			struct ieee80211_vif *vif)
			struct ieee80211_vif *vif)
{
{
	struct ath5k_softc *sc = hw->priv;
	struct ath5k_softc *sc = hw->priv;
	u8 mac[ETH_ALEN] = {};
	struct ath5k_vif *avf = (void *)vif->drv_priv;
	unsigned int i;


	mutex_lock(&sc->lock);
	mutex_lock(&sc->lock);
	if (sc->vif != vif)
	sc->nvifs--;
		goto end;

	if (avf->bbuf) {
		ath5k_txbuf_free_skb(sc, avf->bbuf);
		list_add_tail(&avf->bbuf->list, &sc->bcbuf);
		for (i = 0; i < ATH_BCBUF; i++) {
			if (sc->bslot[i] == vif) {
				sc->bslot[i] = NULL;
				break;
			}
		}
		avf->bbuf = NULL;
	}
	if (avf->opmode == NL80211_IFTYPE_AP)
		sc->num_ap_vifs--;
	else if (avf->opmode == NL80211_IFTYPE_ADHOC)
		sc->num_adhoc_vifs--;


	ath5k_hw_set_lladdr(sc->ah, mac);
	ath5k_update_bssid_mask(sc, NULL);
	sc->vif = NULL;
end:
	mutex_unlock(&sc->lock);
	mutex_unlock(&sc->lock);
}
}


@@ -2815,6 +2986,19 @@ static u64 ath5k_prepare_multicast(struct ieee80211_hw *hw,
	return ((u64)(mfilt[1]) << 32) | mfilt[0];
	return ((u64)(mfilt[1]) << 32) | mfilt[0];
}
}


static bool ath_any_vif_assoc(struct ath5k_softc *sc)
{
	struct ath_vif_iter_data iter_data;
	iter_data.hw_macaddr = NULL;
	iter_data.any_assoc = false;
	iter_data.need_set_hw_addr = false;
	iter_data.found_active = true;

	ieee80211_iterate_active_interfaces_atomic(sc->hw, ath_vif_iter,
						   &iter_data);
	return iter_data.any_assoc;
}

#define SUPPORTED_FIF_FLAGS \
#define SUPPORTED_FIF_FLAGS \
	FIF_PROMISC_IN_BSS |  FIF_ALLMULTI | FIF_FCSFAIL | \
	FIF_PROMISC_IN_BSS |  FIF_ALLMULTI | FIF_FCSFAIL | \
	FIF_PLCPFAIL | FIF_CONTROL | FIF_OTHER_BSS | \
	FIF_PLCPFAIL | FIF_CONTROL | FIF_OTHER_BSS | \
@@ -2885,7 +3069,7 @@ static void ath5k_configure_filter(struct ieee80211_hw *hw,


	/* FIF_BCN_PRBRESP_PROMISC really means to enable beacons
	/* FIF_BCN_PRBRESP_PROMISC really means to enable beacons
	* and probes for any BSSID */
	* and probes for any BSSID */
	if (*new_flags & FIF_BCN_PRBRESP_PROMISC)
	if ((*new_flags & FIF_BCN_PRBRESP_PROMISC) || (sc->nvifs > 1))
		rfilt |= AR5K_RX_FILTER_BEACON;
		rfilt |= AR5K_RX_FILTER_BEACON;


	/* FIF_CONTROL doc says that if FIF_PROMISC_IN_BSS is not
	/* FIF_CONTROL doc says that if FIF_PROMISC_IN_BSS is not
@@ -3070,14 +3254,13 @@ static void ath5k_bss_info_changed(struct ieee80211_hw *hw,
				    struct ieee80211_bss_conf *bss_conf,
				    struct ieee80211_bss_conf *bss_conf,
				    u32 changes)
				    u32 changes)
{
{
	struct ath5k_vif *avf = (void *)vif->drv_priv;
	struct ath5k_softc *sc = hw->priv;
	struct ath5k_softc *sc = hw->priv;
	struct ath5k_hw *ah = sc->ah;
	struct ath5k_hw *ah = sc->ah;
	struct ath_common *common = ath5k_hw_common(ah);
	struct ath_common *common = ath5k_hw_common(ah);
	unsigned long flags;
	unsigned long flags;


	mutex_lock(&sc->lock);
	mutex_lock(&sc->lock);
	if (WARN_ON(sc->vif != vif))
		goto unlock;


	if (changes & BSS_CHANGED_BSSID) {
	if (changes & BSS_CHANGED_BSSID) {
		/* Cache for later use during resets */
		/* Cache for later use during resets */
@@ -3091,7 +3274,12 @@ static void ath5k_bss_info_changed(struct ieee80211_hw *hw,
		sc->bintval = bss_conf->beacon_int;
		sc->bintval = bss_conf->beacon_int;


	if (changes & BSS_CHANGED_ASSOC) {
	if (changes & BSS_CHANGED_ASSOC) {
		avf->assoc = bss_conf->assoc;
		if (bss_conf->assoc)
			sc->assoc = bss_conf->assoc;
			sc->assoc = bss_conf->assoc;
		else
			sc->assoc = ath_any_vif_assoc(sc);

		if (sc->opmode == NL80211_IFTYPE_STATION)
		if (sc->opmode == NL80211_IFTYPE_STATION)
			set_beacon_filter(hw, sc->assoc);
			set_beacon_filter(hw, sc->assoc);
		ath5k_hw_set_ledstate(sc->ah, sc->assoc ?
		ath5k_hw_set_ledstate(sc->ah, sc->assoc ?
@@ -3119,7 +3307,6 @@ static void ath5k_bss_info_changed(struct ieee80211_hw *hw,
		       BSS_CHANGED_BEACON_INT))
		       BSS_CHANGED_BEACON_INT))
		ath5k_beacon_config(sc);
		ath5k_beacon_config(sc);


 unlock:
	mutex_unlock(&sc->lock);
	mutex_unlock(&sc->lock);
}
}


@@ -3394,6 +3581,8 @@ ath5k_pci_probe(struct pci_dev *pdev,
		hw->max_rate_tries = 11;
		hw->max_rate_tries = 11;
	}
	}


	hw->vif_data_size = sizeof(struct ath5k_vif);

	/* Finish private driver data initialization */
	/* Finish private driver data initialization */
	ret = ath5k_attach(pdev, hw);
	ret = ath5k_attach(pdev, hw);
	if (ret)
	if (ret)
+15 −4
Original line number Original line Diff line number Diff line
@@ -58,8 +58,7 @@


#define	ATH_RXBUF	40		/* number of RX buffers */
#define	ATH_RXBUF	40		/* number of RX buffers */
#define	ATH_TXBUF	200		/* number of TX buffers */
#define	ATH_TXBUF	200		/* number of TX buffers */
#define ATH_BCBUF	1		/* number of beacon buffers */
#define ATH_BCBUF	4		/* number of beacon buffers */

#define ATH5K_TXQ_LEN_MAX	(ATH_TXBUF / 4)		/* bufs per queue */
#define ATH5K_TXQ_LEN_MAX	(ATH_TXBUF / 4)		/* bufs per queue */
#define ATH5K_TXQ_LEN_LOW	(ATH5K_TXQ_LEN_MAX / 2)	/* low mark */
#define ATH5K_TXQ_LEN_LOW	(ATH5K_TXQ_LEN_MAX / 2)	/* low mark */


@@ -152,6 +151,14 @@ struct ath5k_statistics {
#define ATH_CHAN_MAX	(14+14+14+252+20)
#define ATH_CHAN_MAX	(14+14+14+252+20)
#endif
#endif


struct ath5k_vif {
	bool			assoc; /* are we associated or not */
	enum nl80211_iftype	opmode;
	int			bslot;
	struct ath5k_buf	*bbuf; /* beacon buffer */
	u8			lladdr[ETH_ALEN];
};

/* Software Carrier, keeps track of the driver state
/* Software Carrier, keeps track of the driver state
 * associated with an instance of a device */
 * associated with an instance of a device */
struct ath5k_softc {
struct ath5k_softc {
@@ -188,10 +195,11 @@ struct ath5k_softc {
	unsigned int		curmode;	/* current phy mode */
	unsigned int		curmode;	/* current phy mode */
	struct ieee80211_channel *curchan;	/* current h/w channel */
	struct ieee80211_channel *curchan;	/* current h/w channel */


	struct ieee80211_vif *vif;
	u16			nvifs;


	enum ath5k_int		imask;		/* interrupt mask copy */
	enum ath5k_int		imask;		/* interrupt mask copy */


	u8			lladdr[ETH_ALEN];
	u8			bssidmask[ETH_ALEN];
	u8			bssidmask[ETH_ALEN];


	unsigned int		led_pin,	/* GPIO pin for driving LED */
	unsigned int		led_pin,	/* GPIO pin for driving LED */
@@ -219,7 +227,10 @@ struct ath5k_softc {


	spinlock_t		block;		/* protects beacon */
	spinlock_t		block;		/* protects beacon */
	struct tasklet_struct	beacontq;	/* beacon intr tasklet */
	struct tasklet_struct	beacontq;	/* beacon intr tasklet */
	struct ath5k_buf	*bbuf;		/* beacon buffer */
	struct list_head	bcbuf;		/* beacon buffer */
	struct ieee80211_vif	*bslot[ATH_BCBUF];
	u16			num_ap_vifs;
	u16			num_adhoc_vifs;
	unsigned int		bhalq,		/* SW q for outgoing beacons */
	unsigned int		bhalq,		/* SW q for outgoing beacons */
				bmisscount,	/* missed beacon transmits */
				bmisscount,	/* missed beacon transmits */
				bintval,	/* beacon interval in TU */
				bintval,	/* beacon interval in TU */
+2 −2
Original line number Original line Diff line number Diff line
@@ -167,7 +167,7 @@ static inline void ath5k_hw_write_rate_duration(struct ath5k_hw *ah,
		 * ieee80211_duration() for a brief description of
		 * ieee80211_duration() for a brief description of
		 * what rate we should choose to TX ACKs. */
		 * what rate we should choose to TX ACKs. */
		tx_time = le16_to_cpu(ieee80211_generic_frame_duration(sc->hw,
		tx_time = le16_to_cpu(ieee80211_generic_frame_duration(sc->hw,
							sc->vif, 10, rate));
							NULL, 10, rate));


		ath5k_hw_reg_write(ah, tx_time, reg);
		ath5k_hw_reg_write(ah, tx_time, reg);


@@ -1060,7 +1060,7 @@ int ath5k_hw_reset(struct ath5k_hw *ah, enum nl80211_iftype op_mode,
		 * XXX: rethink this after new mode changes to
		 * XXX: rethink this after new mode changes to
		 * mac80211 are integrated */
		 * mac80211 are integrated */
		if (ah->ah_version == AR5K_AR5212 &&
		if (ah->ah_version == AR5K_AR5212 &&
			ah->ah_sc->vif != NULL)
			ah->ah_sc->nvifs)
			ath5k_hw_write_rate_duration(ah, mode);
			ath5k_hw_write_rate_duration(ah, mode);


		/*
		/*