All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 00/15] ath9k: Virtual interfaces and radios
@ 2009-03-03 17:23 Jouni Malinen
  2009-03-03 17:23 ` [PATCH 01/15] ath9k: Cleanup multiple VIF processing Jouni Malinen
                   ` (15 more replies)
  0 siblings, 16 replies; 21+ messages in thread
From: Jouni Malinen @ 2009-03-03 17:23 UTC (permalink / raw)
  To: John W. Linville; +Cc: linux-wireless

This set of patches brings in an initial step in adding support for
multiple virtual interfaces into ath9k both at the multi-vif (for
single channel only) and multi-wiphy (virtual radios that can be used
with multiple channels). The patches are on top of the current
wireless-testing tree, the beacon changes from Sujith and a setkey
patch from earlier today.

The first two patches clean up the multi-vif code and prepare the
driver for use of multiple MAC addresses. The third patch starts the
work on the concept of a virtual wiphy/radio (i.e., ath9k will be able
to register multiple "radios" with mac80211, but without mac80211
having to know about multi-channel operations). The fourth patch adds
support for multiple secondary wiphys. The patches 5-13 are completing
and improving the virtual interface operations.

The 14th patch file brings in a simple channel scheduler that is
enough for initial tests, but will be optimized in the future to take
into account various corner cases and different states of each virtual
radio to time the channel changes better.

The final patch in this series adds a debugfs interface that makes it
possible to control the virtual radios (e.g., add a new "radio"). For
now, this is enough to allow initial testing and experiments with the
new virtual wiphy concept. In the future, more official interface
(e.g., an nl80211 command passed through mac80211) is likely to remove
need for a debugfs interface.


Lot of the code here is still quite experimental, but taken into
account how many areas this touches, it would be helpful to get at
least parts of this merged in. I'm not aware of any regressions for
single-interface operations.

There are still some known limitations for the multi-interface
operations and one should be aware of these when running experiments
with the code. These will be worked on in the future to avoid the
extra complexity or compromises needed in the initial version.

Key cache configuration does not yet handle all combinations of
security policies in virtual interfaces. Consequently, the recommended
starting point is to use software crypto when using multiple
wiphys/vifs, i.e., use nohwcrypt=1 module parameter.

There is currently support for only a single beaconing interface,
i.e., not more than one AP wiphy can be added at a time. There is no
such limitation for station interfaces, i.e., arbitrary number of
virtual station wiphys can be added and used on arbitrary channels.
Though, the simple channel scheduler will start showing up its
simplicity with increased latency if there the wiphys end up on large
number of different channels.

Quick steps to start experiments:

modprobe ath9k nohwcrypt=1
# make sure debugfs is mounted somewhere (replace '/debug' in the
# command below with wherever the root directory is)

# wlan0 can now be used, e.g., with wpa_supplicant

echo add > /debug/ath9k/phy0/wiphy

# wlan1 can now be used, e.g., with wpa_supplicant

echo add > /debug/ath9k/phy0/wiphy

# wlan2 can now be used, e.g., with wpa_supplicant

and so on.. In theory, all the virtual wiphy interfaces could be used
as if they are real radios (run wpa_supplicant, hostapd, use iwconfig,
etc.).

When using virtual wiphys, the APs that the virtual interfaces are
associated with do not need to be on the same channel. One of the
wiphys could also be used with hostapd to bring up an AP. However, it
should be noted that most client implementation do not exactly like
the AP disappearing all the time, so using a virtual station interface
on another channel at the same time may not be that good of an idea
for most use cases now. This will hopefully improve somewhat with
future optimizations.


It would be useful to hear what others think about this sort of
functionality and how this should be integrated with Linux, including
the user space interface, applications managing the virtual interface
functionality, and user interface that the end users would see. This
is still quite an early step in the progress, but the goal here is to
get this functionality into mature state and provide something that
makes it easier to use wireless LAN in general for connecting devices
in various scenarios. All feedback would be welcome!

-- 
Jouni Malinen                                            PGP id EFC895FA

^ permalink raw reply	[flat|nested] 21+ messages in thread

* [PATCH 01/15] ath9k: Cleanup multiple VIF processing
  2009-03-03 17:23 [PATCH 00/15] ath9k: Virtual interfaces and radios Jouni Malinen
@ 2009-03-03 17:23 ` Jouni Malinen
  2009-03-03 17:23 ` [PATCH 02/15] ath9k: Set BSSID mask based on configured interfaces Jouni Malinen
                   ` (14 subsequent siblings)
  15 siblings, 0 replies; 21+ messages in thread
From: Jouni Malinen @ 2009-03-03 17:23 UTC (permalink / raw)
  To: John W. Linville; +Cc: linux-wireless, Jouni Malinen

Replace the internal sc_vaps array and index values by using vif
pointer from mac80211. Allow multiple VIPs to be registered. Though,
number of beaconing VIFs is still limited by ATH_BCBUF (currently
1). Multiple virtual STAs support is not yet complete, but at least
the data structures should now be able to handle this.

Signed-off-by: Jouni Malinen <jouni.malinen@atheros.com>

---
 drivers/net/wireless/ath9k/ath9k.h  |    8 ++---
 drivers/net/wireless/ath9k/beacon.c |   54 ++++++++++++++++--------------------
 drivers/net/wireless/ath9k/main.c   |   54 +++++++++++++++++++++---------------
 3 files changed, 59 insertions(+), 57 deletions(-)

--- wireless-testing.orig/drivers/net/wireless/ath9k/beacon.c	2009-03-03 18:28:59.000000000 +0200
+++ wireless-testing/drivers/net/wireless/ath9k/beacon.c	2009-03-03 18:29:02.000000000 +0200
@@ -113,17 +113,16 @@ static void ath_beacon_setup(struct ath_
 				     series, 4, 0);
 }
 
-static struct ath_buf *ath_beacon_generate(struct ath_softc *sc, int if_id)
+static struct ath_buf *ath_beacon_generate(struct ath_softc *sc,
+					   struct ieee80211_vif *vif)
 {
 	struct ath_buf *bf;
 	struct ath_vif *avp;
 	struct sk_buff *skb;
 	struct ath_txq *cabq;
-	struct ieee80211_vif *vif;
 	struct ieee80211_tx_info *info;
 	int cabq_depth;
 
-	vif = sc->vifs[if_id];
 	avp = (void *)vif->drv_priv;
 	cabq = sc->beacon.cabq;
 
@@ -208,15 +207,14 @@ static struct ath_buf *ath_beacon_genera
  * Startup beacon transmission for adhoc mode when they are sent entirely
  * by the hardware using the self-linked descriptor + veol trick.
 */
-static void ath_beacon_start_adhoc(struct ath_softc *sc, int if_id)
+static void ath_beacon_start_adhoc(struct ath_softc *sc,
+				   struct ieee80211_vif *vif)
 {
-	struct ieee80211_vif *vif;
 	struct ath_hw *ah = sc->sc_ah;
 	struct ath_buf *bf;
 	struct ath_vif *avp;
 	struct sk_buff *skb;
 
-	vif = sc->vifs[if_id];
 	avp = (void *)vif->drv_priv;
 
 	if (avp->av_bcbuf == NULL)
@@ -246,16 +244,14 @@ int ath_beaconq_setup(struct ath_hw *ah)
 	return ath9k_hw_setuptxqueue(ah, ATH9K_TX_QUEUE_BEACON, &qi);
 }
 
-int ath_beacon_alloc(struct ath_softc *sc, int if_id)
+int ath_beacon_alloc(struct ath_softc *sc, struct ieee80211_vif *vif)
 {
-	struct ieee80211_vif *vif;
 	struct ath_vif *avp;
 	struct ieee80211_hdr *hdr;
 	struct ath_buf *bf;
 	struct sk_buff *skb;
 	__le64 tstamp;
 
-	vif = sc->vifs[if_id];
 	avp = (void *)vif->drv_priv;
 
 	/* Allocate a beacon descriptor if we haven't done so. */
@@ -275,22 +271,21 @@ int ath_beacon_alloc(struct ath_softc *s
 			 */
 			avp->av_bslot = 0;
 			for (slot = 0; slot < ATH_BCBUF; slot++)
-				if (sc->beacon.bslot[slot] == ATH_IF_ID_ANY) {
+				if (sc->beacon.bslot[slot] == NULL) {
 					/*
 					 * XXX hack, space out slots to better
 					 * deal with misses
 					 */
 					if (slot+1 < ATH_BCBUF &&
-					    sc->beacon.bslot[slot+1] ==
-						ATH_IF_ID_ANY) {
+					    sc->beacon.bslot[slot+1] == NULL) {
 						avp->av_bslot = slot+1;
 						break;
 					}
 					avp->av_bslot = slot;
 					/* NB: keep looking for a double slot */
 				}
-			BUG_ON(sc->beacon.bslot[avp->av_bslot] != ATH_IF_ID_ANY);
-			sc->beacon.bslot[avp->av_bslot] = if_id;
+			BUG_ON(sc->beacon.bslot[avp->av_bslot] != NULL);
+			sc->beacon.bslot[avp->av_bslot] = vif;
 			sc->nbcnvifs++;
 		}
 	}
@@ -372,7 +367,7 @@ void ath_beacon_return(struct ath_softc 
 		struct ath_buf *bf;
 
 		if (avp->av_bslot != -1) {
-			sc->beacon.bslot[avp->av_bslot] = ATH_IF_ID_ANY;
+			sc->beacon.bslot[avp->av_bslot] = NULL;
 			sc->nbcnvifs--;
 		}
 
@@ -395,7 +390,8 @@ void ath_beacon_tasklet(unsigned long da
 	struct ath_softc *sc = (struct ath_softc *)data;
 	struct ath_hw *ah = sc->sc_ah;
 	struct ath_buf *bf = NULL;
-	int slot, if_id;
+	struct ieee80211_vif *vif;
+	int slot;
 	u32 bfaddr, bc = 0, tsftu;
 	u64 tsf;
 	u16 intval;
@@ -442,15 +438,15 @@ void ath_beacon_tasklet(unsigned long da
 	tsf = ath9k_hw_gettsf64(ah);
 	tsftu = TSF_TO_TU(tsf>>32, tsf);
 	slot = ((tsftu % intval) * ATH_BCBUF) / intval;
-	if_id = sc->beacon.bslot[(slot + 1) % ATH_BCBUF];
+	vif = sc->beacon.bslot[(slot + 1) % ATH_BCBUF];
 
 	DPRINTF(sc, ATH_DBG_BEACON,
-		"slot %d [tsf %llu tsftu %u intval %u] if_id %d\n",
-		slot, tsf, tsftu, intval, if_id);
+		"slot %d [tsf %llu tsftu %u intval %u] vif %p\n",
+		slot, tsf, tsftu, intval, vif);
 
 	bfaddr = 0;
-	if (if_id != ATH_IF_ID_ANY) {
-		bf = ath_beacon_generate(sc, if_id);
+	if (vif) {
+		bf = ath_beacon_generate(sc, vif);
 		if (bf != NULL) {
 			bfaddr = bf->bf_daddr;
 			bc = 1;
@@ -652,7 +648,8 @@ static void ath_beacon_config_sta(struct
 
 static void ath_beacon_config_adhoc(struct ath_softc *sc,
 				    struct ath_beacon_config *conf,
-				    struct ath_vif *avp)
+				    struct ath_vif *avp,
+				    struct ieee80211_vif *vif)
 {
 	u64 tsf;
 	u32 tsftu, intval, nexttbtt;
@@ -696,14 +693,12 @@ static void ath_beacon_config_adhoc(stru
 	ath9k_hw_set_interrupts(sc->sc_ah, sc->imask);
 
 	if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_VEOL)
-		ath_beacon_start_adhoc(sc, 0);
+		ath_beacon_start_adhoc(sc, vif);
 }
 
-void ath_beacon_config(struct ath_softc *sc, int if_id)
+void ath_beacon_config(struct ath_softc *sc, struct ieee80211_vif *vif)
 {
 	struct ath_beacon_config conf;
-	struct ath_vif *avp;
-	struct ieee80211_vif *vif;
 
 	/* Setup the beacon configuration parameters */
 
@@ -715,16 +710,15 @@ void ath_beacon_config(struct ath_softc 
 	conf.dtim_count = 1;
 	conf.bmiss_timeout = ATH_DEFAULT_BMISS_LIMIT * conf.beacon_interval;
 
-	if (if_id != ATH_IF_ID_ANY) {
-		vif = sc->vifs[if_id];
-		avp = (struct ath_vif *)vif->drv_priv;
+	if (vif) {
+		struct ath_vif *avp = (struct ath_vif *)vif->drv_priv;
 
 		switch(avp->av_opmode) {
 		case NL80211_IFTYPE_AP:
 			ath_beacon_config_ap(sc, &conf, avp);
 			break;
 		case NL80211_IFTYPE_ADHOC:
-			ath_beacon_config_adhoc(sc, &conf, avp);
+			ath_beacon_config_adhoc(sc, &conf, avp, vif);
 			break;
 		case NL80211_IFTYPE_STATION:
 			ath_beacon_config_sta(sc, &conf, avp);
--- wireless-testing.orig/drivers/net/wireless/ath9k/main.c	2009-03-03 18:29:02.000000000 +0200
+++ wireless-testing/drivers/net/wireless/ath9k/main.c	2009-03-03 18:29:02.000000000 +0200
@@ -798,13 +798,10 @@ static int ath_key_config(struct ath_sof
 		 * need to change with virtual interfaces. */
 		idx = key->keyidx;
 	} else if (key->keyidx) {
-		struct ieee80211_vif *vif;
-
 		if (WARN_ON(!sta))
 			return -EOPNOTSUPP;
 		mac = sta->addr;
 
-		vif = sc->vifs[0];
 		if (vif->type != NL80211_IFTYPE_AP) {
 			/* Only keyidx 0 should be used with unicast key, but
 			 * allow this for client mode for now. */
@@ -913,7 +910,7 @@ static void ath9k_bss_assoc_info(struct 
 		}
 
 		/* Configure the beacon */
-		ath_beacon_config(sc, 0);
+		ath_beacon_config(sc, vif);
 
 		/* Reset rssi stats */
 		sc->nodestats.ns_avgbrssi = ATH_RSSI_DUMMY_MARKER;
@@ -1118,7 +1115,7 @@ static void ath_radio_enable(struct ath_
 	}
 
 	if (sc->sc_flags & SC_OP_BEACONS)
-		ath_beacon_config(sc, ATH_IF_ID_ANY);	/* restart beacons */
+		ath_beacon_config(sc, NULL);	/* restart beacons */
 
 	/* Re-Enable  interrupts */
 	ath9k_hw_set_interrupts(ah, sc->imask);
@@ -1525,7 +1522,7 @@ static int ath_init(u16 devid, struct at
 
 	/* initialize beacon slots */
 	for (i = 0; i < ARRAY_SIZE(sc->beacon.bslot); i++)
-		sc->beacon.bslot[i] = ATH_IF_ID_ANY;
+		sc->beacon.bslot[i] = NULL;
 
 	/* save MISC configurations */
 	sc->config.swBeaconProcess = 1;
@@ -1713,7 +1710,7 @@ int ath_reset(struct ath_softc *sc, bool
 	ath_update_txpow(sc);
 
 	if (sc->sc_flags & SC_OP_BEACONS)
-		ath_beacon_config(sc, ATH_IF_ID_ANY);	/* restart beacons */
+		ath_beacon_config(sc, NULL);	/* restart beacons */
 
 	ath9k_hw_set_interrupts(ah, sc->imask);
 
@@ -2125,11 +2122,7 @@ static int ath9k_add_interface(struct ie
 	struct ath_softc *sc = hw->priv;
 	struct ath_vif *avp = (void *)conf->vif->drv_priv;
 	enum nl80211_iftype ic_opmode = NL80211_IFTYPE_UNSPECIFIED;
-
-	/* Support only vif for now */
-
-	if (sc->nvifs)
-		return -ENOBUFS;
+	int ret = 0;
 
 	mutex_lock(&sc->mutex);
 
@@ -2138,16 +2131,24 @@ static int ath9k_add_interface(struct ie
 		ic_opmode = NL80211_IFTYPE_STATION;
 		break;
 	case NL80211_IFTYPE_ADHOC:
+		if (sc->nbcnvifs >= ATH_BCBUF) {
+			ret = -ENOBUFS;
+			goto out;
+		}
 		ic_opmode = NL80211_IFTYPE_ADHOC;
 		break;
 	case NL80211_IFTYPE_AP:
+		if (sc->nbcnvifs >= ATH_BCBUF) {
+			ret = -ENOBUFS;
+			goto out;
+		}
 		ic_opmode = NL80211_IFTYPE_AP;
 		break;
 	default:
 		DPRINTF(sc, ATH_DBG_FATAL,
 			"Interface type %d not yet supported\n", conf->type);
-		mutex_unlock(&sc->mutex);
-		return -EOPNOTSUPP;
+		ret = -EOPNOTSUPP;
+		goto out;
 	}
 
 	DPRINTF(sc, ATH_DBG_CONFIG, "Attach a VIF of type: %d\n", ic_opmode);
@@ -2156,14 +2157,15 @@ static int ath9k_add_interface(struct ie
 	avp->av_opmode = ic_opmode;
 	avp->av_bslot = -1;
 
+	sc->nvifs++;
+	if (sc->nvifs > 1)
+		goto out; /* skip global settings for secondary vif */
+
 	if (ic_opmode == NL80211_IFTYPE_AP) {
 		ath9k_hw_set_tsfadjust(sc->sc_ah, 1);
 		sc->sc_flags |= SC_OP_TSF_RESET;
 	}
 
-	sc->vifs[0] = conf->vif;
-	sc->nvifs++;
-
 	/* Set the device opmode */
 	sc->sc_ah->opmode = ic_opmode;
 
@@ -2198,9 +2200,9 @@ static int ath9k_add_interface(struct ie
 			  jiffies + msecs_to_jiffies(ATH_ANI_POLLINTERVAL));
 	}
 
+out:
 	mutex_unlock(&sc->mutex);
-
-	return 0;
+	return ret;
 }
 
 static void ath9k_remove_interface(struct ieee80211_hw *hw,
@@ -2208,6 +2210,7 @@ static void ath9k_remove_interface(struc
 {
 	struct ath_softc *sc = hw->priv;
 	struct ath_vif *avp = (void *)conf->vif->drv_priv;
+	int i;
 
 	DPRINTF(sc, ATH_DBG_CONFIG, "Detach Interface\n");
 
@@ -2225,7 +2228,14 @@ static void ath9k_remove_interface(struc
 
 	sc->sc_flags &= ~SC_OP_BEACONS;
 
-	sc->vifs[0] = NULL;
+	for (i = 0; i < ARRAY_SIZE(sc->beacon.bslot); i++) {
+		if (sc->beacon.bslot[i] == conf->vif) {
+			printk(KERN_DEBUG "%s: vif had allocated beacon "
+			       "slot\n", __func__);
+			sc->beacon.bslot[i] = NULL;
+		}
+	}
+
 	sc->nvifs--;
 
 	mutex_unlock(&sc->mutex);
@@ -2362,13 +2372,13 @@ static int ath9k_config_interface(struct
 			 */
 			ath9k_hw_stoptxdma(sc->sc_ah, sc->beacon.beaconq);
 
-			error = ath_beacon_alloc(sc, 0);
+			error = ath_beacon_alloc(sc, vif);
 			if (error != 0) {
 				mutex_unlock(&sc->mutex);
 				return error;
 			}
 
-			ath_beacon_config(sc, 0);
+			ath_beacon_config(sc, vif);
 		}
 	}
 
--- wireless-testing.orig/drivers/net/wireless/ath9k/ath9k.h	2009-03-03 18:29:01.000000000 +0200
+++ wireless-testing/drivers/net/wireless/ath9k/ath9k.h	2009-03-03 18:29:02.000000000 +0200
@@ -439,7 +439,7 @@ struct ath_beacon {
 	u32 bmisscnt;
 	u32 ast_be_xmit;
 	u64 bc_tstamp;
-	int bslot[ATH_BCBUF];
+	struct ieee80211_vif *bslot[ATH_BCBUF];
 	int slottime;
 	int slotupdate;
 	struct ath9k_tx_queue_info beacon_qi;
@@ -449,9 +449,9 @@ struct ath_beacon {
 };
 
 void ath_beacon_tasklet(unsigned long data);
-void ath_beacon_config(struct ath_softc *sc, int if_id);
+void ath_beacon_config(struct ath_softc *sc, struct ieee80211_vif *vif);
 int ath_beaconq_setup(struct ath_hw *ah);
-int ath_beacon_alloc(struct ath_softc *sc, int if_id);
+int ath_beacon_alloc(struct ath_softc *sc, struct ieee80211_vif *vif);
 void ath_beacon_return(struct ath_softc *sc, struct ath_vif *avp);
 
 /*******/
@@ -532,7 +532,6 @@ struct ath_rfkill {
  */
 #define	ATH_KEYMAX	        128     /* max key cache size we handle */
 
-#define ATH_IF_ID_ANY   	0xff
 #define ATH_TXPOWER_MAX         100     /* .5 dBm units */
 #define ATH_RSSI_DUMMY_MARKER   0x127
 #define ATH_RATE_DUMMY_MARKER   0
@@ -595,7 +594,6 @@ struct ath_softc {
 	struct ath_rx rx;
 	struct ath_tx tx;
 	struct ath_beacon beacon;
-	struct ieee80211_vif *vifs[ATH_BCBUF];
 	struct ieee80211_rate rates[IEEE80211_NUM_BANDS][ATH_RATE_MAX];
 	struct ath_rate_table *hw_rate_table[ATH9K_MODE_MAX];
 	struct ath_rate_table *cur_rate_table;

-- 

-- 
Jouni Malinen                                            PGP id EFC895FA

^ permalink raw reply	[flat|nested] 21+ messages in thread

* [PATCH 02/15] ath9k: Set BSSID mask based on configured interfaces
  2009-03-03 17:23 [PATCH 00/15] ath9k: Virtual interfaces and radios Jouni Malinen
  2009-03-03 17:23 ` [PATCH 01/15] ath9k: Cleanup multiple VIF processing Jouni Malinen
@ 2009-03-03 17:23 ` Jouni Malinen
  2009-03-03 17:23 ` [PATCH 03/15] ath9k: Add data structure for supporting virtual radio/wiphy operation Jouni Malinen
                   ` (13 subsequent siblings)
  15 siblings, 0 replies; 21+ messages in thread
From: Jouni Malinen @ 2009-03-03 17:23 UTC (permalink / raw)
  To: John W. Linville; +Cc: linux-wireless, Jouni Malinen

Instead of using a hardcoded BSSID mask (mask for own addresses),
iterate through all active interfaces and determine the minimal mask
that covers all local addresses.

Signed-off-by: Jouni Malinen <jouni.malinen@atheros.com>

---
 drivers/net/wireless/ath9k/Makefile  |    1 
 drivers/net/wireless/ath9k/ath9k.h   |   14 +----
 drivers/net/wireless/ath9k/main.c    |   15 ++++-
 drivers/net/wireless/ath9k/virtual.c |   88 +++++++++++++++++++++++++++++++++++
 4 files changed, 103 insertions(+), 15 deletions(-)

--- wireless-testing.orig/drivers/net/wireless/ath9k/main.c	2009-03-03 18:29:02.000000000 +0200
+++ wireless-testing/drivers/net/wireless/ath9k/main.c	2009-03-03 18:30:22.000000000 +0200
@@ -1512,11 +1512,8 @@ static int ath_init(u16 devid, struct at
 	ath9k_hw_setcapability(ah, ATH9K_CAP_DIVERSITY, 1, true, NULL);
 	sc->rx.defant = ath9k_hw_getdefantenna(ah);
 
-	if (ah->caps.hw_caps & ATH9K_HW_CAP_BSSIDMASK) {
+	if (ah->caps.hw_caps & ATH9K_HW_CAP_BSSIDMASK)
 		memcpy(sc->bssidmask, ath_bcast_mac, ETH_ALEN);
-		ATH_SET_VIF_BSSID_MASK(sc->bssidmask);
-		ath9k_hw_setbssidmask(sc);
-	}
 
 	sc->beacon.slottime = ATH9K_SLOT_TIME_9;	/* default to short slot time */
 
@@ -2126,6 +2123,12 @@ static int ath9k_add_interface(struct ie
 
 	mutex_lock(&sc->mutex);
 
+	if (!(sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_BSSIDMASK) &&
+	    sc->nvifs > 0) {
+		ret = -ENOBUFS;
+		goto out;
+	}
+
 	switch (conf->type) {
 	case NL80211_IFTYPE_STATION:
 		ic_opmode = NL80211_IFTYPE_STATION;
@@ -2158,6 +2161,10 @@ static int ath9k_add_interface(struct ie
 	avp->av_bslot = -1;
 
 	sc->nvifs++;
+
+	if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_BSSIDMASK)
+		ath9k_set_bssid_mask(hw);
+
 	if (sc->nvifs > 1)
 		goto out; /* skip global settings for secondary vif */
 
--- wireless-testing.orig/drivers/net/wireless/ath9k/ath9k.h	2009-03-03 18:29:02.000000000 +0200
+++ wireless-testing/drivers/net/wireless/ath9k/ath9k.h	2009-03-03 18:30:22.000000000 +0200
@@ -387,17 +387,6 @@ void ath_tx_aggr_resume(struct ath_softc
 /* VIFs */
 /********/
 
-/*
- * Define the scheme that we select MAC address for multiple
- * BSS on the same radio. The very first VIF will just use the MAC
- * address from the EEPROM. For the next 3 VIFs, we set the
- * U/L bit (bit 1) in MAC address, and use the next two bits as the
- * index of the VIF.
- */
-
-#define ATH_SET_VIF_BSSID_MASK(bssid_mask) \
-	((bssid_mask)[0] &= ~(((ATH_BCBUF-1)<<2)|0x02))
-
 struct ath_vif {
 	int av_bslot;
 	enum nl80211_iftype av_opmode;
@@ -675,4 +664,7 @@ static inline void ath9k_ps_restore(stru
 					  sc->sc_ah->restore_mode);
 }
 
+
+void ath9k_set_bssid_mask(struct ieee80211_hw *hw);
+
 #endif /* ATH9K_H */
--- wireless-testing.orig/drivers/net/wireless/ath9k/Makefile	2009-02-27 10:53:01.000000000 +0200
+++ wireless-testing/drivers/net/wireless/ath9k/Makefile	2009-03-03 18:30:22.000000000 +0200
@@ -9,6 +9,7 @@ ath9k-y +=	hw.o \
 		main.o \
 		recv.o \
 		xmit.o \
+		virtual.o \
 		rc.o
 
 ath9k-$(CONFIG_PCI) += pci.o
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ wireless-testing/drivers/net/wireless/ath9k/virtual.c	2009-03-03 18:30:22.000000000 +0200
@@ -0,0 +1,88 @@
+/*
+ * Copyright (c) 2008-2009 Atheros Communications Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "ath9k.h"
+
+struct ath9k_vif_iter_data {
+	int count;
+	u8 *addr;
+};
+
+static void ath9k_vif_iter(void *data, u8 *mac, struct ieee80211_vif *vif)
+{
+	struct ath9k_vif_iter_data *iter_data = data;
+	u8 *nbuf;
+
+	nbuf = krealloc(iter_data->addr, (iter_data->count + 1) * ETH_ALEN,
+			GFP_ATOMIC);
+	if (nbuf == NULL)
+		return;
+
+	memcpy(nbuf + iter_data->count * ETH_ALEN, mac, ETH_ALEN);
+	iter_data->addr = nbuf;
+	iter_data->count++;
+}
+
+void ath9k_set_bssid_mask(struct ieee80211_hw *hw)
+{
+	struct ath_softc *sc = hw->priv;
+	struct ath9k_vif_iter_data iter_data;
+	int i, j;
+	u8 mask[ETH_ALEN];
+
+	/*
+	 * Add primary MAC address even if it is not in active use since it
+	 * will be configured to the hardware as the starting point and the
+	 * BSSID mask will need to be changed if another address is active.
+	 */
+	iter_data.addr = kmalloc(ETH_ALEN, GFP_ATOMIC);
+	if (iter_data.addr) {
+		memcpy(iter_data.addr, sc->sc_ah->macaddr, ETH_ALEN);
+		iter_data.count = 1;
+	} else
+		iter_data.count = 0;
+
+	/* Get list of all active MAC addresses */
+	ieee80211_iterate_active_interfaces_atomic(hw, ath9k_vif_iter,
+						   &iter_data);
+
+	/* Generate an address mask to cover all active addresses */
+	memset(mask, 0, ETH_ALEN);
+	for (i = 0; i < iter_data.count; i++) {
+		u8 *a1 = iter_data.addr + i * ETH_ALEN;
+		for (j = i + 1; j < iter_data.count; j++) {
+			u8 *a2 = iter_data.addr + j * ETH_ALEN;
+			mask[0] |= a1[0] ^ a2[0];
+			mask[1] |= a1[1] ^ a2[1];
+			mask[2] |= a1[2] ^ a2[2];
+			mask[3] |= a1[3] ^ a2[3];
+			mask[4] |= a1[4] ^ a2[4];
+			mask[5] |= a1[5] ^ a2[5];
+		}
+	}
+
+	kfree(iter_data.addr);
+
+	/* Invert the mask and configure hardware */
+	sc->bssidmask[0] = ~mask[0];
+	sc->bssidmask[1] = ~mask[1];
+	sc->bssidmask[2] = ~mask[2];
+	sc->bssidmask[3] = ~mask[3];
+	sc->bssidmask[4] = ~mask[4];
+	sc->bssidmask[5] = ~mask[5];
+
+	ath9k_hw_setbssidmask(sc);
+}

-- 

-- 
Jouni Malinen                                            PGP id EFC895FA

^ permalink raw reply	[flat|nested] 21+ messages in thread

* [PATCH 03/15] ath9k: Add data structure for supporting virtual radio/wiphy operation
  2009-03-03 17:23 [PATCH 00/15] ath9k: Virtual interfaces and radios Jouni Malinen
  2009-03-03 17:23 ` [PATCH 01/15] ath9k: Cleanup multiple VIF processing Jouni Malinen
  2009-03-03 17:23 ` [PATCH 02/15] ath9k: Set BSSID mask based on configured interfaces Jouni Malinen
@ 2009-03-03 17:23 ` Jouni Malinen
  2009-03-03 17:23 ` [PATCH 04/15] ath9k: Add support for multiple secondary virtual wiphys Jouni Malinen
                   ` (12 subsequent siblings)
  15 siblings, 0 replies; 21+ messages in thread
From: Jouni Malinen @ 2009-03-03 17:23 UTC (permalink / raw)
  To: John W. Linville; +Cc: linux-wireless, Jouni Malinen

This is the initial step in allowing ath9k to register multiple
virtual radios (wiphys). The goal of virtual radios is to allow the
same radio to be shared for multiple virtual interfaces that may
operate on different channels. The mac80211 virtual interface support
is designed only for single channel operation and as such, it is not
suitable for this type of use. Anyway, it can be used on top of the
virtual radio concept, if desired (e.g., use two virtual radios to
handle two channels and then add multiple mac80211 virtual interfaces
on top of each virtual radio).

The new struct ath_wiphy is now registered as the driver data
structure for wiphy. This structure has a pointer to the shared (among
virtual wiphys of the same physical radio) struct ath_softc data. The
primary wiphy maintains the allocated memory for ath_softc. Secondary
(virtual) wiphys will only allocate the new ath_wiphy structure.

Registration of secondary wiphys is added in a separate patch.

Signed-off-by: Jouni Malinen <jouni.malinen@atheros.com>

---
 drivers/net/wireless/ath9k/ahb.c     |   12 +++++--
 drivers/net/wireless/ath9k/ath9k.h   |    8 ++++
 drivers/net/wireless/ath9k/main.c    |   58 ++++++++++++++++++++++-------------
 drivers/net/wireless/ath9k/pci.c     |   19 ++++++++---
 drivers/net/wireless/ath9k/rc.c      |    3 +
 drivers/net/wireless/ath9k/recv.c    |   15 ++++++---
 drivers/net/wireless/ath9k/regd.c    |    6 ++-
 drivers/net/wireless/ath9k/virtual.c |    3 +
 8 files changed, 88 insertions(+), 36 deletions(-)

--- wireless-testing.orig/drivers/net/wireless/ath9k/ath9k.h	2009-03-03 18:30:22.000000000 +0200
+++ wireless-testing/drivers/net/wireless/ath9k/ath9k.h	2009-03-03 18:30:34.000000000 +0200
@@ -549,9 +549,12 @@ struct ath_bus_ops {
 	bool		(*eeprom_read)(struct ath_hw *ah, u32 off, u16 *data);
 };
 
+struct ath_wiphy;
+
 struct ath_softc {
 	struct ieee80211_hw *hw;
 	struct device *dev;
+	struct ath_wiphy *pri_wiphy;
 	struct tasklet_struct intr_tq;
 	struct tasklet_struct bcon_tasklet;
 	struct ath_hw *sc_ah;
@@ -607,6 +610,11 @@ struct ath_softc {
 	struct ath_bus_ops *bus_ops;
 };
 
+struct ath_wiphy {
+	struct ath_softc *sc; /* shared for all virtual wiphys */
+	struct ieee80211_hw *hw;
+};
+
 int ath_reset(struct ath_softc *sc, bool retry_tx);
 int ath_get_hal_qnum(u16 queue, struct ath_softc *sc);
 int ath_get_mac80211_qnum(u32 queue, struct ath_softc *sc);
--- wireless-testing.orig/drivers/net/wireless/ath9k/pci.c	2009-02-27 10:53:01.000000000 +0200
+++ wireless-testing/drivers/net/wireless/ath9k/pci.c	2009-03-03 18:30:34.000000000 +0200
@@ -83,6 +83,7 @@ static struct ath_bus_ops ath_pci_bus_op
 static int ath_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
 {
 	void __iomem *mem;
+	struct ath_wiphy *aphy;
 	struct ath_softc *sc;
 	struct ieee80211_hw *hw;
 	u8 csz;
@@ -155,7 +156,8 @@ static int ath_pci_probe(struct pci_dev 
 		goto bad1;
 	}
 
-	hw = ieee80211_alloc_hw(sizeof(struct ath_softc), &ath9k_ops);
+	hw = ieee80211_alloc_hw(sizeof(struct ath_wiphy) +
+				sizeof(struct ath_softc), &ath9k_ops);
 	if (hw == NULL) {
 		printk(KERN_ERR "ath_pci: no memory for ieee80211_hw\n");
 		goto bad2;
@@ -164,7 +166,11 @@ static int ath_pci_probe(struct pci_dev 
 	SET_IEEE80211_DEV(hw, &pdev->dev);
 	pci_set_drvdata(pdev, hw);
 
-	sc = hw->priv;
+	aphy = hw->priv;
+	sc = (struct ath_softc *) (aphy + 1);
+	aphy->sc = sc;
+	aphy->hw = hw;
+	sc->pri_wiphy = aphy;
 	sc->hw = hw;
 	sc->dev = &pdev->dev;
 	sc->mem = mem;
@@ -214,7 +220,8 @@ bad:
 static void ath_pci_remove(struct pci_dev *pdev)
 {
 	struct ieee80211_hw *hw = pci_get_drvdata(pdev);
-	struct ath_softc *sc = hw->priv;
+	struct ath_wiphy *aphy = hw->priv;
+	struct ath_softc *sc = aphy->sc;
 
 	ath_cleanup(sc);
 }
@@ -224,7 +231,8 @@ static void ath_pci_remove(struct pci_de
 static int ath_pci_suspend(struct pci_dev *pdev, pm_message_t state)
 {
 	struct ieee80211_hw *hw = pci_get_drvdata(pdev);
-	struct ath_softc *sc = hw->priv;
+	struct ath_wiphy *aphy = hw->priv;
+	struct ath_softc *sc = aphy->sc;
 
 	ath9k_hw_set_gpio(sc->sc_ah, ATH_LED_PIN, 1);
 
@@ -243,7 +251,8 @@ static int ath_pci_suspend(struct pci_de
 static int ath_pci_resume(struct pci_dev *pdev)
 {
 	struct ieee80211_hw *hw = pci_get_drvdata(pdev);
-	struct ath_softc *sc = hw->priv;
+	struct ath_wiphy *aphy = hw->priv;
+	struct ath_softc *sc = aphy->sc;
 	u32 val;
 	int err;
 
--- wireless-testing.orig/drivers/net/wireless/ath9k/main.c	2009-03-03 18:30:22.000000000 +0200
+++ wireless-testing/drivers/net/wireless/ath9k/main.c	2009-03-03 18:30:34.000000000 +0200
@@ -1932,7 +1932,8 @@ static void ath9k_update_ichannel(struct
 
 static int ath9k_start(struct ieee80211_hw *hw)
 {
-	struct ath_softc *sc = hw->priv;
+	struct ath_wiphy *aphy = hw->priv;
+	struct ath_softc *sc = aphy->sc;
 	struct ieee80211_channel *curchan = hw->conf.channel;
 	struct ath9k_channel *init_channel;
 	int r, pos;
@@ -2010,7 +2011,7 @@ static int ath9k_start(struct ieee80211_
 	sc->imask &= ~(ATH9K_INT_SWBA | ATH9K_INT_BMISS);
 	ath9k_hw_set_interrupts(sc->sc_ah, sc->imask);
 
-	ieee80211_wake_queues(sc->hw);
+	ieee80211_wake_queues(hw);
 
 #if defined(CONFIG_RFKILL) || defined(CONFIG_RFKILL_MODULE)
 	r = ath_start_rfkill_poll(sc);
@@ -2026,7 +2027,8 @@ static int ath9k_tx(struct ieee80211_hw 
 		    struct sk_buff *skb)
 {
 	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
-	struct ath_softc *sc = hw->priv;
+	struct ath_wiphy *aphy = hw->priv;
+	struct ath_softc *sc = aphy->sc;
 	struct ath_tx_control txctl;
 	int hdrlen, padsize;
 
@@ -2076,7 +2078,8 @@ exit:
 
 static void ath9k_stop(struct ieee80211_hw *hw)
 {
-	struct ath_softc *sc = hw->priv;
+	struct ath_wiphy *aphy = hw->priv;
+	struct ath_softc *sc = aphy->sc;
 
 	if (sc->sc_flags & SC_OP_INVALID) {
 		DPRINTF(sc, ATH_DBG_ANY, "Device not present\n");
@@ -2085,7 +2088,7 @@ static void ath9k_stop(struct ieee80211_
 
 	mutex_lock(&sc->mutex);
 
-	ieee80211_stop_queues(sc->hw);
+	ieee80211_stop_queues(hw);
 
 	/* make sure h/w will not generate any interrupt
 	 * before setting the invalid flag. */
@@ -2116,7 +2119,8 @@ static void ath9k_stop(struct ieee80211_
 static int ath9k_add_interface(struct ieee80211_hw *hw,
 			       struct ieee80211_if_init_conf *conf)
 {
-	struct ath_softc *sc = hw->priv;
+	struct ath_wiphy *aphy = hw->priv;
+	struct ath_softc *sc = aphy->sc;
 	struct ath_vif *avp = (void *)conf->vif->drv_priv;
 	enum nl80211_iftype ic_opmode = NL80211_IFTYPE_UNSPECIFIED;
 	int ret = 0;
@@ -2215,7 +2219,8 @@ out:
 static void ath9k_remove_interface(struct ieee80211_hw *hw,
 				   struct ieee80211_if_init_conf *conf)
 {
-	struct ath_softc *sc = hw->priv;
+	struct ath_wiphy *aphy = hw->priv;
+	struct ath_softc *sc = aphy->sc;
 	struct ath_vif *avp = (void *)conf->vif->drv_priv;
 	int i;
 
@@ -2250,7 +2255,8 @@ static void ath9k_remove_interface(struc
 
 static int ath9k_config(struct ieee80211_hw *hw, u32 changed)
 {
-	struct ath_softc *sc = hw->priv;
+	struct ath_wiphy *aphy = hw->priv;
+	struct ath_softc *sc = aphy->sc;
 	struct ieee80211_conf *conf = &hw->conf;
 
 	mutex_lock(&sc->mutex);
@@ -2317,7 +2323,8 @@ static int ath9k_config_interface(struct
 				  struct ieee80211_vif *vif,
 				  struct ieee80211_if_conf *conf)
 {
-	struct ath_softc *sc = hw->priv;
+	struct ath_wiphy *aphy = hw->priv;
+	struct ath_softc *sc = aphy->sc;
 	struct ath_hw *ah = sc->sc_ah;
 	struct ath_vif *avp = (void *)vif->drv_priv;
 	u32 rfilt = 0;
@@ -2422,7 +2429,8 @@ static void ath9k_configure_filter(struc
 				   int mc_count,
 				   struct dev_mc_list *mclist)
 {
-	struct ath_softc *sc = hw->priv;
+	struct ath_wiphy *aphy = hw->priv;
+	struct ath_softc *sc = aphy->sc;
 	u32 rfilt;
 
 	changed_flags &= SUPPORTED_FILTERS;
@@ -2448,7 +2456,8 @@ static void ath9k_sta_notify(struct ieee
 			     enum sta_notify_cmd cmd,
 			     struct ieee80211_sta *sta)
 {
-	struct ath_softc *sc = hw->priv;
+	struct ath_wiphy *aphy = hw->priv;
+	struct ath_softc *sc = aphy->sc;
 
 	switch (cmd) {
 	case STA_NOTIFY_ADD:
@@ -2465,7 +2474,8 @@ static void ath9k_sta_notify(struct ieee
 static int ath9k_conf_tx(struct ieee80211_hw *hw, u16 queue,
 			 const struct ieee80211_tx_queue_params *params)
 {
-	struct ath_softc *sc = hw->priv;
+	struct ath_wiphy *aphy = hw->priv;
+	struct ath_softc *sc = aphy->sc;
 	struct ath9k_tx_queue_info qi;
 	int ret = 0, qnum;
 
@@ -2501,7 +2511,8 @@ static int ath9k_set_key(struct ieee8021
 			 struct ieee80211_sta *sta,
 			 struct ieee80211_key_conf *key)
 {
-	struct ath_softc *sc = hw->priv;
+	struct ath_wiphy *aphy = hw->priv;
+	struct ath_softc *sc = aphy->sc;
 	int ret = 0;
 
 	if (modparam_nohwcrypt)
@@ -2543,7 +2554,8 @@ static void ath9k_bss_info_changed(struc
 				   struct ieee80211_bss_conf *bss_conf,
 				   u32 changed)
 {
-	struct ath_softc *sc = hw->priv;
+	struct ath_wiphy *aphy = hw->priv;
+	struct ath_softc *sc = aphy->sc;
 
 	mutex_lock(&sc->mutex);
 
@@ -2578,7 +2590,8 @@ static void ath9k_bss_info_changed(struc
 static u64 ath9k_get_tsf(struct ieee80211_hw *hw)
 {
 	u64 tsf;
-	struct ath_softc *sc = hw->priv;
+	struct ath_wiphy *aphy = hw->priv;
+	struct ath_softc *sc = aphy->sc;
 
 	mutex_lock(&sc->mutex);
 	tsf = ath9k_hw_gettsf64(sc->sc_ah);
@@ -2589,7 +2602,8 @@ static u64 ath9k_get_tsf(struct ieee8021
 
 static void ath9k_set_tsf(struct ieee80211_hw *hw, u64 tsf)
 {
-	struct ath_softc *sc = hw->priv;
+	struct ath_wiphy *aphy = hw->priv;
+	struct ath_softc *sc = aphy->sc;
 
 	mutex_lock(&sc->mutex);
 	ath9k_hw_settsf64(sc->sc_ah, tsf);
@@ -2598,7 +2612,8 @@ static void ath9k_set_tsf(struct ieee802
 
 static void ath9k_reset_tsf(struct ieee80211_hw *hw)
 {
-	struct ath_softc *sc = hw->priv;
+	struct ath_wiphy *aphy = hw->priv;
+	struct ath_softc *sc = aphy->sc;
 
 	mutex_lock(&sc->mutex);
 	ath9k_hw_reset_tsf(sc->sc_ah);
@@ -2610,7 +2625,8 @@ static int ath9k_ampdu_action(struct iee
 			      struct ieee80211_sta *sta,
 			      u16 tid, u16 *ssn)
 {
-	struct ath_softc *sc = hw->priv;
+	struct ath_wiphy *aphy = hw->priv;
+	struct ath_softc *sc = aphy->sc;
 	int ret = 0;
 
 	switch (action) {
@@ -2648,7 +2664,8 @@ static int ath9k_ampdu_action(struct iee
 
 static void ath9k_sw_scan_start(struct ieee80211_hw *hw)
 {
-	struct ath_softc *sc = hw->priv;
+	struct ath_wiphy *aphy = hw->priv;
+	struct ath_softc *sc = aphy->sc;
 
 	mutex_lock(&sc->mutex);
 	sc->sc_flags |= SC_OP_SCANNING;
@@ -2657,7 +2674,8 @@ static void ath9k_sw_scan_start(struct i
 
 static void ath9k_sw_scan_complete(struct ieee80211_hw *hw)
 {
-	struct ath_softc *sc = hw->priv;
+	struct ath_wiphy *aphy = hw->priv;
+	struct ath_softc *sc = aphy->sc;
 
 	mutex_lock(&sc->mutex);
 	sc->sc_flags &= ~SC_OP_SCANNING;
--- wireless-testing.orig/drivers/net/wireless/ath9k/ahb.c	2009-02-27 10:53:01.000000000 +0200
+++ wireless-testing/drivers/net/wireless/ath9k/ahb.c	2009-03-03 18:30:34.000000000 +0200
@@ -96,7 +96,8 @@ static int ath_ahb_probe(struct platform
 
 	irq = res->start;
 
-	hw = ieee80211_alloc_hw(sizeof(struct ath_softc), &ath9k_ops);
+	hw = ieee80211_alloc_hw(sizeof(struct ath_wiphy) +
+				sizeof(struct ath_softc), &ath9k_ops);
 	if (hw == NULL) {
 		dev_err(&pdev->dev, "no memory for ieee80211_hw\n");
 		ret = -ENOMEM;
@@ -106,7 +107,11 @@ static int ath_ahb_probe(struct platform
 	SET_IEEE80211_DEV(hw, &pdev->dev);
 	platform_set_drvdata(pdev, hw);
 
-	sc = hw->priv;
+	aphy = hw->priv;
+	sc = (struct ath_softc *) (aphy + 1);
+	aphy->sc = sc;
+	aphy->hw = hw;
+	sc->pri_wiphy = aphy;
 	sc->hw = hw;
 	sc->dev = &pdev->dev;
 	sc->mem = mem;
@@ -156,7 +161,8 @@ static int ath_ahb_remove(struct platfor
 	struct ieee80211_hw *hw = platform_get_drvdata(pdev);
 
 	if (hw) {
-		struct ath_softc *sc = hw->priv;
+		struct ath_wiphy *aphy = hw->priv;
+		struct ath_softc *sc = aphy->sc;
 
 		ath_cleanup(sc);
 		platform_set_drvdata(pdev, NULL);
--- wireless-testing.orig/drivers/net/wireless/ath9k/rc.c	2009-02-28 00:24:24.000000000 +0200
+++ wireless-testing/drivers/net/wireless/ath9k/rc.c	2009-03-03 18:30:34.000000000 +0200
@@ -1611,7 +1611,8 @@ static void ath_rate_init(void *priv, st
 
 static void *ath_rate_alloc(struct ieee80211_hw *hw, struct dentry *debugfsdir)
 {
-	return hw->priv;
+	struct ath_wiphy *aphy = hw->priv;
+	return aphy->sc;
 }
 
 static void ath_rate_free(void *priv)
--- wireless-testing.orig/drivers/net/wireless/ath9k/regd.c	2009-02-27 10:53:01.000000000 +0200
+++ wireless-testing/drivers/net/wireless/ath9k/regd.c	2009-03-03 18:30:34.000000000 +0200
@@ -311,7 +311,8 @@ void ath9k_reg_apply_radar_flags(struct 
 void ath9k_reg_apply_world_flags(struct wiphy *wiphy, enum reg_set_by setby)
 {
 	struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);
-	struct ath_softc *sc = hw->priv;
+	struct ath_wiphy *aphy = hw->priv;
+	struct ath_softc *sc = aphy->sc;
 	struct ath_hw *ah = sc->sc_ah;
 
 	switch (ah->regulatory.regpair->regDmnEnum) {
@@ -332,7 +333,8 @@ void ath9k_reg_apply_world_flags(struct 
 int ath9k_reg_notifier(struct wiphy *wiphy, struct regulatory_request *request)
 {
 	struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);
-	struct ath_softc *sc = hw->priv;
+	struct ath_wiphy *aphy = hw->priv;
+	struct ath_softc *sc = aphy->sc;
 
 	/* We always apply this */
 	ath9k_reg_apply_radar_flags(wiphy);
--- wireless-testing.orig/drivers/net/wireless/ath9k/recv.c	2009-02-27 10:53:01.000000000 +0200
+++ wireless-testing/drivers/net/wireless/ath9k/recv.c	2009-03-03 18:30:34.000000000 +0200
@@ -16,6 +16,12 @@
 
 #include "ath9k.h"
 
+static struct ieee80211_hw * ath_get_virt_hw(struct ath_softc *sc,
+					     struct ieee80211_hdr *hdr)
+{
+	return sc->pri_wiphy->hw;
+}
+
 /*
  * Setup and link descriptors.
  *
@@ -123,10 +129,12 @@ static int ath_rx_prepare(struct sk_buff
 	struct ieee80211_hdr *hdr;
 	u8 ratecode;
 	__le16 fc;
+	struct ieee80211_hw *hw;
 
 	hdr = (struct ieee80211_hdr *)skb->data;
 	fc = hdr->frame_control;
 	memset(rx_status, 0, sizeof(struct ieee80211_rx_status));
+	hw = ath_get_virt_hw(sc, hdr);
 
 	if (ds->ds_rxstat.rs_more) {
 		/*
@@ -186,7 +194,6 @@ static int ath_rx_prepare(struct sk_buff
 		rx_status->rate_idx = ratecode & 0x7f;
 	} else {
 		int i = 0, cur_band, n_rates;
-		struct ieee80211_hw *hw = sc->hw;
 
 		cur_band = hw->conf.channel->band;
 		n_rates = sc->sbands[cur_band].n_bitrates;
@@ -208,8 +215,8 @@ static int ath_rx_prepare(struct sk_buff
 	}
 
 	rx_status->mactime = ath_extend_tsf(sc, ds->ds_rxstat.rs_tstamp);
-	rx_status->band = sc->hw->conf.channel->band;
-	rx_status->freq =  sc->hw->conf.channel->center_freq;
+	rx_status->band = hw->conf.channel->band;
+	rx_status->freq = hw->conf.channel->center_freq;
 	rx_status->noise = sc->ani.noise_floor;
 	rx_status->signal = rx_status->noise + ds->ds_rxstat.rs_rssi;
 	rx_status->antenna = ds->ds_rxstat.rs_antenna;
@@ -604,7 +611,7 @@ int ath_rx_tasklet(struct ath_softc *sc,
 		}
 
 		/* Send the frame to mac80211 */
-		__ieee80211_rx(sc->hw, skb, &rx_status);
+		__ieee80211_rx(ath_get_virt_hw(sc, hdr), skb, &rx_status);
 
 		/* We will now give hardware our shiny new allocated skb */
 		bf->bf_mpdu = requeue_skb;
--- wireless-testing.orig/drivers/net/wireless/ath9k/virtual.c	2009-03-03 18:30:22.000000000 +0200
+++ wireless-testing/drivers/net/wireless/ath9k/virtual.c	2009-03-03 18:30:34.000000000 +0200
@@ -38,7 +38,8 @@ static void ath9k_vif_iter(void *data, u
 
 void ath9k_set_bssid_mask(struct ieee80211_hw *hw)
 {
-	struct ath_softc *sc = hw->priv;
+	struct ath_wiphy *aphy = hw->priv;
+	struct ath_softc *sc = aphy->sc;
 	struct ath9k_vif_iter_data iter_data;
 	int i, j;
 	u8 mask[ETH_ALEN];

-- 

-- 
Jouni Malinen                                            PGP id EFC895FA

^ permalink raw reply	[flat|nested] 21+ messages in thread

* [PATCH 04/15] ath9k: Add support for multiple secondary virtual wiphys
  2009-03-03 17:23 [PATCH 00/15] ath9k: Virtual interfaces and radios Jouni Malinen
                   ` (2 preceding siblings ...)
  2009-03-03 17:23 ` [PATCH 03/15] ath9k: Add data structure for supporting virtual radio/wiphy operation Jouni Malinen
@ 2009-03-03 17:23 ` Jouni Malinen
  2009-03-03 17:23 ` [PATCH 05/15] ath9k: Configure RX filter for multi-BSSID broadcast Jouni Malinen
                   ` (11 subsequent siblings)
  15 siblings, 0 replies; 21+ messages in thread
From: Jouni Malinen @ 2009-03-03 17:23 UTC (permalink / raw)
  To: John W. Linville; +Cc: linux-wireless, Jouni Malinen

The new struct ath_softc::sec_wiphy array is used to store information
about virtual wiphys and select which wiphy is used in calls to
mac80211. Each virtual wiphy will be assigned a different MAC address
based on the virtual wiphy index.

Signed-off-by: Jouni Malinen <jouni.malinen@atheros.com>

---
 drivers/net/wireless/ath9k/ath9k.h   |   15 ++++-
 drivers/net/wireless/ath9k/beacon.c  |   21 +++++---
 drivers/net/wireless/ath9k/main.c    |   65 ++++++++++++++++---------
 drivers/net/wireless/ath9k/rc.h      |    1 
 drivers/net/wireless/ath9k/recv.c    |   41 +++++++++++++++
 drivers/net/wireless/ath9k/virtual.c |   90 ++++++++++++++++++++++++++++++++++-
 drivers/net/wireless/ath9k/xmit.c    |   20 +++++--
 7 files changed, 212 insertions(+), 41 deletions(-)

--- wireless-testing.orig/drivers/net/wireless/ath9k/ath9k.h	2009-03-03 18:30:34.000000000 +0200
+++ wireless-testing/drivers/net/wireless/ath9k/ath9k.h	2009-03-03 18:30:47.000000000 +0200
@@ -373,10 +373,10 @@ int ath_tx_cleanup(struct ath_softc *sc)
 struct ath_txq *ath_test_get_txq(struct ath_softc *sc, struct sk_buff *skb);
 int ath_txq_update(struct ath_softc *sc, int qnum,
 		   struct ath9k_tx_queue_info *q);
-int ath_tx_start(struct ath_softc *sc, struct sk_buff *skb,
+int ath_tx_start(struct ieee80211_hw *hw, struct sk_buff *skb,
 		 struct ath_tx_control *txctl);
 void ath_tx_tasklet(struct ath_softc *sc);
-void ath_tx_cabq(struct ath_softc *sc, struct sk_buff *skb);
+void ath_tx_cabq(struct ieee80211_hw *hw, struct sk_buff *skb);
 bool ath_tx_aggr_check(struct ath_softc *sc, struct ath_node *an, u8 tidno);
 int ath_tx_aggr_start(struct ath_softc *sc, struct ieee80211_sta *sta,
 		      u16 tid, u16 *ssn);
@@ -429,6 +429,7 @@ struct ath_beacon {
 	u32 ast_be_xmit;
 	u64 bc_tstamp;
 	struct ieee80211_vif *bslot[ATH_BCBUF];
+	struct ath_wiphy *bslot_aphy[ATH_BCBUF];
 	int slottime;
 	int slotupdate;
 	struct ath9k_tx_queue_info beacon_qi;
@@ -440,7 +441,7 @@ struct ath_beacon {
 void ath_beacon_tasklet(unsigned long data);
 void ath_beacon_config(struct ath_softc *sc, struct ieee80211_vif *vif);
 int ath_beaconq_setup(struct ath_hw *ah);
-int ath_beacon_alloc(struct ath_softc *sc, struct ieee80211_vif *vif);
+int ath_beacon_alloc(struct ath_wiphy *aphy, struct ieee80211_vif *vif);
 void ath_beacon_return(struct ath_softc *sc, struct ath_vif *avp);
 
 /*******/
@@ -554,7 +555,12 @@ struct ath_wiphy;
 struct ath_softc {
 	struct ieee80211_hw *hw;
 	struct device *dev;
+
+	spinlock_t wiphy_lock; /* spinlock to protect ath_wiphy data */
 	struct ath_wiphy *pri_wiphy;
+	struct ath_wiphy **sec_wiphy; /* secondary wiphys (virtual radios); may
+				       * have NULL entries */
+	int num_sec_wiphy; /* number of sec_wiphy pointers in the array */
 	struct tasklet_struct intr_tq;
 	struct tasklet_struct bcon_tasklet;
 	struct ath_hw *sc_ah;
@@ -638,6 +644,7 @@ int ath_attach(u16 devid, struct ath_sof
 void ath_detach(struct ath_softc *sc);
 const char *ath_mac_bb_name(u32 mac_bb_version);
 const char *ath_rf_name(u16 rf_version);
+void ath_set_hw_capab(struct ath_softc *sc, struct ieee80211_hw *hw);
 
 #ifdef CONFIG_PCI
 int ath_pci_init(void);
@@ -674,5 +681,7 @@ static inline void ath9k_ps_restore(stru
 
 
 void ath9k_set_bssid_mask(struct ieee80211_hw *hw);
+int ath9k_wiphy_add(struct ath_softc *sc);
+int ath9k_wiphy_del(struct ath_wiphy *aphy);
 
 #endif /* ATH9K_H */
--- wireless-testing.orig/drivers/net/wireless/ath9k/main.c	2009-03-03 18:30:34.000000000 +0200
+++ wireless-testing/drivers/net/wireless/ath9k/main.c	2009-03-03 18:30:47.000000000 +0200
@@ -1305,6 +1305,7 @@ void ath_cleanup(struct ath_softc *sc)
 	ath_detach(sc);
 	free_irq(sc->irq, sc);
 	ath_bus_cleanup(sc);
+	kfree(sc->sec_wiphy);
 	ieee80211_free_hw(sc->hw);
 }
 
@@ -1322,6 +1323,14 @@ void ath_detach(struct ath_softc *sc)
 #endif
 	ath_deinit_leds(sc);
 
+	for (i = 0; i < sc->num_sec_wiphy; i++) {
+		struct ath_wiphy *aphy = sc->sec_wiphy[i];
+		if (aphy == NULL)
+			continue;
+		sc->sec_wiphy[i] = NULL;
+		ieee80211_unregister_hw(aphy->hw);
+		ieee80211_free_hw(aphy->hw);
+	}
 	ieee80211_unregister_hw(hw);
 	ath_rx_cleanup(sc);
 	ath_tx_cleanup(sc);
@@ -1355,6 +1364,7 @@ static int ath_init(u16 devid, struct at
 	if (ath9k_init_debug(sc) < 0)
 		printk(KERN_ERR "Unable to create debugfs files\n");
 
+	spin_lock_init(&sc->wiphy_lock);
 	spin_lock_init(&sc->sc_resetlock);
 	mutex_init(&sc->mutex);
 	tasklet_init(&sc->intr_tq, ath9k_tasklet, (unsigned long)sc);
@@ -1518,8 +1528,10 @@ static int ath_init(u16 devid, struct at
 	sc->beacon.slottime = ATH9K_SLOT_TIME_9;	/* default to short slot time */
 
 	/* initialize beacon slots */
-	for (i = 0; i < ARRAY_SIZE(sc->beacon.bslot); i++)
+	for (i = 0; i < ARRAY_SIZE(sc->beacon.bslot); i++) {
 		sc->beacon.bslot[i] = NULL;
+		sc->beacon.bslot_aphy[i] = NULL;
+	}
 
 	/* save MISC configurations */
 	sc->config.swBeaconProcess = 1;
@@ -1559,22 +1571,8 @@ bad:
 	return error;
 }
 
-int ath_attach(u16 devid, struct ath_softc *sc)
+void ath_set_hw_capab(struct ath_softc *sc, struct ieee80211_hw *hw)
 {
-	struct ieee80211_hw *hw = sc->hw;
-	const struct ieee80211_regdomain *regd;
-	int error = 0, i;
-
-	DPRINTF(sc, ATH_DBG_CONFIG, "Attach ATH hw\n");
-
-	error = ath_init(devid, sc);
-	if (error != 0)
-		return error;
-
-	/* get mac address from hardware and set in mac80211 */
-
-	SET_IEEE80211_PERM_ADDR(hw, sc->sc_ah->macaddr);
-
 	hw->flags = IEEE80211_HW_RX_INCLUDES_FCS |
 		IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING |
 		IEEE80211_HW_SIGNAL_DBM |
@@ -1602,17 +1600,37 @@ int ath_attach(u16 devid, struct ath_sof
 
 	hw->rate_control_algorithm = "ath9k_rate_control";
 
+	hw->wiphy->bands[IEEE80211_BAND_2GHZ] =
+		&sc->sbands[IEEE80211_BAND_2GHZ];
+	if (test_bit(ATH9K_MODE_11A, sc->sc_ah->caps.wireless_modes))
+		hw->wiphy->bands[IEEE80211_BAND_5GHZ] =
+			&sc->sbands[IEEE80211_BAND_5GHZ];
+}
+
+int ath_attach(u16 devid, struct ath_softc *sc)
+{
+	struct ieee80211_hw *hw = sc->hw;
+	const struct ieee80211_regdomain *regd;
+	int error = 0, i;
+
+	DPRINTF(sc, ATH_DBG_CONFIG, "Attach ATH hw\n");
+
+	error = ath_init(devid, sc);
+	if (error != 0)
+		return error;
+
+	/* get mac address from hardware and set in mac80211 */
+
+	SET_IEEE80211_PERM_ADDR(hw, sc->sc_ah->macaddr);
+
+	ath_set_hw_capab(sc, hw);
+
 	if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_HT) {
 		setup_ht_cap(sc, &sc->sbands[IEEE80211_BAND_2GHZ].ht_cap);
 		if (test_bit(ATH9K_MODE_11A, sc->sc_ah->caps.wireless_modes))
 			setup_ht_cap(sc, &sc->sbands[IEEE80211_BAND_5GHZ].ht_cap);
 	}
 
-	hw->wiphy->bands[IEEE80211_BAND_2GHZ] =	&sc->sbands[IEEE80211_BAND_2GHZ];
-	if (test_bit(ATH9K_MODE_11A, sc->sc_ah->caps.wireless_modes))
-		hw->wiphy->bands[IEEE80211_BAND_5GHZ] =
-			&sc->sbands[IEEE80211_BAND_5GHZ];
-
 	/* initialize tx/rx engine */
 	error = ath_tx_init(sc, ATH_TXBUF);
 	if (error != 0)
@@ -2065,7 +2083,7 @@ static int ath9k_tx(struct ieee80211_hw 
 
 	DPRINTF(sc, ATH_DBG_XMIT, "transmitting packet, skb: %p\n", skb);
 
-	if (ath_tx_start(sc, skb, &txctl) != 0) {
+	if (ath_tx_start(hw, skb, &txctl) != 0) {
 		DPRINTF(sc, ATH_DBG_XMIT, "TX failed\n");
 		goto exit;
 	}
@@ -2245,6 +2263,7 @@ static void ath9k_remove_interface(struc
 			printk(KERN_DEBUG "%s: vif had allocated beacon "
 			       "slot\n", __func__);
 			sc->beacon.bslot[i] = NULL;
+			sc->beacon.bslot_aphy[i] = NULL;
 		}
 	}
 
@@ -2386,7 +2405,7 @@ static int ath9k_config_interface(struct
 			 */
 			ath9k_hw_stoptxdma(sc->sc_ah, sc->beacon.beaconq);
 
-			error = ath_beacon_alloc(sc, vif);
+			error = ath_beacon_alloc(aphy, vif);
 			if (error != 0) {
 				mutex_unlock(&sc->mutex);
 				return error;
--- wireless-testing.orig/drivers/net/wireless/ath9k/recv.c	2009-03-03 18:30:34.000000000 +0200
+++ wireless-testing/drivers/net/wireless/ath9k/recv.c	2009-03-03 18:30:47.000000000 +0200
@@ -19,7 +19,22 @@
 static struct ieee80211_hw * ath_get_virt_hw(struct ath_softc *sc,
 					     struct ieee80211_hdr *hdr)
 {
-	return sc->pri_wiphy->hw;
+	struct ieee80211_hw *hw = sc->pri_wiphy->hw;
+	int i;
+
+	spin_lock_bh(&sc->wiphy_lock);
+	for (i = 0; i < sc->num_sec_wiphy; i++) {
+		struct ath_wiphy *aphy = sc->sec_wiphy[i];
+		if (aphy == NULL)
+			continue;
+		if (compare_ether_addr(hdr->addr1, aphy->hw->wiphy->perm_addr)
+		    == 0) {
+			hw = aphy->hw;
+			break;
+		}
+	}
+	spin_unlock_bh(&sc->wiphy_lock);
+	return hw;
 }
 
 /*
@@ -611,7 +626,29 @@ int ath_rx_tasklet(struct ath_softc *sc,
 		}
 
 		/* Send the frame to mac80211 */
-		__ieee80211_rx(ath_get_virt_hw(sc, hdr), skb, &rx_status);
+		if (hdr->addr1[5] & 0x01) {
+			int i;
+			/*
+			 * Deliver broadcast/multicast frames to all suitable
+			 * virtual wiphys.
+			 */
+			/* TODO: filter based on channel configuration */
+			for (i = 0; i < sc->num_sec_wiphy; i++) {
+				struct ath_wiphy *aphy = sc->sec_wiphy[i];
+				struct sk_buff *nskb;
+				if (aphy == NULL)
+					continue;
+				nskb = skb_copy(skb, GFP_ATOMIC);
+				if (nskb)
+					__ieee80211_rx(aphy->hw, nskb,
+						       &rx_status);
+			}
+			__ieee80211_rx(sc->hw, skb, &rx_status);
+		} else {
+			/* Deliver unicast frames based on receiver address */
+			__ieee80211_rx(ath_get_virt_hw(sc, hdr), skb,
+				       &rx_status);
+		}
 
 		/* We will now give hardware our shiny new allocated skb */
 		bf->bf_mpdu = requeue_skb;
--- wireless-testing.orig/drivers/net/wireless/ath9k/beacon.c	2009-03-03 18:29:02.000000000 +0200
+++ wireless-testing/drivers/net/wireless/ath9k/beacon.c	2009-03-03 18:30:47.000000000 +0200
@@ -113,9 +113,11 @@ static void ath_beacon_setup(struct ath_
 				     series, 4, 0);
 }
 
-static struct ath_buf *ath_beacon_generate(struct ath_softc *sc,
+static struct ath_buf *ath_beacon_generate(struct ieee80211_hw *hw,
 					   struct ieee80211_vif *vif)
 {
+	struct ath_wiphy *aphy = hw->priv;
+	struct ath_softc *sc = aphy->sc;
 	struct ath_buf *bf;
 	struct ath_vif *avp;
 	struct sk_buff *skb;
@@ -144,7 +146,7 @@ static struct ath_buf *ath_beacon_genera
 
 	/* Get a new beacon from mac80211 */
 
-	skb = ieee80211_beacon_get(sc->hw, vif);
+	skb = ieee80211_beacon_get(hw, vif);
 	bf->bf_mpdu = skb;
 	if (skb == NULL)
 		return NULL;
@@ -171,7 +173,7 @@ static struct ath_buf *ath_beacon_genera
 		return NULL;
 	}
 
-	skb = ieee80211_get_buffered_bc(sc->hw, vif);
+	skb = ieee80211_get_buffered_bc(hw, vif);
 
 	/*
 	 * if the CABQ traffic from previous DTIM is pending and the current
@@ -196,8 +198,8 @@ static struct ath_buf *ath_beacon_genera
 	ath_beacon_setup(sc, avp, bf);
 
 	while (skb) {
-		ath_tx_cabq(sc, skb);
-		skb = ieee80211_get_buffered_bc(sc->hw, vif);
+		ath_tx_cabq(hw, skb);
+		skb = ieee80211_get_buffered_bc(hw, vif);
 	}
 
 	return bf;
@@ -244,8 +246,9 @@ int ath_beaconq_setup(struct ath_hw *ah)
 	return ath9k_hw_setuptxqueue(ah, ATH9K_TX_QUEUE_BEACON, &qi);
 }
 
-int ath_beacon_alloc(struct ath_softc *sc, struct ieee80211_vif *vif)
+int ath_beacon_alloc(struct ath_wiphy *aphy, struct ieee80211_vif *vif)
 {
+	struct ath_softc *sc = aphy->sc;
 	struct ath_vif *avp;
 	struct ieee80211_hdr *hdr;
 	struct ath_buf *bf;
@@ -286,6 +289,7 @@ int ath_beacon_alloc(struct ath_softc *s
 				}
 			BUG_ON(sc->beacon.bslot[avp->av_bslot] != NULL);
 			sc->beacon.bslot[avp->av_bslot] = vif;
+			sc->beacon.bslot_aphy[avp->av_bslot] = aphy;
 			sc->nbcnvifs++;
 		}
 	}
@@ -368,6 +372,7 @@ void ath_beacon_return(struct ath_softc 
 
 		if (avp->av_bslot != -1) {
 			sc->beacon.bslot[avp->av_bslot] = NULL;
+			sc->beacon.bslot_aphy[avp->av_bslot] = NULL;
 			sc->nbcnvifs--;
 		}
 
@@ -391,6 +396,7 @@ void ath_beacon_tasklet(unsigned long da
 	struct ath_hw *ah = sc->sc_ah;
 	struct ath_buf *bf = NULL;
 	struct ieee80211_vif *vif;
+	struct ath_wiphy *aphy;
 	int slot;
 	u32 bfaddr, bc = 0, tsftu;
 	u64 tsf;
@@ -439,6 +445,7 @@ void ath_beacon_tasklet(unsigned long da
 	tsftu = TSF_TO_TU(tsf>>32, tsf);
 	slot = ((tsftu % intval) * ATH_BCBUF) / intval;
 	vif = sc->beacon.bslot[(slot + 1) % ATH_BCBUF];
+	aphy = sc->beacon.bslot_aphy[(slot + 1) % ATH_BCBUF];
 
 	DPRINTF(sc, ATH_DBG_BEACON,
 		"slot %d [tsf %llu tsftu %u intval %u] vif %p\n",
@@ -446,7 +453,7 @@ void ath_beacon_tasklet(unsigned long da
 
 	bfaddr = 0;
 	if (vif) {
-		bf = ath_beacon_generate(sc, vif);
+		bf = ath_beacon_generate(aphy->hw, vif);
 		if (bf != NULL) {
 			bfaddr = bf->bf_daddr;
 			bc = 1;
--- wireless-testing.orig/drivers/net/wireless/ath9k/rc.h	2009-02-28 00:24:24.000000000 +0200
+++ wireless-testing/drivers/net/wireless/ath9k/rc.h	2009-03-03 18:30:47.000000000 +0200
@@ -195,6 +195,7 @@ struct ath_rate_priv {
 };
 
 struct ath_tx_info_priv {
+	struct ath_wiphy *aphy;
 	struct ath_tx_status tx;
 	int n_frames;
 	int n_bad_frames;
--- wireless-testing.orig/drivers/net/wireless/ath9k/xmit.c	2009-02-27 10:53:01.000000000 +0200
+++ wireless-testing/drivers/net/wireless/ath9k/xmit.c	2009-03-03 18:30:47.000000000 +0200
@@ -1497,10 +1497,12 @@ static void ath_buf_set_rate(struct ath_
 		ath9k_hw_set11n_burstduration(sc->sc_ah, bf->bf_desc, 8192);
 }
 
-static int ath_tx_setup_buffer(struct ath_softc *sc, struct ath_buf *bf,
+static int ath_tx_setup_buffer(struct ieee80211_hw *hw, struct ath_buf *bf,
 				struct sk_buff *skb,
 				struct ath_tx_control *txctl)
 {
+	struct ath_wiphy *aphy = hw->priv;
+	struct ath_softc *sc = aphy->sc;
 	struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb);
 	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
 	struct ath_tx_info_priv *tx_info_priv;
@@ -1511,6 +1513,7 @@ static int ath_tx_setup_buffer(struct at
 	if (unlikely(!tx_info_priv))
 		return -ENOMEM;
 	tx_info->rate_driver_data[0] = tx_info_priv;
+	tx_info_priv->aphy = aphy;
 	hdrlen = ieee80211_get_hdrlen_from_skb(skb);
 	fc = hdr->frame_control;
 
@@ -1614,9 +1617,11 @@ static void ath_tx_start_dma(struct ath_
 }
 
 /* Upon failure caller should free skb */
-int ath_tx_start(struct ath_softc *sc, struct sk_buff *skb,
+int ath_tx_start(struct ieee80211_hw *hw, struct sk_buff *skb,
 		 struct ath_tx_control *txctl)
 {
+	struct ath_wiphy *aphy = hw->priv;
+	struct ath_softc *sc = aphy->sc;
 	struct ath_buf *bf;
 	int r;
 
@@ -1626,7 +1631,7 @@ int ath_tx_start(struct ath_softc *sc, s
 		return -1;
 	}
 
-	r = ath_tx_setup_buffer(sc, bf, skb, txctl);
+	r = ath_tx_setup_buffer(hw, bf, skb, txctl);
 	if (unlikely(r)) {
 		struct ath_txq *txq = txctl->txq;
 
@@ -1656,8 +1661,10 @@ int ath_tx_start(struct ath_softc *sc, s
 	return 0;
 }
 
-void ath_tx_cabq(struct ath_softc *sc, struct sk_buff *skb)
+void ath_tx_cabq(struct ieee80211_hw *hw, struct sk_buff *skb)
 {
+	struct ath_wiphy *aphy = hw->priv;
+	struct ath_softc *sc = aphy->sc;
 	int hdrlen, padsize;
 	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
 	struct ath_tx_control txctl;
@@ -1694,7 +1701,7 @@ void ath_tx_cabq(struct ath_softc *sc, s
 
 	DPRINTF(sc, ATH_DBG_XMIT, "transmitting CABQ packet, skb: %p\n", skb);
 
-	if (ath_tx_start(sc, skb, &txctl) != 0) {
+	if (ath_tx_start(hw, skb, &txctl) != 0) {
 		DPRINTF(sc, ATH_DBG_XMIT, "CABQ TX failed\n");
 		goto exit;
 	}
@@ -1718,6 +1725,9 @@ static void ath_tx_complete(struct ath_s
 
 	DPRINTF(sc, ATH_DBG_XMIT, "TX complete: skb: %p\n", skb);
 
+	if (tx_info_priv)
+		hw = tx_info_priv->aphy->hw;
+
 	if (tx_info->flags & IEEE80211_TX_CTL_NO_ACK ||
 	    tx_info->flags & IEEE80211_TX_STAT_TX_FILTERED) {
 		kfree(tx_info_priv);
--- wireless-testing.orig/drivers/net/wireless/ath9k/virtual.c	2009-03-03 18:30:34.000000000 +0200
+++ wireless-testing/drivers/net/wireless/ath9k/virtual.c	2009-03-03 18:30:47.000000000 +0200
@@ -57,8 +57,16 @@ void ath9k_set_bssid_mask(struct ieee802
 		iter_data.count = 0;
 
 	/* Get list of all active MAC addresses */
-	ieee80211_iterate_active_interfaces_atomic(hw, ath9k_vif_iter,
+	spin_lock_bh(&sc->wiphy_lock);
+	ieee80211_iterate_active_interfaces_atomic(sc->hw, ath9k_vif_iter,
 						   &iter_data);
+	for (i = 0; i < sc->num_sec_wiphy; i++) {
+		if (sc->sec_wiphy[i] == NULL)
+			continue;
+		ieee80211_iterate_active_interfaces_atomic(
+			sc->sec_wiphy[i]->hw, ath9k_vif_iter, &iter_data);
+	}
+	spin_unlock_bh(&sc->wiphy_lock);
 
 	/* Generate an address mask to cover all active addresses */
 	memset(mask, 0, ETH_ALEN);
@@ -87,3 +95,83 @@ void ath9k_set_bssid_mask(struct ieee802
 
 	ath9k_hw_setbssidmask(sc);
 }
+
+int ath9k_wiphy_add(struct ath_softc *sc)
+{
+	int i, error;
+	struct ath_wiphy *aphy;
+	struct ieee80211_hw *hw;
+	u8 addr[ETH_ALEN];
+
+	hw = ieee80211_alloc_hw(sizeof(struct ath_wiphy), &ath9k_ops);
+	if (hw == NULL)
+		return -ENOMEM;
+
+	spin_lock_bh(&sc->wiphy_lock);
+	for (i = 0; i < sc->num_sec_wiphy; i++) {
+		if (sc->sec_wiphy[i] == NULL)
+			break;
+	}
+
+	if (i == sc->num_sec_wiphy) {
+		/* No empty slot available; increase array length */
+		struct ath_wiphy **n;
+		n = krealloc(sc->sec_wiphy,
+			     (sc->num_sec_wiphy + 1) *
+			     sizeof(struct ath_wiphy *),
+			     GFP_ATOMIC);
+		if (n == NULL) {
+			spin_unlock_bh(&sc->wiphy_lock);
+			ieee80211_free_hw(hw);
+			return -ENOMEM;
+		}
+		n[i] = NULL;
+		sc->sec_wiphy = n;
+		sc->num_sec_wiphy++;
+	}
+
+	SET_IEEE80211_DEV(hw, sc->dev);
+
+	aphy = hw->priv;
+	aphy->sc = sc;
+	aphy->hw = hw;
+	sc->sec_wiphy[i] = aphy;
+	spin_unlock_bh(&sc->wiphy_lock);
+
+	memcpy(addr, sc->sc_ah->macaddr, ETH_ALEN);
+	addr[0] |= 0x02; /* Locally managed address */
+	/*
+	 * XOR virtual wiphy index into the least significant bits to generate
+	 * a different MAC address for each virtual wiphy.
+	 */
+	addr[5] ^= i & 0xff;
+	addr[4] ^= (i & 0xff00) >> 8;
+	addr[3] ^= (i & 0xff0000) >> 16;
+
+	SET_IEEE80211_PERM_ADDR(hw, addr);
+
+	ath_set_hw_capab(sc, hw);
+
+	error = ieee80211_register_hw(hw);
+
+	return error;
+}
+
+int ath9k_wiphy_del(struct ath_wiphy *aphy)
+{
+	struct ath_softc *sc = aphy->sc;
+	int i;
+
+	spin_lock_bh(&sc->wiphy_lock);
+	for (i = 0; i < sc->num_sec_wiphy; i++) {
+		if (aphy == sc->sec_wiphy[i]) {
+			sc->sec_wiphy[i] = NULL;
+			spin_unlock_bh(&sc->wiphy_lock);
+			ieee80211_unregister_hw(aphy->hw);
+			ieee80211_free_hw(aphy->hw);
+			return 0;
+		}
+	}
+	spin_unlock_bh(&sc->wiphy_lock);
+	return -ENOENT;
+}

-- 

-- 
Jouni Malinen                                            PGP id EFC895FA

^ permalink raw reply	[flat|nested] 21+ messages in thread

* [PATCH 05/15] ath9k: Configure RX filter for multi-BSSID broadcast
  2009-03-03 17:23 [PATCH 00/15] ath9k: Virtual interfaces and radios Jouni Malinen
                   ` (3 preceding siblings ...)
  2009-03-03 17:23 ` [PATCH 04/15] ath9k: Add support for multiple secondary virtual wiphys Jouni Malinen
@ 2009-03-03 17:23 ` Jouni Malinen
  2009-03-03 17:23 ` [PATCH 06/15] ath9k: Virtual wiphy pause/unpause functionality Jouni Malinen
                   ` (10 subsequent siblings)
  15 siblings, 0 replies; 21+ messages in thread
From: Jouni Malinen @ 2009-03-03 17:23 UTC (permalink / raw)
  To: John W. Linville; +Cc: linux-wireless, Jouni Malinen

Allow RX filter to pass through all broadcast/multicast frames (i.e.,
no BSSID filtering) if virtual interfaces are used. Software filtering
will be used in this case to drop broadcast/multicast frames for
foreign BSSIDs.

Signed-off-by: Jouni Malinen <jouni.malinen@atheros.com>

---
 drivers/net/wireless/ath9k/mac.h  |    1 +
 drivers/net/wireless/ath9k/recv.c |    8 ++++++++
 2 files changed, 9 insertions(+)

--- wireless-testing.orig/drivers/net/wireless/ath9k/recv.c	2009-03-03 18:30:47.000000000 +0200
+++ wireless-testing/drivers/net/wireless/ath9k/recv.c	2009-03-03 18:30:59.000000000 +0200
@@ -407,6 +407,14 @@ u32 ath_calcrxfilter(struct ath_softc *s
 	if (sc->sc_ah->opmode == NL80211_IFTYPE_AP)
 		rfilt |= ATH9K_RX_FILTER_PSPOLL;
 
+	if (sc->sec_wiphy) {
+		/* TODO: only needed if more than one BSSID is in use in
+		 * station/adhoc mode */
+		/* TODO: for older chips, may need to add ATH9K_RX_FILTER_PROM
+		 */
+		rfilt |= ATH9K_RX_FILTER_MCAST_BCAST_ALL;
+	}
+
 	return rfilt;
 
 #undef RX_FILTER_PRESERVE
--- wireless-testing.orig/drivers/net/wireless/ath9k/mac.h	2009-03-02 22:17:55.000000000 +0200
+++ wireless-testing/drivers/net/wireless/ath9k/mac.h	2009-03-03 18:30:59.000000000 +0200
@@ -570,6 +570,7 @@ enum ath9k_rx_filter {
 	ATH9K_RX_FILTER_MYBEACON = 0x00000200,
 	ATH9K_RX_FILTER_PSPOLL = 0x00004000,
 	ATH9K_RX_FILTER_PHYRADAR = 0x00002000,
+	ATH9K_RX_FILTER_MCAST_BCAST_ALL = 0x00008000,
 };
 
 #define ATH9K_RATESERIES_RTS_CTS  0x0001

-- 

-- 
Jouni Malinen                                            PGP id EFC895FA

^ permalink raw reply	[flat|nested] 21+ messages in thread

* [PATCH 06/15] ath9k: Virtual wiphy pause/unpause functionality
  2009-03-03 17:23 [PATCH 00/15] ath9k: Virtual interfaces and radios Jouni Malinen
                   ` (4 preceding siblings ...)
  2009-03-03 17:23 ` [PATCH 05/15] ath9k: Configure RX filter for multi-BSSID broadcast Jouni Malinen
@ 2009-03-03 17:23 ` Jouni Malinen
  2009-03-03 17:23 ` [PATCH 07/15] ath9k: Add routines for switching between active virtual wiphys Jouni Malinen
                   ` (9 subsequent siblings)
  15 siblings, 0 replies; 21+ messages in thread
From: Jouni Malinen @ 2009-03-03 17:23 UTC (permalink / raw)
  To: John W. Linville; +Cc: linux-wireless, Jouni Malinen

Allow virtual wiphys to be paused/unpaused to allow off-channel
operations. Pause will stop all TX queues for the wiphy and move the
STA into power save mode if in managed mode. Unpause wakes up the TX
queues and notifies the AP that the STA woke up if in managed mode.

Signed-off-by: Jouni Malinen <jouni.malinen@atheros.com>

---
 drivers/net/wireless/ath9k/ath9k.h   |   10 ++
 drivers/net/wireless/ath9k/beacon.c  |    3 
 drivers/net/wireless/ath9k/main.c    |    1 
 drivers/net/wireless/ath9k/rc.h      |    7 +
 drivers/net/wireless/ath9k/virtual.c |  161 +++++++++++++++++++++++++++++++++++
 drivers/net/wireless/ath9k/xmit.c    |   11 +-
 6 files changed, 191 insertions(+), 2 deletions(-)

--- wireless-testing.orig/drivers/net/wireless/ath9k/ath9k.h	2009-03-03 18:30:47.000000000 +0200
+++ wireless-testing/drivers/net/wireless/ath9k/ath9k.h	2009-03-03 18:31:09.000000000 +0200
@@ -292,6 +292,7 @@ struct ath_atx_ac {
 struct ath_tx_control {
 	struct ath_txq *txq;
 	int if_id;
+	enum ath9k_internal_frame_type frame_type;
 };
 
 struct ath_xmit_status {
@@ -392,6 +393,7 @@ struct ath_vif {
 	enum nl80211_iftype av_opmode;
 	struct ath_buf *av_bcbuf;
 	struct ath_tx_control av_btxctl;
+	u8 bssid[ETH_ALEN]; /* current BSSID from config_interface */
 };
 
 /*******************/
@@ -619,6 +621,11 @@ struct ath_softc {
 struct ath_wiphy {
 	struct ath_softc *sc; /* shared for all virtual wiphys */
 	struct ieee80211_hw *hw;
+	enum ath_wiphy_state {
+		ATH_WIPHY_ACTIVE,
+		ATH_WIPHY_PAUSING,
+		ATH_WIPHY_PAUSED,
+	} state;
 };
 
 int ath_reset(struct ath_softc *sc, bool retry_tx);
@@ -683,5 +690,8 @@ static inline void ath9k_ps_restore(stru
 void ath9k_set_bssid_mask(struct ieee80211_hw *hw);
 int ath9k_wiphy_add(struct ath_softc *sc);
 int ath9k_wiphy_del(struct ath_wiphy *aphy);
+void ath9k_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb);
+int ath9k_wiphy_pause(struct ath_wiphy *aphy);
+int ath9k_wiphy_unpause(struct ath_wiphy *aphy);
 
 #endif /* ATH9K_H */
--- wireless-testing.orig/drivers/net/wireless/ath9k/main.c	2009-03-03 18:30:47.000000000 +0200
+++ wireless-testing/drivers/net/wireless/ath9k/main.c	2009-03-03 18:31:09.000000000 +0200
@@ -2371,6 +2371,7 @@ static int ath9k_config_interface(struct
 		case NL80211_IFTYPE_ADHOC:
 			/* Set BSSID */
 			memcpy(sc->curbssid, conf->bssid, ETH_ALEN);
+			memcpy(avp->bssid, conf->bssid, ETH_ALEN);
 			sc->curaid = 0;
 			ath9k_hw_write_associd(sc);
 
--- wireless-testing.orig/drivers/net/wireless/ath9k/xmit.c	2009-03-03 18:30:47.000000000 +0200
+++ wireless-testing/drivers/net/wireless/ath9k/xmit.c	2009-03-03 18:31:09.000000000 +0200
@@ -1514,6 +1514,7 @@ static int ath_tx_setup_buffer(struct ie
 		return -ENOMEM;
 	tx_info->rate_driver_data[0] = tx_info_priv;
 	tx_info_priv->aphy = aphy;
+	tx_info_priv->frame_type = txctl->frame_type;
 	hdrlen = ieee80211_get_hdrlen_from_skb(skb);
 	fc = hdr->frame_control;
 
@@ -1722,11 +1723,14 @@ static void ath_tx_complete(struct ath_s
 	struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb);
 	struct ath_tx_info_priv *tx_info_priv = ATH_TX_INFO_PRIV(tx_info);
 	int hdrlen, padsize;
+	int frame_type = ATH9K_NOT_INTERNAL;
 
 	DPRINTF(sc, ATH_DBG_XMIT, "TX complete: skb: %p\n", skb);
 
-	if (tx_info_priv)
+	if (tx_info_priv) {
 		hw = tx_info_priv->aphy->hw;
+		frame_type = tx_info_priv->frame_type;
+	}
 
 	if (tx_info->flags & IEEE80211_TX_CTL_NO_ACK ||
 	    tx_info->flags & IEEE80211_TX_STAT_TX_FILTERED) {
@@ -1757,7 +1761,10 @@ static void ath_tx_complete(struct ath_s
 		skb_pull(skb, padsize);
 	}
 
-	ieee80211_tx_status(hw, skb);
+	if (frame_type == ATH9K_NOT_INTERNAL)
+		ieee80211_tx_status(hw, skb);
+	else
+		ath9k_tx_status(hw, skb);
 }
 
 static void ath_tx_complete_buf(struct ath_softc *sc, struct ath_buf *bf,
--- wireless-testing.orig/drivers/net/wireless/ath9k/rc.h	2009-03-03 18:30:47.000000000 +0200
+++ wireless-testing/drivers/net/wireless/ath9k/rc.h	2009-03-03 18:31:09.000000000 +0200
@@ -194,12 +194,19 @@ struct ath_rate_priv {
 	struct ath_rate_softc *asc;
 };
 
+enum ath9k_internal_frame_type {
+	ATH9K_NOT_INTERNAL,
+	ATH9K_INT_PAUSE,
+	ATH9K_INT_UNPAUSE
+};
+
 struct ath_tx_info_priv {
 	struct ath_wiphy *aphy;
 	struct ath_tx_status tx;
 	int n_frames;
 	int n_bad_frames;
 	bool update_rc;
+	enum ath9k_internal_frame_type frame_type;
 };
 
 #define ATH_TX_INFO_PRIV(tx_info) \
--- wireless-testing.orig/drivers/net/wireless/ath9k/virtual.c	2009-03-03 18:30:47.000000000 +0200
+++ wireless-testing/drivers/net/wireless/ath9k/virtual.c	2009-03-03 18:31:09.000000000 +0200
@@ -175,3 +175,164 @@ int ath9k_wiphy_del(struct ath_wiphy *ap
 	spin_unlock_bh(&sc->wiphy_lock);
 	return -ENOENT;
 }
+
+static int ath9k_send_nullfunc(struct ath_wiphy *aphy,
+			       struct ieee80211_vif *vif, const u8 *bssid,
+			       int ps)
+{
+	struct ath_softc *sc = aphy->sc;
+	struct ath_tx_control txctl;
+	struct sk_buff *skb;
+	struct ieee80211_hdr *hdr;
+	__le16 fc;
+	struct ieee80211_tx_info *info;
+
+	skb = dev_alloc_skb(24);
+	if (skb == NULL)
+		return -ENOMEM;
+	hdr = (struct ieee80211_hdr *) skb_put(skb, 24);
+	memset(hdr, 0, 24);
+	fc = cpu_to_le16(IEEE80211_FTYPE_DATA | IEEE80211_STYPE_NULLFUNC |
+			 IEEE80211_FCTL_TODS);
+	if (ps)
+		fc |= cpu_to_le16(IEEE80211_FCTL_PM);
+	hdr->frame_control = fc;
+	memcpy(hdr->addr1, bssid, ETH_ALEN);
+	memcpy(hdr->addr2, aphy->hw->wiphy->perm_addr, ETH_ALEN);
+	memcpy(hdr->addr3, bssid, ETH_ALEN);
+
+	info = IEEE80211_SKB_CB(skb);
+	memset(info, 0, sizeof(*info));
+	info->flags = IEEE80211_TX_CTL_REQ_TX_STATUS;
+	info->control.vif = vif;
+	info->control.rates[0].idx = 0;
+	info->control.rates[0].count = 4;
+	info->control.rates[1].idx = -1;
+
+	memset(&txctl, 0, sizeof(struct ath_tx_control));
+	txctl.txq = &sc->tx.txq[sc->tx.hwq_map[ATH9K_WME_AC_VO]];
+	txctl.frame_type = ps ? ATH9K_INT_PAUSE : ATH9K_INT_UNPAUSE;
+
+	if (ath_tx_start(aphy->hw, skb, &txctl) != 0)
+		goto exit;
+
+	return 0;
+exit:
+	dev_kfree_skb_any(skb);
+	return -1;
+}
+
+/*
+ * ath9k version of ieee80211_tx_status() for TX frames that are generated
+ * internally in the driver.
+ */
+void ath9k_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb)
+{
+	struct ath_wiphy *aphy = hw->priv;
+	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+	struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb);
+	struct ath_tx_info_priv *tx_info_priv = ATH_TX_INFO_PRIV(tx_info);
+
+	if (tx_info_priv && tx_info_priv->frame_type == ATH9K_INT_PAUSE &&
+	    aphy->state == ATH_WIPHY_PAUSING) {
+		if (!(info->flags & IEEE80211_TX_STAT_ACK)) {
+			printk(KERN_DEBUG "ath9k: %s: no ACK for pause "
+			       "frame\n", wiphy_name(hw->wiphy));
+			/*
+			 * The AP did not reply; ignore this to allow us to
+			 * continue.
+			 */
+		}
+		aphy->state = ATH_WIPHY_PAUSED;
+	}
+
+	kfree(tx_info_priv);
+	tx_info->rate_driver_data[0] = NULL;
+
+	dev_kfree_skb(skb);
+}
+
+static void ath9k_pause_iter(void *data, u8 *mac, struct ieee80211_vif *vif)
+{
+	struct ath_wiphy *aphy = data;
+	struct ath_vif *avp = (void *) vif->drv_priv;
+
+	switch (vif->type) {
+	case NL80211_IFTYPE_STATION:
+		if (!vif->bss_conf.assoc) {
+			aphy->state = ATH_WIPHY_PAUSED;
+			break;
+		}
+		/* TODO: could avoid this if already in PS mode */
+		ath9k_send_nullfunc(aphy, vif, avp->bssid, 1);
+		break;
+	case NL80211_IFTYPE_AP:
+		/* Beacon transmission is paused by aphy->state change */
+		aphy->state = ATH_WIPHY_PAUSED;
+		break;
+	default:
+		break;
+	}
+}
+
+/* caller must hold wiphy_lock */
+static int __ath9k_wiphy_pause(struct ath_wiphy *aphy)
+{
+	ieee80211_stop_queues(aphy->hw);
+	aphy->state = ATH_WIPHY_PAUSING;
+	/*
+	 * TODO: handle PAUSING->PAUSED for the case where there are multiple
+	 * active vifs (now we do it on the first vif getting ready; should be
+	 * on the last)
+	 */
+	ieee80211_iterate_active_interfaces_atomic(aphy->hw, ath9k_pause_iter,
+						   aphy);
+	return 0;
+}
+
+int ath9k_wiphy_pause(struct ath_wiphy *aphy)
+{
+	int ret;
+	spin_lock_bh(&aphy->sc->wiphy_lock);
+	ret = __ath9k_wiphy_pause(aphy);
+	spin_unlock_bh(&aphy->sc->wiphy_lock);
+	return ret;
+}
+
+static void ath9k_unpause_iter(void *data, u8 *mac, struct ieee80211_vif *vif)
+{
+	struct ath_wiphy *aphy = data;
+	struct ath_vif *avp = (void *) vif->drv_priv;
+
+	switch (vif->type) {
+	case NL80211_IFTYPE_STATION:
+		if (!vif->bss_conf.assoc)
+			break;
+		ath9k_send_nullfunc(aphy, vif, avp->bssid, 0);
+		break;
+	case NL80211_IFTYPE_AP:
+		/* Beacon transmission is re-enabled by aphy->state change */
+		break;
+	default:
+		break;
+	}
+}
+
+/* caller must hold wiphy_lock */
+static int __ath9k_wiphy_unpause(struct ath_wiphy *aphy)
+{
+	ieee80211_iterate_active_interfaces_atomic(aphy->hw,
+						   ath9k_unpause_iter, aphy);
+	aphy->state = ATH_WIPHY_ACTIVE;
+	ieee80211_wake_queues(aphy->hw);
+	return 0;
+}
+
+int ath9k_wiphy_unpause(struct ath_wiphy *aphy)
+{
+	int ret;
+	spin_lock_bh(&aphy->sc->wiphy_lock);
+	ret = __ath9k_wiphy_unpause(aphy);
+	spin_unlock_bh(&aphy->sc->wiphy_lock);
+	return ret;
+}
--- wireless-testing.orig/drivers/net/wireless/ath9k/beacon.c	2009-03-03 18:30:47.000000000 +0200
+++ wireless-testing/drivers/net/wireless/ath9k/beacon.c	2009-03-03 18:31:09.000000000 +0200
@@ -125,6 +125,9 @@ static struct ath_buf *ath_beacon_genera
 	struct ieee80211_tx_info *info;
 	int cabq_depth;
 
+	if (aphy->state != ATH_WIPHY_ACTIVE)
+		return NULL;
+
 	avp = (void *)vif->drv_priv;
 	cabq = sc->beacon.cabq;
 

-- 

-- 
Jouni Malinen                                            PGP id EFC895FA

^ permalink raw reply	[flat|nested] 21+ messages in thread

* [PATCH 07/15] ath9k: Add routines for switching between active virtual wiphys
  2009-03-03 17:23 [PATCH 00/15] ath9k: Virtual interfaces and radios Jouni Malinen
                   ` (5 preceding siblings ...)
  2009-03-03 17:23 ` [PATCH 06/15] ath9k: Virtual wiphy pause/unpause functionality Jouni Malinen
@ 2009-03-03 17:23 ` Jouni Malinen
  2009-03-03 17:23 ` [PATCH 08/15] ath9k: Make start/stop operations aware of " Jouni Malinen
                   ` (8 subsequent siblings)
  15 siblings, 0 replies; 21+ messages in thread
From: Jouni Malinen @ 2009-03-03 17:23 UTC (permalink / raw)
  To: John W. Linville; +Cc: linux-wireless, Jouni Malinen

ath9k_wiphy_select() can be used to select a virtual wiphy to be
activated. Other virtual wiphys will be paused and once that is done,
the operational channel is changed and the wiphys that are on the
selected channel will be unpaused.

Signed-off-by: Jouni Malinen <jouni.malinen@atheros.com>

---
 drivers/net/wireless/ath9k/ath9k.h   |   14 +++
 drivers/net/wireless/ath9k/main.c    |   27 ++++--
 drivers/net/wireless/ath9k/virtual.c |  147 ++++++++++++++++++++++++++++++++++-
 3 files changed, 176 insertions(+), 12 deletions(-)

--- wireless-testing.orig/drivers/net/wireless/ath9k/ath9k.h	2009-03-03 18:31:09.000000000 +0200
+++ wireless-testing/drivers/net/wireless/ath9k/ath9k.h	2009-03-03 18:31:20.000000000 +0200
@@ -563,6 +563,11 @@ struct ath_softc {
 	struct ath_wiphy **sec_wiphy; /* secondary wiphys (virtual radios); may
 				       * have NULL entries */
 	int num_sec_wiphy; /* number of sec_wiphy pointers in the array */
+	int chan_idx;
+	int chan_is_ht;
+	struct ath_wiphy *next_wiphy;
+	struct work_struct chan_work;
+
 	struct tasklet_struct intr_tq;
 	struct tasklet_struct bcon_tasklet;
 	struct ath_hw *sc_ah;
@@ -626,6 +631,8 @@ struct ath_wiphy {
 		ATH_WIPHY_PAUSING,
 		ATH_WIPHY_PAUSED,
 	} state;
+	int chan_idx;
+	int chan_is_ht;
 };
 
 int ath_reset(struct ath_softc *sc, bool retry_tx);
@@ -652,6 +659,11 @@ void ath_detach(struct ath_softc *sc);
 const char *ath_mac_bb_name(u32 mac_bb_version);
 const char *ath_rf_name(u16 rf_version);
 void ath_set_hw_capab(struct ath_softc *sc, struct ieee80211_hw *hw);
+void ath9k_update_ichannel(struct ath_softc *sc, struct ieee80211_hw *hw,
+			   struct ath9k_channel *ichan);
+void ath_update_chainmask(struct ath_softc *sc, int is_ht);
+int ath_set_channel(struct ath_softc *sc, struct ieee80211_hw *hw,
+		    struct ath9k_channel *hchan);
 
 #ifdef CONFIG_PCI
 int ath_pci_init(void);
@@ -693,5 +705,7 @@ int ath9k_wiphy_del(struct ath_wiphy *ap
 void ath9k_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb);
 int ath9k_wiphy_pause(struct ath_wiphy *aphy);
 int ath9k_wiphy_unpause(struct ath_wiphy *aphy);
+int ath9k_wiphy_select(struct ath_wiphy *aphy);
+void ath9k_wiphy_chan_work(struct work_struct *work);
 
 #endif /* ATH9K_H */
--- wireless-testing.orig/drivers/net/wireless/ath9k/main.c	2009-03-03 18:31:09.000000000 +0200
+++ wireless-testing/drivers/net/wireless/ath9k/main.c	2009-03-03 18:31:20.000000000 +0200
@@ -236,11 +236,11 @@ static void ath_setup_rates(struct ath_s
  * by reseting the chip.  To accomplish this we must first cleanup any pending
  * DMA, then restart stuff.
 */
-static int ath_set_channel(struct ath_softc *sc, struct ath9k_channel *hchan)
+int ath_set_channel(struct ath_softc *sc, struct ieee80211_hw *hw,
+		    struct ath9k_channel *hchan)
 {
 	struct ath_hw *ah = sc->sc_ah;
 	bool fastcc = true, stopped;
-	struct ieee80211_hw *hw = sc->hw;
 	struct ieee80211_channel *channel = hw->conf.channel;
 	int r;
 
@@ -414,7 +414,7 @@ set_timer:
  * the chainmask configuration, for bt coexistence, use
  * the chainmask configuration even in legacy mode.
  */
-static void ath_update_chainmask(struct ath_softc *sc, int is_ht)
+void ath_update_chainmask(struct ath_softc *sc, int is_ht)
 {
 	sc->sc_flags |= SC_OP_CHAINMASK_UPDATE;
 	if (is_ht ||
@@ -1322,6 +1322,7 @@ void ath_detach(struct ath_softc *sc)
 	ath_deinit_rfkill(sc);
 #endif
 	ath_deinit_leds(sc);
+	cancel_work_sync(&sc->chan_work);
 
 	for (i = 0; i < sc->num_sec_wiphy; i++) {
 		struct ath_wiphy *aphy = sc->sec_wiphy[i];
@@ -1667,6 +1668,8 @@ int ath_attach(u16 devid, struct ath_sof
 	ath9k_reg_apply_radar_flags(hw->wiphy);
 	ath9k_reg_apply_world_flags(hw->wiphy, REGDOM_SET_BY_INIT);
 
+	INIT_WORK(&sc->chan_work, ath9k_wiphy_chan_work);
+
 	error = ieee80211_register_hw(hw);
 
 	if (!ath9k_is_world_regd(sc->sc_ah)) {
@@ -1915,10 +1918,9 @@ int ath_get_mac80211_qnum(u32 queue, str
 
 /* XXX: Remove me once we don't depend on ath9k_channel for all
  * this redundant data */
-static void ath9k_update_ichannel(struct ath_softc *sc,
-			  struct ath9k_channel *ichan)
+void ath9k_update_ichannel(struct ath_softc *sc, struct ieee80211_hw *hw,
+			   struct ath9k_channel *ichan)
 {
-	struct ieee80211_hw *hw = sc->hw;
 	struct ieee80211_channel *chan = hw->conf.channel;
 	struct ieee80211_conf *conf = &hw->conf;
 
@@ -1965,8 +1967,9 @@ static int ath9k_start(struct ieee80211_
 
 	pos = curchan->hw_value;
 
+	sc->chan_idx = pos;
 	init_channel = &sc->sc_ah->channels[pos];
-	ath9k_update_ichannel(sc, init_channel);
+	ath9k_update_ichannel(sc, hw, init_channel);
 
 	/* Reset SERDES registers */
 	ath9k_hw_configpcipowersave(sc->sc_ah, 0);
@@ -2305,15 +2308,21 @@ static int ath9k_config(struct ieee80211
 		struct ieee80211_channel *curchan = hw->conf.channel;
 		int pos = curchan->hw_value;
 
+		aphy->chan_idx = pos;
+		aphy->chan_is_ht = conf_is_ht(conf);
+
+		/* TODO: do not change operation channel immediately if there
+		 * are other virtual wiphys that use another channel */
+
 		DPRINTF(sc, ATH_DBG_CONFIG, "Set channel: %d MHz\n",
 			curchan->center_freq);
 
 		/* XXX: remove me eventualy */
-		ath9k_update_ichannel(sc, &sc->sc_ah->channels[pos]);
+		ath9k_update_ichannel(sc, hw, &sc->sc_ah->channels[pos]);
 
 		ath_update_chainmask(sc, conf_is_ht(conf));
 
-		if (ath_set_channel(sc, &sc->sc_ah->channels[pos]) < 0) {
+		if (ath_set_channel(sc, hw, &sc->sc_ah->channels[pos]) < 0) {
 			DPRINTF(sc, ATH_DBG_FATAL, "Unable to set channel\n");
 			mutex_unlock(&sc->mutex);
 			return -EINVAL;
--- wireless-testing.orig/drivers/net/wireless/ath9k/virtual.c	2009-03-03 18:31:09.000000000 +0200
+++ wireless-testing/drivers/net/wireless/ath9k/virtual.c	2009-03-03 18:31:20.000000000 +0200
@@ -222,6 +222,81 @@ exit:
 	return -1;
 }
 
+static bool __ath9k_wiphy_pausing(struct ath_softc *sc)
+{
+	int i;
+	if (sc->pri_wiphy->state == ATH_WIPHY_PAUSING)
+		return true;
+	for (i = 0; i < sc->num_sec_wiphy; i++) {
+		if (sc->sec_wiphy[i] &&
+		    sc->sec_wiphy[i]->state == ATH_WIPHY_PAUSING)
+			return true;
+	}
+	return false;
+}
+
+static bool ath9k_wiphy_pausing(struct ath_softc *sc)
+{
+	bool ret;
+	spin_lock_bh(&sc->wiphy_lock);
+	ret = __ath9k_wiphy_pausing(sc);
+	spin_unlock_bh(&sc->wiphy_lock);
+	return ret;
+}
+
+static int __ath9k_wiphy_unpause(struct ath_wiphy *aphy);
+
+/* caller must hold wiphy_lock */
+static void __ath9k_wiphy_unpause_ch(struct ath_wiphy *aphy)
+{
+	if (aphy == NULL)
+		return;
+	if (aphy->chan_idx != aphy->sc->chan_idx)
+		return; /* wiphy not on the selected channel */
+	__ath9k_wiphy_unpause(aphy);
+}
+
+static void ath9k_wiphy_unpause_channel(struct ath_softc *sc)
+{
+	int i;
+	spin_lock_bh(&sc->wiphy_lock);
+	__ath9k_wiphy_unpause_ch(sc->pri_wiphy);
+	for (i = 0; i < sc->num_sec_wiphy; i++)
+		__ath9k_wiphy_unpause_ch(sc->sec_wiphy[i]);
+	spin_unlock_bh(&sc->wiphy_lock);
+}
+
+void ath9k_wiphy_chan_work(struct work_struct *work)
+{
+	struct ath_softc *sc = container_of(work, struct ath_softc, chan_work);
+	struct ath_wiphy *aphy = sc->next_wiphy;
+
+	if (aphy == NULL)
+		return;
+
+	/*
+	 * All pending interfaces paused; ready to change
+	 * channels.
+	 */
+
+	/* Change channels */
+	mutex_lock(&sc->mutex);
+	/* XXX: remove me eventually */
+	ath9k_update_ichannel(sc, aphy->hw,
+			      &sc->sc_ah->channels[sc->chan_idx]);
+	ath_update_chainmask(sc, sc->chan_is_ht);
+	if (ath_set_channel(sc, aphy->hw,
+			    &sc->sc_ah->channels[sc->chan_idx]) < 0) {
+		printk(KERN_DEBUG "ath9k: Failed to set channel for new "
+		       "virtual wiphy\n");
+		mutex_unlock(&sc->mutex);
+		return;
+	}
+	mutex_unlock(&sc->mutex);
+
+	ath9k_wiphy_unpause_channel(sc);
+}
+
 /*
  * ath9k version of ieee80211_tx_status() for TX frames that are generated
  * internally in the driver.
@@ -244,6 +319,14 @@ void ath9k_tx_status(struct ieee80211_hw
 			 */
 		}
 		aphy->state = ATH_WIPHY_PAUSED;
+		if (!ath9k_wiphy_pausing(aphy->sc)) {
+			/*
+			 * Drop from tasklet to work to allow mutex for channel
+			 * change.
+			 */
+			queue_work(aphy->sc->hw->workqueue,
+				   &aphy->sc->chan_work);
+		}
 	}
 
 	kfree(tx_info_priv);
@@ -252,6 +335,14 @@ void ath9k_tx_status(struct ieee80211_hw
 	dev_kfree_skb(skb);
 }
 
+static void ath9k_mark_paused(struct ath_wiphy *aphy)
+{
+	struct ath_softc *sc = aphy->sc;
+	aphy->state = ATH_WIPHY_PAUSED;
+	if (!__ath9k_wiphy_pausing(sc))
+		queue_work(sc->hw->workqueue, &sc->chan_work);
+}
+
 static void ath9k_pause_iter(void *data, u8 *mac, struct ieee80211_vif *vif)
 {
 	struct ath_wiphy *aphy = data;
@@ -260,15 +351,19 @@ static void ath9k_pause_iter(void *data,
 	switch (vif->type) {
 	case NL80211_IFTYPE_STATION:
 		if (!vif->bss_conf.assoc) {
-			aphy->state = ATH_WIPHY_PAUSED;
+			ath9k_mark_paused(aphy);
 			break;
 		}
 		/* TODO: could avoid this if already in PS mode */
-		ath9k_send_nullfunc(aphy, vif, avp->bssid, 1);
+		if (ath9k_send_nullfunc(aphy, vif, avp->bssid, 1)) {
+			printk(KERN_DEBUG "%s: failed to send PS nullfunc\n",
+			       __func__);
+			ath9k_mark_paused(aphy);
+		}
 		break;
 	case NL80211_IFTYPE_AP:
 		/* Beacon transmission is paused by aphy->state change */
-		aphy->state = ATH_WIPHY_PAUSED;
+		ath9k_mark_paused(aphy);
 		break;
 	default:
 		break;
@@ -336,3 +431,49 @@ int ath9k_wiphy_unpause(struct ath_wiphy
 	spin_unlock_bh(&aphy->sc->wiphy_lock);
 	return ret;
 }
+
+/* caller must hold wiphy_lock */
+static void __ath9k_wiphy_pause_all(struct ath_softc *sc)
+{
+	int i;
+	if (sc->pri_wiphy->state == ATH_WIPHY_ACTIVE)
+		__ath9k_wiphy_pause(sc->pri_wiphy);
+	for (i = 0; i < sc->num_sec_wiphy; i++) {
+		if (sc->sec_wiphy[i] &&
+		    sc->sec_wiphy[i]->state == ATH_WIPHY_ACTIVE)
+			__ath9k_wiphy_pause(sc->sec_wiphy[i]);
+	}
+}
+
+int ath9k_wiphy_select(struct ath_wiphy *aphy)
+{
+	struct ath_softc *sc = aphy->sc;
+	bool now;
+
+	spin_lock_bh(&sc->wiphy_lock);
+	if (__ath9k_wiphy_pausing(sc)) {
+		spin_unlock_bh(&sc->wiphy_lock);
+		return -EBUSY; /* previous select still in progress */
+	}
+
+	/* Store the new channel */
+	sc->chan_idx = aphy->chan_idx;
+	sc->chan_is_ht = aphy->chan_is_ht;
+	sc->next_wiphy = aphy;
+
+	__ath9k_wiphy_pause_all(sc);
+	now = !__ath9k_wiphy_pausing(aphy->sc);
+	spin_unlock_bh(&sc->wiphy_lock);
+
+	if (now) {
+		/* Ready to request channel change immediately */
+		queue_work(aphy->sc->hw->workqueue, &aphy->sc->chan_work);
+	}
+
+	/*
+	 * wiphys will be unpaused in ath9k_tx_status() once channel has been
+	 * changed if any wiphy needs time to become paused.
+	 */
+
+	return 0;
+}

-- 

-- 
Jouni Malinen                                            PGP id EFC895FA

^ permalink raw reply	[flat|nested] 21+ messages in thread

* [PATCH 08/15] ath9k: Make start/stop operations aware of virtual wiphys
  2009-03-03 17:23 [PATCH 00/15] ath9k: Virtual interfaces and radios Jouni Malinen
                   ` (6 preceding siblings ...)
  2009-03-03 17:23 ` [PATCH 07/15] ath9k: Add routines for switching between active virtual wiphys Jouni Malinen
@ 2009-03-03 17:23 ` Jouni Malinen
  2009-03-03 17:23 ` [PATCH 09/15] ath9k: Register larger listen interval Jouni Malinen
                   ` (7 subsequent siblings)
  15 siblings, 0 replies; 21+ messages in thread
From: Jouni Malinen @ 2009-03-03 17:23 UTC (permalink / raw)
  To: John W. Linville; +Cc: linux-wireless, Jouni Malinen

Instead of always going through initialization/deinitialization steps,
do this only for the first/last wiphy to not break the other wiphys.

Signed-off-by: Jouni Malinen <jouni.malinen@atheros.com>

---
 drivers/net/wireless/ath9k/ath9k.h   |    2 ++
 drivers/net/wireless/ath9k/main.c    |   28 ++++++++++++++++++++++++++++
 drivers/net/wireless/ath9k/virtual.c |   19 +++++++++++++++++++
 3 files changed, 49 insertions(+)

--- wireless-testing.orig/drivers/net/wireless/ath9k/ath9k.h	2009-03-03 18:31:20.000000000 +0200
+++ wireless-testing/drivers/net/wireless/ath9k/ath9k.h	2009-03-03 18:31:30.000000000 +0200
@@ -627,6 +627,7 @@ struct ath_wiphy {
 	struct ath_softc *sc; /* shared for all virtual wiphys */
 	struct ieee80211_hw *hw;
 	enum ath_wiphy_state {
+		ATH_WIPHY_INACTIVE,
 		ATH_WIPHY_ACTIVE,
 		ATH_WIPHY_PAUSING,
 		ATH_WIPHY_PAUSED,
@@ -707,5 +708,6 @@ int ath9k_wiphy_pause(struct ath_wiphy *
 int ath9k_wiphy_unpause(struct ath_wiphy *aphy);
 int ath9k_wiphy_select(struct ath_wiphy *aphy);
 void ath9k_wiphy_chan_work(struct work_struct *work);
+bool ath9k_wiphy_started(struct ath_softc *sc);
 
 #endif /* ATH9K_H */
--- wireless-testing.orig/drivers/net/wireless/ath9k/main.c	2009-03-03 18:31:20.000000000 +0200
+++ wireless-testing/drivers/net/wireless/ath9k/main.c	2009-03-03 18:31:30.000000000 +0200
@@ -1963,6 +1963,27 @@ static int ath9k_start(struct ieee80211_
 
 	mutex_lock(&sc->mutex);
 
+	if (ath9k_wiphy_started(sc)) {
+		if (sc->chan_idx == curchan->hw_value) {
+			/*
+			 * Already on the operational channel, the new wiphy
+			 * can be marked active.
+			 */
+			aphy->state = ATH_WIPHY_ACTIVE;
+			ieee80211_wake_queues(hw);
+		} else {
+			/*
+			 * Another wiphy is on another channel, start the new
+			 * wiphy in paused state.
+			 */
+			aphy->state = ATH_WIPHY_PAUSED;
+			ieee80211_stop_queues(hw);
+		}
+		mutex_unlock(&sc->mutex);
+		return 0;
+	}
+	aphy->state = ATH_WIPHY_ACTIVE;
+
 	/* setup initial channel */
 
 	pos = curchan->hw_value;
@@ -2102,6 +2123,8 @@ static void ath9k_stop(struct ieee80211_
 	struct ath_wiphy *aphy = hw->priv;
 	struct ath_softc *sc = aphy->sc;
 
+	aphy->state = ATH_WIPHY_INACTIVE;
+
 	if (sc->sc_flags & SC_OP_INVALID) {
 		DPRINTF(sc, ATH_DBG_ANY, "Device not present\n");
 		return;
@@ -2111,6 +2134,11 @@ static void ath9k_stop(struct ieee80211_
 
 	ieee80211_stop_queues(hw);
 
+	if (ath9k_wiphy_started(sc)) {
+		mutex_unlock(&sc->mutex);
+		return; /* another wiphy still in use */
+	}
+
 	/* make sure h/w will not generate any interrupt
 	 * before setting the invalid flag. */
 	ath9k_hw_set_interrupts(sc->sc_ah, 0);
--- wireless-testing.orig/drivers/net/wireless/ath9k/virtual.c	2009-03-03 18:31:20.000000000 +0200
+++ wireless-testing/drivers/net/wireless/ath9k/virtual.c	2009-03-03 18:31:30.000000000 +0200
@@ -477,3 +477,22 @@ int ath9k_wiphy_select(struct ath_wiphy 
 
 	return 0;
 }
+
+bool ath9k_wiphy_started(struct ath_softc *sc)
+{
+	int i;
+	spin_lock_bh(&sc->wiphy_lock);
+	if (sc->pri_wiphy->state != ATH_WIPHY_INACTIVE) {
+		spin_unlock_bh(&sc->wiphy_lock);
+		return true;
+	}
+	for (i = 0; i < sc->num_sec_wiphy; i++) {
+		if (sc->sec_wiphy[i] &&
+		    sc->sec_wiphy[i]->state != ATH_WIPHY_INACTIVE) {
+			spin_unlock_bh(&sc->wiphy_lock);
+			return true;
+		}
+	}
+	spin_unlock_bh(&sc->wiphy_lock);
+	return false;
+}

-- 

-- 
Jouni Malinen                                            PGP id EFC895FA

^ permalink raw reply	[flat|nested] 21+ messages in thread

* [PATCH 09/15] ath9k: Register larger listen interval
  2009-03-03 17:23 [PATCH 00/15] ath9k: Virtual interfaces and radios Jouni Malinen
                   ` (7 preceding siblings ...)
  2009-03-03 17:23 ` [PATCH 08/15] ath9k: Make start/stop operations aware of " Jouni Malinen
@ 2009-03-03 17:23 ` Jouni Malinen
  2009-03-03 17:23 ` [PATCH 10/15] ath9k: Pause other virtual wiphys on channel change Jouni Malinen
                   ` (6 subsequent siblings)
  15 siblings, 0 replies; 21+ messages in thread
From: Jouni Malinen @ 2009-03-03 17:23 UTC (permalink / raw)
  To: John W. Linville; +Cc: linux-wireless, Jouni Malinen

Notify the AP that we may be sleeping longer to allow the AP power
save code to buffer larger number of frames for us when using virtual
wiphys.

Signed-off-by: Jouni Malinen <jouni.malinen@atheros.com>

---
 drivers/net/wireless/ath9k/main.c |    1 +
 1 file changed, 1 insertion(+)

--- wireless-testing.orig/drivers/net/wireless/ath9k/main.c	2009-03-03 18:31:30.000000000 +0200
+++ wireless-testing/drivers/net/wireless/ath9k/main.c	2009-03-03 18:31:43.000000000 +0200
@@ -1595,6 +1595,7 @@ void ath_set_hw_capab(struct ath_softc *
 	hw->queues = 4;
 	hw->max_rates = 4;
 	hw->channel_change_time = 5000;
+	hw->max_listen_interval = 10;
 	hw->max_rate_tries = ATH_11N_TXMAXTRY;
 	hw->sta_data_size = sizeof(struct ath_node);
 	hw->vif_data_size = sizeof(struct ath_vif);

-- 

-- 
Jouni Malinen                                            PGP id EFC895FA

^ permalink raw reply	[flat|nested] 21+ messages in thread

* [PATCH 10/15] ath9k: Pause other virtual wiphys on channel change
  2009-03-03 17:23 [PATCH 00/15] ath9k: Virtual interfaces and radios Jouni Malinen
                   ` (8 preceding siblings ...)
  2009-03-03 17:23 ` [PATCH 09/15] ath9k: Register larger listen interval Jouni Malinen
@ 2009-03-03 17:23 ` Jouni Malinen
  2009-03-03 17:23 ` [PATCH 11/15] ath9k: Check virtual wiphy state on tx() Jouni Malinen
                   ` (5 subsequent siblings)
  15 siblings, 0 replies; 21+ messages in thread
From: Jouni Malinen @ 2009-03-03 17:23 UTC (permalink / raw)
  To: John W. Linville; +Cc: linux-wireless, Jouni Malinen

For now, allow channel changes immediately and just force the other
virtual wiphys to paused state. This is needed to allow
mac80211-controlled scan to control channel changes.

Signed-off-by: Jouni Malinen <jouni.malinen@atheros.com>

---
 drivers/net/wireless/ath9k/ath9k.h   |    2 ++
 drivers/net/wireless/ath9k/main.c    |    8 +++++++-
 drivers/net/wireless/ath9k/virtual.c |   24 ++++++++++++++++++++++++
 3 files changed, 33 insertions(+), 1 deletion(-)

--- wireless-testing.orig/drivers/net/wireless/ath9k/main.c	2009-03-03 18:31:43.000000000 +0200
+++ wireless-testing/drivers/net/wireless/ath9k/main.c	2009-03-03 18:31:50.000000000 +0200
@@ -2341,7 +2341,13 @@ static int ath9k_config(struct ieee80211
 		aphy->chan_is_ht = conf_is_ht(conf);
 
 		/* TODO: do not change operation channel immediately if there
-		 * are other virtual wiphys that use another channel */
+		 * are other virtual wiphys that use another channel. For now,
+		 * we do the change immediately to allow mac80211-operated scan
+		 * to work. Once the scan operation is moved into ath9k, we can
+		 * just move the current aphy in PAUSED state if the channel is
+		 * changed into something different from the current operation
+		 * channel. */
+		ath9k_wiphy_pause_all_forced(sc, aphy);
 
 		DPRINTF(sc, ATH_DBG_CONFIG, "Set channel: %d MHz\n",
 			curchan->center_freq);
--- wireless-testing.orig/drivers/net/wireless/ath9k/ath9k.h	2009-03-03 18:31:30.000000000 +0200
+++ wireless-testing/drivers/net/wireless/ath9k/ath9k.h	2009-03-03 18:31:50.000000000 +0200
@@ -709,5 +709,7 @@ int ath9k_wiphy_unpause(struct ath_wiphy
 int ath9k_wiphy_select(struct ath_wiphy *aphy);
 void ath9k_wiphy_chan_work(struct work_struct *work);
 bool ath9k_wiphy_started(struct ath_softc *sc);
+void ath9k_wiphy_pause_all_forced(struct ath_softc *sc,
+				  struct ath_wiphy *selected);
 
 #endif /* ATH9K_H */
--- wireless-testing.orig/drivers/net/wireless/ath9k/virtual.c	2009-03-03 18:31:30.000000000 +0200
+++ wireless-testing/drivers/net/wireless/ath9k/virtual.c	2009-03-03 18:31:50.000000000 +0200
@@ -496,3 +496,27 @@ bool ath9k_wiphy_started(struct ath_soft
 	spin_unlock_bh(&sc->wiphy_lock);
 	return false;
 }
+
+static void ath9k_wiphy_pause_chan(struct ath_wiphy *aphy,
+				   struct ath_wiphy *selected)
+{
+	if (aphy->chan_idx == selected->chan_idx)
+		return;
+	aphy->state = ATH_WIPHY_PAUSED;
+	ieee80211_stop_queues(aphy->hw);
+}
+
+void ath9k_wiphy_pause_all_forced(struct ath_softc *sc,
+				  struct ath_wiphy *selected)
+{
+	int i;
+	spin_lock_bh(&sc->wiphy_lock);
+	if (sc->pri_wiphy->state == ATH_WIPHY_ACTIVE)
+		ath9k_wiphy_pause_chan(sc->pri_wiphy, selected);
+	for (i = 0; i < sc->num_sec_wiphy; i++) {
+		if (sc->sec_wiphy[i] &&
+		    sc->sec_wiphy[i]->state == ATH_WIPHY_ACTIVE)
+			ath9k_wiphy_pause_chan(sc->sec_wiphy[i], selected);
+	}
+	spin_unlock_bh(&sc->wiphy_lock);
+}

-- 

-- 
Jouni Malinen                                            PGP id EFC895FA

^ permalink raw reply	[flat|nested] 21+ messages in thread

* [PATCH 11/15] ath9k: Check virtual wiphy state on tx()
  2009-03-03 17:23 [PATCH 00/15] ath9k: Virtual interfaces and radios Jouni Malinen
                   ` (9 preceding siblings ...)
  2009-03-03 17:23 ` [PATCH 10/15] ath9k: Pause other virtual wiphys on channel change Jouni Malinen
@ 2009-03-03 17:23 ` Jouni Malinen
  2009-03-03 17:23 ` [PATCH 12/15] ath9k: Add workaround to recover from failed channel changes Jouni Malinen
                   ` (4 subsequent siblings)
  15 siblings, 0 replies; 21+ messages in thread
From: Jouni Malinen @ 2009-03-03 17:23 UTC (permalink / raw)
  To: John W. Linville; +Cc: linux-wireless, Jouni Malinen

mac80211 should not be requesting us to transmit frames on paused
wiphys since we stop the TX queues. Just in case, add debug code to
make sure we catch if this were to happen.

Signed-off-by: Jouni Malinen <jouni.malinen@atheros.com>

---
 drivers/net/wireless/ath9k/main.c |    6 ++++++
 1 file changed, 6 insertions(+)

--- wireless-testing.orig/drivers/net/wireless/ath9k/main.c	2009-03-03 18:31:50.000000000 +0200
+++ wireless-testing/drivers/net/wireless/ath9k/main.c	2009-03-03 18:32:00.000000000 +0200
@@ -2075,6 +2075,12 @@ static int ath9k_tx(struct ieee80211_hw 
 	struct ath_tx_control txctl;
 	int hdrlen, padsize;
 
+	if (aphy->state != ATH_WIPHY_ACTIVE) {
+		printk(KERN_DEBUG "ath9k: %s: TX in unexpected wiphy state "
+		       "%d\n", wiphy_name(hw->wiphy), aphy->state);
+		goto exit;
+	}
+
 	memset(&txctl, 0, sizeof(struct ath_tx_control));
 
 	/*

-- 

-- 
Jouni Malinen                                            PGP id EFC895FA

^ permalink raw reply	[flat|nested] 21+ messages in thread

* [PATCH 12/15] ath9k: Add workaround to recover from failed channel changes
  2009-03-03 17:23 [PATCH 00/15] ath9k: Virtual interfaces and radios Jouni Malinen
                   ` (10 preceding siblings ...)
  2009-03-03 17:23 ` [PATCH 11/15] ath9k: Check virtual wiphy state on tx() Jouni Malinen
@ 2009-03-03 17:23 ` Jouni Malinen
  2009-03-03 17:23 ` [PATCH 13/15] ath9k: Special processing for channel changes during scan Jouni Malinen
                   ` (3 subsequent siblings)
  15 siblings, 0 replies; 21+ messages in thread
From: Jouni Malinen @ 2009-03-03 17:23 UTC (permalink / raw)
  To: John W. Linville; +Cc: linux-wireless, Jouni Malinen

It looks like channel change may fail in some cases and end up leaving
the hardware in state where it cannot transmit any frames. Add a
workaround to recover from this state if we detect that wiphy
selection is failing due to wiphys not leaving PAUSING state.

Signed-off-by: Jouni Malinen <jouni.malinen@atheros.com>

---
 drivers/net/wireless/ath9k/ath9k.h   |    4 +++
 drivers/net/wireless/ath9k/main.c    |    4 +--
 drivers/net/wireless/ath9k/virtual.c |   37 +++++++++++++++++++++++++++++++++++
 3 files changed, 43 insertions(+), 2 deletions(-)

--- wireless-testing.orig/drivers/net/wireless/ath9k/ath9k.h	2009-03-03 18:31:50.000000000 +0200
+++ wireless-testing/drivers/net/wireless/ath9k/ath9k.h	2009-03-03 18:32:06.000000000 +0200
@@ -567,6 +567,8 @@ struct ath_softc {
 	int chan_is_ht;
 	struct ath_wiphy *next_wiphy;
 	struct work_struct chan_work;
+	int wiphy_select_failures;
+	unsigned long wiphy_select_first_fail;
 
 	struct tasklet_struct intr_tq;
 	struct tasklet_struct bcon_tasklet;
@@ -665,6 +667,8 @@ void ath9k_update_ichannel(struct ath_so
 void ath_update_chainmask(struct ath_softc *sc, int is_ht);
 int ath_set_channel(struct ath_softc *sc, struct ieee80211_hw *hw,
 		    struct ath9k_channel *hchan);
+void ath_radio_enable(struct ath_softc *sc);
+void ath_radio_disable(struct ath_softc *sc);
 
 #ifdef CONFIG_PCI
 int ath_pci_init(void);
--- wireless-testing.orig/drivers/net/wireless/ath9k/virtual.c	2009-03-03 18:31:50.000000000 +0200
+++ wireless-testing/drivers/net/wireless/ath9k/virtual.c	2009-03-03 18:32:06.000000000 +0200
@@ -432,6 +432,18 @@ int ath9k_wiphy_unpause(struct ath_wiphy
 	return ret;
 }
 
+static void __ath9k_wiphy_mark_all_paused(struct ath_softc *sc)
+{
+	int i;
+	if (sc->pri_wiphy->state != ATH_WIPHY_INACTIVE)
+		sc->pri_wiphy->state = ATH_WIPHY_PAUSED;
+	for (i = 0; i < sc->num_sec_wiphy; i++) {
+		if (sc->sec_wiphy[i] &&
+		    sc->sec_wiphy[i]->state != ATH_WIPHY_INACTIVE)
+			sc->sec_wiphy[i]->state = ATH_WIPHY_PAUSED;
+	}
+}
+
 /* caller must hold wiphy_lock */
 static void __ath9k_wiphy_pause_all(struct ath_softc *sc)
 {
@@ -452,9 +464,34 @@ int ath9k_wiphy_select(struct ath_wiphy 
 
 	spin_lock_bh(&sc->wiphy_lock);
 	if (__ath9k_wiphy_pausing(sc)) {
+		if (sc->wiphy_select_failures == 0)
+			sc->wiphy_select_first_fail = jiffies;
+		sc->wiphy_select_failures++;
+		if (time_after(jiffies, sc->wiphy_select_first_fail + HZ / 2))
+		{
+			printk(KERN_DEBUG "ath9k: Previous wiphy select timed "
+			       "out; disable/enable hw to recover\n");
+			__ath9k_wiphy_mark_all_paused(sc);
+			/*
+			 * TODO: this workaround to fix hardware is unlikely to
+			 * be specific to virtual wiphy changes. It can happen
+			 * on normal channel change, too, and as such, this
+			 * should really be made more generic. For example,
+			 * tricker radio disable/enable on GTT interrupt burst
+			 * (say, 10 GTT interrupts received without any TX
+			 * frame being completed)
+			 */
+			spin_unlock_bh(&sc->wiphy_lock);
+			ath_radio_disable(sc);
+			ath_radio_enable(sc);
+			queue_work(aphy->sc->hw->workqueue,
+				   &aphy->sc->chan_work);
+			return -EBUSY; /* previous select still in progress */
+		}
 		spin_unlock_bh(&sc->wiphy_lock);
 		return -EBUSY; /* previous select still in progress */
 	}
+	sc->wiphy_select_failures = 0;
 
 	/* Store the new channel */
 	sc->chan_idx = aphy->chan_idx;
--- wireless-testing.orig/drivers/net/wireless/ath9k/main.c	2009-03-03 18:32:00.000000000 +0200
+++ wireless-testing/drivers/net/wireless/ath9k/main.c	2009-03-03 18:32:06.000000000 +0200
@@ -1088,7 +1088,7 @@ fail:
 /*	Rfkill	   */
 /*******************/
 
-static void ath_radio_enable(struct ath_softc *sc)
+void ath_radio_enable(struct ath_softc *sc)
 {
 	struct ath_hw *ah = sc->sc_ah;
 	struct ieee80211_channel *channel = sc->hw->conf.channel;
@@ -1129,7 +1129,7 @@ static void ath_radio_enable(struct ath_
 	ath9k_ps_restore(sc);
 }
 
-static void ath_radio_disable(struct ath_softc *sc)
+void ath_radio_disable(struct ath_softc *sc)
 {
 	struct ath_hw *ah = sc->sc_ah;
 	struct ieee80211_channel *channel = sc->hw->conf.channel;

-- 

-- 
Jouni Malinen                                            PGP id EFC895FA

^ permalink raw reply	[flat|nested] 21+ messages in thread

* [PATCH 13/15] ath9k: Special processing for channel changes during scan
  2009-03-03 17:23 [PATCH 00/15] ath9k: Virtual interfaces and radios Jouni Malinen
                   ` (11 preceding siblings ...)
  2009-03-03 17:23 ` [PATCH 12/15] ath9k: Add workaround to recover from failed channel changes Jouni Malinen
@ 2009-03-03 17:23 ` Jouni Malinen
  2009-03-03 17:23 ` [PATCH 14/15] ath9k: Add a simple virtual wiphy scheduler Jouni Malinen
                   ` (2 subsequent siblings)
  15 siblings, 0 replies; 21+ messages in thread
From: Jouni Malinen @ 2009-03-03 17:23 UTC (permalink / raw)
  To: John W. Linville; +Cc: linux-wireless, Jouni Malinen

Allow mac80211-controlled channel changes on an active wiphy and
especially during a scan. We need this as long as the scan is
controlled by mac80211. Moving this control into the driver could
allow some optimizations on scanning while using multiple virtual
interfaces, but for now, try to work as well as possible with the
current scan mechanism.

Signed-off-by: Jouni Malinen <jouni.malinen@atheros.com>

---
 drivers/net/wireless/ath9k/ath9k.h   |    2 +
 drivers/net/wireless/ath9k/main.c    |   35 ++++++++++++++++++++++-------
 drivers/net/wireless/ath9k/virtual.c |   41 ++++++++++++++++++++++++++++++++++-
 3 files changed, 68 insertions(+), 10 deletions(-)

--- wireless-testing.orig/drivers/net/wireless/ath9k/ath9k.h	2009-03-03 18:32:06.000000000 +0200
+++ wireless-testing/drivers/net/wireless/ath9k/ath9k.h	2009-03-03 18:32:41.000000000 +0200
@@ -633,6 +633,7 @@ struct ath_wiphy {
 		ATH_WIPHY_ACTIVE,
 		ATH_WIPHY_PAUSING,
 		ATH_WIPHY_PAUSED,
+		ATH_WIPHY_SCAN,
 	} state;
 	int chan_idx;
 	int chan_is_ht;
@@ -715,5 +716,6 @@ void ath9k_wiphy_chan_work(struct work_s
 bool ath9k_wiphy_started(struct ath_softc *sc);
 void ath9k_wiphy_pause_all_forced(struct ath_softc *sc,
 				  struct ath_wiphy *selected);
+bool ath9k_wiphy_scanning(struct ath_softc *sc);
 
 #endif /* ATH9K_H */
--- wireless-testing.orig/drivers/net/wireless/ath9k/main.c	2009-03-03 18:32:06.000000000 +0200
+++ wireless-testing/drivers/net/wireless/ath9k/main.c	2009-03-03 18:32:41.000000000 +0200
@@ -2075,7 +2075,7 @@ static int ath9k_tx(struct ieee80211_hw 
 	struct ath_tx_control txctl;
 	int hdrlen, padsize;
 
-	if (aphy->state != ATH_WIPHY_ACTIVE) {
+	if (aphy->state != ATH_WIPHY_ACTIVE && aphy->state != ATH_WIPHY_SCAN) {
 		printk(KERN_DEBUG "ath9k: %s: TX in unexpected wiphy state "
 		       "%d\n", wiphy_name(hw->wiphy), aphy->state);
 		goto exit;
@@ -2346,14 +2346,16 @@ static int ath9k_config(struct ieee80211
 		aphy->chan_idx = pos;
 		aphy->chan_is_ht = conf_is_ht(conf);
 
-		/* TODO: do not change operation channel immediately if there
-		 * are other virtual wiphys that use another channel. For now,
-		 * we do the change immediately to allow mac80211-operated scan
-		 * to work. Once the scan operation is moved into ath9k, we can
-		 * just move the current aphy in PAUSED state if the channel is
-		 * changed into something different from the current operation
-		 * channel. */
-		ath9k_wiphy_pause_all_forced(sc, aphy);
+		if (aphy->state == ATH_WIPHY_SCAN ||
+		    aphy->state == ATH_WIPHY_ACTIVE)
+			ath9k_wiphy_pause_all_forced(sc, aphy);
+		else {
+			/*
+			 * Do not change operational channel based on a paused
+			 * wiphy changes.
+			 */
+			goto skip_chan_change;
+		}
 
 		DPRINTF(sc, ATH_DBG_CONFIG, "Set channel: %d MHz\n",
 			curchan->center_freq);
@@ -2370,6 +2372,7 @@ static int ath9k_config(struct ieee80211
 		}
 	}
 
+skip_chan_change:
 	if (changed & IEEE80211_CONF_CHANGE_POWER)
 		sc->config.txpowlimit = 2 * conf->power_level;
 
@@ -2737,6 +2740,19 @@ static void ath9k_sw_scan_start(struct i
 	struct ath_wiphy *aphy = hw->priv;
 	struct ath_softc *sc = aphy->sc;
 
+	if (ath9k_wiphy_scanning(sc)) {
+		printk(KERN_DEBUG "ath9k: Two wiphys trying to scan at the "
+		       "same time\n");
+		/*
+		 * Do not allow the concurrent scanning state for now. This
+		 * could be improved with scanning control moved into ath9k.
+		 */
+		return;
+	}
+
+	aphy->state = ATH_WIPHY_SCAN;
+	ath9k_wiphy_pause_all_forced(sc, aphy);
+
 	mutex_lock(&sc->mutex);
 	sc->sc_flags |= SC_OP_SCANNING;
 	mutex_unlock(&sc->mutex);
@@ -2748,6 +2764,7 @@ static void ath9k_sw_scan_complete(struc
 	struct ath_softc *sc = aphy->sc;
 
 	mutex_lock(&sc->mutex);
+	aphy->state = ATH_WIPHY_ACTIVE;
 	sc->sc_flags &= ~SC_OP_SCANNING;
 	mutex_unlock(&sc->mutex);
 }
--- wireless-testing.orig/drivers/net/wireless/ath9k/virtual.c	2009-03-03 18:32:06.000000000 +0200
+++ wireless-testing/drivers/net/wireless/ath9k/virtual.c	2009-03-03 18:32:41.000000000 +0200
@@ -244,6 +244,28 @@ static bool ath9k_wiphy_pausing(struct a
 	return ret;
 }
 
+static bool __ath9k_wiphy_scanning(struct ath_softc *sc)
+{
+	int i;
+	if (sc->pri_wiphy->state == ATH_WIPHY_SCAN)
+		return true;
+	for (i = 0; i < sc->num_sec_wiphy; i++) {
+		if (sc->sec_wiphy[i] &&
+		    sc->sec_wiphy[i]->state == ATH_WIPHY_SCAN)
+			return true;
+	}
+	return false;
+}
+
+bool ath9k_wiphy_scanning(struct ath_softc *sc)
+{
+	bool ret;
+	spin_lock_bh(&sc->wiphy_lock);
+	ret = __ath9k_wiphy_scanning(sc);
+	spin_unlock_bh(&sc->wiphy_lock);
+	return ret;
+}
+
 static int __ath9k_wiphy_unpause(struct ath_wiphy *aphy);
 
 /* caller must hold wiphy_lock */
@@ -463,6 +485,16 @@ int ath9k_wiphy_select(struct ath_wiphy 
 	bool now;
 
 	spin_lock_bh(&sc->wiphy_lock);
+	if (__ath9k_wiphy_scanning(sc)) {
+		/*
+		 * For now, we are using mac80211 sw scan and it expects to
+		 * have full control over channel changes, so avoid wiphy
+		 * scheduling during a scan. This could be optimized if the
+		 * scanning control were moved into the driver.
+		 */
+		spin_unlock_bh(&sc->wiphy_lock);
+		return -EBUSY;
+	}
 	if (__ath9k_wiphy_pausing(sc)) {
 		if (sc->wiphy_select_failures == 0)
 			sc->wiphy_select_first_fail = jiffies;
@@ -537,7 +569,14 @@ bool ath9k_wiphy_started(struct ath_soft
 static void ath9k_wiphy_pause_chan(struct ath_wiphy *aphy,
 				   struct ath_wiphy *selected)
 {
-	if (aphy->chan_idx == selected->chan_idx)
+	if (selected->state == ATH_WIPHY_SCAN) {
+		if (aphy == selected)
+			return;
+		/*
+		 * Pause all other wiphys for the duration of the scan even if
+		 * they are on the current channel now.
+		 */
+	} else if (aphy->chan_idx == selected->chan_idx)
 		return;
 	aphy->state = ATH_WIPHY_PAUSED;
 	ieee80211_stop_queues(aphy->hw);

-- 

-- 
Jouni Malinen                                            PGP id EFC895FA

^ permalink raw reply	[flat|nested] 21+ messages in thread

* [PATCH 14/15] ath9k: Add a simple virtual wiphy scheduler
  2009-03-03 17:23 [PATCH 00/15] ath9k: Virtual interfaces and radios Jouni Malinen
                   ` (12 preceding siblings ...)
  2009-03-03 17:23 ` [PATCH 13/15] ath9k: Special processing for channel changes during scan Jouni Malinen
@ 2009-03-03 17:23 ` Jouni Malinen
  2009-03-03 17:23 ` [PATCH 15/15] ath9k: Add a debugfs interface for controlling virtual wiphys Jouni Malinen
  2009-03-07 13:56 ` [PATCH 00/15] ath9k: Virtual interfaces and radios Florian Fainelli
  15 siblings, 0 replies; 21+ messages in thread
From: Jouni Malinen @ 2009-03-03 17:23 UTC (permalink / raw)
  To: John W. Linville; +Cc: linux-wireless, Jouni Malinen

This is a very simple scheduler that goes through the wiphys and
schedules one at a time every N milliseconds (current default value:
500 ms). This is enough for initial testing, but there are number of
areas where a more complex scheduler can improve operations greatly.

Signed-off-by: Jouni Malinen <jouni.malinen@atheros.com>

---
 drivers/net/wireless/ath9k/ath9k.h   |    5 ++
 drivers/net/wireless/ath9k/main.c    |    3 +
 drivers/net/wireless/ath9k/virtual.c |   64 +++++++++++++++++++++++++++++++++++
 3 files changed, 72 insertions(+)

--- wireless-testing.orig/drivers/net/wireless/ath9k/ath9k.h	2009-03-03 18:32:41.000000000 +0200
+++ wireless-testing/drivers/net/wireless/ath9k/ath9k.h	2009-03-03 18:32:52.000000000 +0200
@@ -569,6 +569,9 @@ struct ath_softc {
 	struct work_struct chan_work;
 	int wiphy_select_failures;
 	unsigned long wiphy_select_first_fail;
+	struct delayed_work wiphy_work;
+	unsigned long wiphy_scheduler_int;
+	int wiphy_scheduler_index;
 
 	struct tasklet_struct intr_tq;
 	struct tasklet_struct bcon_tasklet;
@@ -712,10 +715,12 @@ void ath9k_tx_status(struct ieee80211_hw
 int ath9k_wiphy_pause(struct ath_wiphy *aphy);
 int ath9k_wiphy_unpause(struct ath_wiphy *aphy);
 int ath9k_wiphy_select(struct ath_wiphy *aphy);
+void ath9k_wiphy_set_scheduler(struct ath_softc *sc, unsigned int msec_int);
 void ath9k_wiphy_chan_work(struct work_struct *work);
 bool ath9k_wiphy_started(struct ath_softc *sc);
 void ath9k_wiphy_pause_all_forced(struct ath_softc *sc,
 				  struct ath_wiphy *selected);
 bool ath9k_wiphy_scanning(struct ath_softc *sc);
+void ath9k_wiphy_work(struct work_struct *work);
 
 #endif /* ATH9K_H */
--- wireless-testing.orig/drivers/net/wireless/ath9k/main.c	2009-03-03 18:32:41.000000000 +0200
+++ wireless-testing/drivers/net/wireless/ath9k/main.c	2009-03-03 18:32:52.000000000 +0200
@@ -1323,6 +1323,7 @@ void ath_detach(struct ath_softc *sc)
 #endif
 	ath_deinit_leds(sc);
 	cancel_work_sync(&sc->chan_work);
+	cancel_delayed_work_sync(&sc->wiphy_work);
 
 	for (i = 0; i < sc->num_sec_wiphy; i++) {
 		struct ath_wiphy *aphy = sc->sec_wiphy[i];
@@ -1670,6 +1671,8 @@ int ath_attach(u16 devid, struct ath_sof
 	ath9k_reg_apply_world_flags(hw->wiphy, REGDOM_SET_BY_INIT);
 
 	INIT_WORK(&sc->chan_work, ath9k_wiphy_chan_work);
+	INIT_DELAYED_WORK(&sc->wiphy_work, ath9k_wiphy_work);
+	sc->wiphy_scheduler_int = msecs_to_jiffies(500);
 
 	error = ieee80211_register_hw(hw);
 
--- wireless-testing.orig/drivers/net/wireless/ath9k/virtual.c	2009-03-03 18:32:41.000000000 +0200
+++ wireless-testing/drivers/net/wireless/ath9k/virtual.c	2009-03-03 18:32:52.000000000 +0200
@@ -154,6 +154,11 @@ int ath9k_wiphy_add(struct ath_softc *sc
 
 	error = ieee80211_register_hw(hw);
 
+	if (error == 0) {
+		/* Make sure wiphy scheduler is started (if enabled) */
+		ath9k_wiphy_set_scheduler(sc, sc->wiphy_scheduler_int);
+	}
+
 	return error;
 }
 
@@ -596,3 +601,62 @@ void ath9k_wiphy_pause_all_forced(struct
 	}
 	spin_unlock_bh(&sc->wiphy_lock);
 }
+
+void ath9k_wiphy_work(struct work_struct *work)
+{
+	struct ath_softc *sc = container_of(work, struct ath_softc,
+					    wiphy_work.work);
+	struct ath_wiphy *aphy = NULL;
+	bool first = true;
+
+	spin_lock_bh(&sc->wiphy_lock);
+
+	if (sc->wiphy_scheduler_int == 0) {
+		/* wiphy scheduler is disabled */
+		spin_unlock_bh(&sc->wiphy_lock);
+		return;
+	}
+
+try_again:
+	sc->wiphy_scheduler_index++;
+	while (sc->wiphy_scheduler_index <= sc->num_sec_wiphy) {
+		aphy = sc->sec_wiphy[sc->wiphy_scheduler_index - 1];
+		if (aphy && aphy->state != ATH_WIPHY_INACTIVE)
+			break;
+
+		sc->wiphy_scheduler_index++;
+		aphy = NULL;
+	}
+	if (aphy == NULL) {
+		sc->wiphy_scheduler_index = 0;
+		if (sc->pri_wiphy->state == ATH_WIPHY_INACTIVE) {
+			if (first) {
+				first = false;
+				goto try_again;
+			}
+			/* No wiphy is ready to be scheduled */
+		} else
+			aphy = sc->pri_wiphy;
+	}
+
+	spin_unlock_bh(&sc->wiphy_lock);
+
+	if (aphy &&
+	    aphy->state != ATH_WIPHY_ACTIVE && aphy->state != ATH_WIPHY_SCAN &&
+	    ath9k_wiphy_select(aphy)) {
+		printk(KERN_DEBUG "ath9k: Failed to schedule virtual wiphy "
+		       "change\n");
+	}
+
+	queue_delayed_work(sc->hw->workqueue, &sc->wiphy_work,
+			   sc->wiphy_scheduler_int);
+}
+
+void ath9k_wiphy_set_scheduler(struct ath_softc *sc, unsigned int msec_int)
+{
+	cancel_delayed_work_sync(&sc->wiphy_work);
+	sc->wiphy_scheduler_int = msecs_to_jiffies(msec_int);
+	if (sc->wiphy_scheduler_int)
+		queue_delayed_work(sc->hw->workqueue, &sc->wiphy_work,
+				   sc->wiphy_scheduler_int);
+}

-- 

-- 
Jouni Malinen                                            PGP id EFC895FA

^ permalink raw reply	[flat|nested] 21+ messages in thread

* [PATCH 15/15] ath9k: Add a debugfs interface for controlling virtual wiphys
  2009-03-03 17:23 [PATCH 00/15] ath9k: Virtual interfaces and radios Jouni Malinen
                   ` (13 preceding siblings ...)
  2009-03-03 17:23 ` [PATCH 14/15] ath9k: Add a simple virtual wiphy scheduler Jouni Malinen
@ 2009-03-03 17:23 ` Jouni Malinen
  2009-03-07 13:56 ` [PATCH 00/15] ath9k: Virtual interfaces and radios Florian Fainelli
  15 siblings, 0 replies; 21+ messages in thread
From: Jouni Malinen @ 2009-03-03 17:23 UTC (permalink / raw)
  To: John W. Linville; +Cc: linux-wireless, Jouni Malinen

debugfs ath9k/phy#/wiphy can be used to show the current list of
virtual wiphys and to add/remove virtual wiphys. Eventually, this
interface could be replaced with a cfg80211/nl80211 command that is
passed through mac80211.

For example:
# cat /debug/ath9k/phy0/wiphy 
primary: phy0
# echo add > /debug/ath9k/phy0/wiphy 
# cat /debug/ath9k/phy0/wiphy 
primary: phy0
secondary: phy1
# echo del=phy1 > /debug/ath9k/phy0/wiphy 
# cat /debug/ath9k/phy0/wiphy 
primary: phy0

In addition, following commands can be used to test pausing and
unpausing of the virtual wiphys:
pause=phy1
unpause=phy1
select=phy1
(select pauses and unpauses wiphys automatically based on channel)
schedule=500
(set wiphy scheduling interval in msec; 0 = disable; default value: 500)

Signed-off-by: Jouni Malinen <jouni.malinen@atheros.com>

---
 drivers/net/wireless/ath9k/debug.c |  164 +++++++++++++++++++++++++++++++++++++
 drivers/net/wireless/ath9k/debug.h |    1 
 2 files changed, 165 insertions(+)

--- wireless-testing.orig/drivers/net/wireless/ath9k/debug.c	2009-02-27 10:52:58.000000000 +0200
+++ wireless-testing/drivers/net/wireless/ath9k/debug.c	2009-03-03 18:33:02.000000000 +0200
@@ -330,6 +330,163 @@ static const struct file_operations fops
 	.owner = THIS_MODULE
 };
 
+static const char * ath_wiphy_state_str(enum ath_wiphy_state state)
+{
+	switch (state) {
+	case ATH_WIPHY_INACTIVE:
+		return "INACTIVE";
+	case ATH_WIPHY_ACTIVE:
+		return "ACTIVE";
+	case ATH_WIPHY_PAUSING:
+		return "PAUSING";
+	case ATH_WIPHY_PAUSED:
+		return "PAUSED";
+	case ATH_WIPHY_SCAN:
+		return "SCAN";
+	}
+	return "?";
+}
+
+static ssize_t read_file_wiphy(struct file *file, char __user *user_buf,
+			       size_t count, loff_t *ppos)
+{
+	struct ath_softc *sc = file->private_data;
+	char buf[512];
+	unsigned int len = 0;
+	int i;
+	u8 addr[ETH_ALEN];
+
+	len += snprintf(buf + len, sizeof(buf) - len,
+			"primary: %s (%s chan=%d ht=%d)\n",
+			wiphy_name(sc->pri_wiphy->hw->wiphy),
+			ath_wiphy_state_str(sc->pri_wiphy->state),
+			sc->pri_wiphy->chan_idx, sc->pri_wiphy->chan_is_ht);
+	for (i = 0; i < sc->num_sec_wiphy; i++) {
+		struct ath_wiphy *aphy = sc->sec_wiphy[i];
+		if (aphy == NULL)
+			continue;
+		len += snprintf(buf + len, sizeof(buf) - len,
+				"secondary: %s (%s chan=%d ht=%d)\n",
+				wiphy_name(aphy->hw->wiphy),
+				ath_wiphy_state_str(aphy->state),
+				aphy->chan_idx, aphy->chan_is_ht);
+	}
+
+	put_unaligned_le32(REG_READ(sc->sc_ah, AR_STA_ID0), addr);
+	put_unaligned_le16(REG_READ(sc->sc_ah, AR_STA_ID1) & 0xffff, addr + 4);
+	len += snprintf(buf + len, sizeof(buf) - len,
+			"addr: %pM\n", addr);
+	put_unaligned_le32(REG_READ(sc->sc_ah, AR_BSSMSKL), addr);
+	put_unaligned_le16(REG_READ(sc->sc_ah, AR_BSSMSKU) & 0xffff, addr + 4);
+	len += snprintf(buf + len, sizeof(buf) - len,
+			"addrmask: %pM\n", addr);
+
+	return simple_read_from_buffer(user_buf, count, ppos, buf, len);
+}
+
+static struct ath_wiphy * get_wiphy(struct ath_softc *sc, const char *name)
+{
+	int i;
+	if (strcmp(name, wiphy_name(sc->pri_wiphy->hw->wiphy)) == 0)
+		return sc->pri_wiphy;
+	for (i = 0; i < sc->num_sec_wiphy; i++) {
+		struct ath_wiphy *aphy = sc->sec_wiphy[i];
+		if (aphy && strcmp(name, wiphy_name(aphy->hw->wiphy)) == 0)
+			return aphy;
+	}
+	return NULL;
+}
+
+static int del_wiphy(struct ath_softc *sc, const char *name)
+{
+	struct ath_wiphy *aphy = get_wiphy(sc, name);
+	if (!aphy)
+		return -ENOENT;
+	return ath9k_wiphy_del(aphy);
+}
+
+static int pause_wiphy(struct ath_softc *sc, const char *name)
+{
+	struct ath_wiphy *aphy = get_wiphy(sc, name);
+	if (!aphy)
+		return -ENOENT;
+	return ath9k_wiphy_pause(aphy);
+}
+
+static int unpause_wiphy(struct ath_softc *sc, const char *name)
+{
+	struct ath_wiphy *aphy = get_wiphy(sc, name);
+	if (!aphy)
+		return -ENOENT;
+	return ath9k_wiphy_unpause(aphy);
+}
+
+static int select_wiphy(struct ath_softc *sc, const char *name)
+{
+	struct ath_wiphy *aphy = get_wiphy(sc, name);
+	if (!aphy)
+		return -ENOENT;
+	return ath9k_wiphy_select(aphy);
+}
+
+static int schedule_wiphy(struct ath_softc *sc, const char *msec)
+{
+	ath9k_wiphy_set_scheduler(sc, simple_strtoul(msec, NULL, 0));
+	return 0;
+}
+
+static ssize_t write_file_wiphy(struct file *file, const char __user *user_buf,
+				size_t count, loff_t *ppos)
+{
+	struct ath_softc *sc = file->private_data;
+	char buf[50];
+	size_t len;
+
+	len = min(count, sizeof(buf) - 1);
+	if (copy_from_user(buf, user_buf, len))
+		return -EFAULT;
+	buf[len] = '\0';
+	if (len > 0 && buf[len - 1] == '\n')
+		buf[len - 1] = '\0';
+
+	if (strncmp(buf, "add", 3) == 0) {
+		int res = ath9k_wiphy_add(sc);
+		if (res < 0)
+			return res;
+	} else if (strncmp(buf, "del=", 4) == 0) {
+		int res = del_wiphy(sc, buf + 4);
+		if (res < 0)
+			return res;
+	} else if (strncmp(buf, "pause=", 6) == 0) {
+		int res = pause_wiphy(sc, buf + 6);
+		if (res < 0)
+			return res;
+	} else if (strncmp(buf, "unpause=", 8) == 0) {
+		int res = unpause_wiphy(sc, buf + 8);
+		if (res < 0)
+			return res;
+	} else if (strncmp(buf, "select=", 7) == 0) {
+		int res = select_wiphy(sc, buf + 7);
+		if (res < 0)
+			return res;
+	} else if (strncmp(buf, "schedule=", 9) == 0) {
+		int res = schedule_wiphy(sc, buf + 9);
+		if (res < 0)
+			return res;
+	} else
+		return -EOPNOTSUPP;
+
+	return count;
+}
+
+static const struct file_operations fops_wiphy = {
+	.read = read_file_wiphy,
+	.write = write_file_wiphy,
+	.open = ath9k_debugfs_open,
+	.owner = THIS_MODULE
+};
+
+
 int ath9k_init_debug(struct ath_softc *sc)
 {
 	sc->debug.debug_mask = ath9k_debug;
@@ -362,6 +519,12 @@ int ath9k_init_debug(struct ath_softc *s
 	if (!sc->debug.debugfs_rcstat)
 		goto err;
 
+	sc->debug.debugfs_wiphy = debugfs_create_file(
+		"wiphy", S_IRUGO | S_IWUSR, sc->debug.debugfs_phy, sc,
+		&fops_wiphy);
+	if (!sc->debug.debugfs_wiphy)
+		goto err;
+
 	return 0;
 err:
 	ath9k_exit_debug(sc);
@@ -370,6 +533,7 @@ err:
 
 void ath9k_exit_debug(struct ath_softc *sc)
 {
+	debugfs_remove(sc->debug.debugfs_wiphy);
 	debugfs_remove(sc->debug.debugfs_rcstat);
 	debugfs_remove(sc->debug.debugfs_interrupt);
 	debugfs_remove(sc->debug.debugfs_dma);
--- wireless-testing.orig/drivers/net/wireless/ath9k/debug.h	2009-02-27 10:52:58.000000000 +0200
+++ wireless-testing/drivers/net/wireless/ath9k/debug.h	2009-03-03 18:33:02.000000000 +0200
@@ -107,6 +107,7 @@ struct ath9k_debug {
 	struct dentry *debugfs_dma;
 	struct dentry *debugfs_interrupt;
 	struct dentry *debugfs_rcstat;
+	struct dentry *debugfs_wiphy;
 	struct ath_stats stats;
 };
 

-- 

-- 
Jouni Malinen                                            PGP id EFC895FA

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH 00/15] ath9k: Virtual interfaces and radios
  2009-03-03 17:23 [PATCH 00/15] ath9k: Virtual interfaces and radios Jouni Malinen
                   ` (14 preceding siblings ...)
  2009-03-03 17:23 ` [PATCH 15/15] ath9k: Add a debugfs interface for controlling virtual wiphys Jouni Malinen
@ 2009-03-07 13:56 ` Florian Fainelli
  2009-03-12 20:19   ` Jouni Malinen
  15 siblings, 1 reply; 21+ messages in thread
From: Florian Fainelli @ 2009-03-07 13:56 UTC (permalink / raw)
  To: Jouni Malinen; +Cc: John W. Linville, linux-wireless

Hello Jouni,

Le Tuesday 03 March 2009 18:23:25 Jouni Malinen, vous avez =C3=A9crit=C2=
=A0:
> When using virtual wiphys, the APs that the virtual interfaces are
> associated with do not need to be on the same channel. One of the
> wiphys could also be used with hostapd to bring up an AP. However, it
> should be noted that most client implementation do not exactly like
> the AP disappearing all the time, so using a virtual station interfac=
e
> on another channel at the same time may not be that good of an idea
> for most use cases now. This will hopefully improve somewhat with
> future optimizations.
>
>
> It would be useful to hear what others think about this sort of
> functionality and how this should be integrated with Linux, including
> the user space interface, applications managing the virtual interface
> functionality, and user interface that the end users would see. This
> is still quite an early step in the progress, but the goal here is to
> get this functionality into mature state and provide something that
> makes it easier to use wireless LAN in general for connecting devices
> in various scenarios. All feedback would be welcome!

I am only commenting from a user point of view this feature. This defin=
itively=20
is very useful since quite a lot of people stick to madwifi instead of =
using=20
ath5k or ath9k since there was not real support before this patch for v=
irtual=20
interfaces.

One thing that I did not like with wlanconfig and I would like not to f=
ind=20
with mac80211/cfg80211 is the incompatibility of some setups (i.e: AP s=
hould=20
be created first, STA interface later with nosbeacon), not sure this is=
=20
possible, but if it is, one should be able to add a virtual interface w=
ithout=20
knowing the state of the other interfaces before.

I really like the way we can create mesh point interfaces with iw, so t=
his=20
could be the user interface to configure virtual interfaces, say like t=
his :

iw dev wlan0 interface add sta0 type sta
iw dev wlan0 interface add ap0 type master

On a broader perspective, I really hope other drivers will become capab=
le of=20
supporting virtual interfaces as well.

Thank you for your work.
--=20
Best regards, Florian Fainelli
Email : florian@openwrt.org
http://openwrt.org
-------------------------------
--
To unsubscribe from this list: send the line "unsubscribe linux-wireles=
s" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH 00/15] ath9k: Virtual interfaces and radios
  2009-03-07 13:56 ` [PATCH 00/15] ath9k: Virtual interfaces and radios Florian Fainelli
@ 2009-03-12 20:19   ` Jouni Malinen
  0 siblings, 0 replies; 21+ messages in thread
From: Jouni Malinen @ 2009-03-12 20:19 UTC (permalink / raw)
  To: Florian Fainelli; +Cc: John W. Linville, linux-wireless

On Sat, 2009-03-07 at 05:56 -0800, Florian Fainelli wrote:
> I am only commenting from a user point of view this feature. This definitively
> is very useful since quite a lot of people stick to madwifi instead of using
> ath5k or ath9k since there was not real support before this patch for virtual
> interfaces.

The set of patches that went in should take care of the virtual station
side. The patch I just sent out will add multi-BSSID support for AP mode
(it will require an update to hostapd, too), so this should provide
support for quite complete set of virtual interface configurations.

> One thing that I did not like with wlanconfig and I would like not to find
> with mac80211/cfg80211 is the incompatibility of some setups (i.e: AP should
> be created first, STA interface later with nosbeacon), not sure this is
> possible, but if it is, one should be able to add a virtual interface without
> knowing the state of the other interfaces before.

The goal is certainly to be able to add and remove virtual interfaces in
any order and at any time. There may be some limitations in the current
version, i.e., something might end up not working, but the configuration
part itself is not prevented. If someone finds a sequence that does not
work, I would hope it can be resolved.

> I really like the way we can create mesh point interfaces with iw, so this
> could be the user interface to configure virtual interfaces, say like this :
> 
> iw dev wlan0 interface add sta0 type sta
> iw dev wlan0 interface add ap0 type master

The multi-channel operation with virtual radio (wiphy) is at somewhat
different layer than the mesh interface (vif). Anyway, if nl80211 is
extended with a command that can be used to add a new virtual wiphy,
similar iw command could be added for this use, too. Here standard
commands (iwconfig/wpa_supplicant/hostapd) can change the interface
mode, so it does not need to be specified at wiphy creation time.

As far as multi-BSSID configuration of AP mode on the same channel is
concerned, those virtual interfaces are vifs and hostapd generates them
dynamically using the same mechanism as the iw commands above.

- Jouni



^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH 00/15] ath9k: Virtual interfaces and radios
  2009-03-10 17:03 ` Jouni Malinen
@ 2009-03-10 17:23   ` Johannes Berg
  0 siblings, 0 replies; 21+ messages in thread
From: Johannes Berg @ 2009-03-10 17:23 UTC (permalink / raw)
  To: Jouni Malinen
  Cc: Stephen Gutknecht (hilltx), linux-wireless, ath9k-devel@lists.ath9k.org

[-- Attachment #1: Type: text/plain, Size: 529 bytes --]

On Tue, 2009-03-10 at 19:03 +0200, Jouni Malinen wrote:

> They went already in into wireless-testing git tree. I would certainly
> be interested in see test reports if people are doing some experiments
> with the new code. There are still number of areas that can benefit from
> optimizations and combinations that do not probably work yet, so it will
> be useful to know if these show up in a configuration that someone would
> like to use.

Speaking of which -- has anyone ever tested WDS with mac80211?

johannes

[-- Attachment #2: This is a digitally signed message part --]
[-- Type: application/pgp-signature, Size: 836 bytes --]

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH 00/15] ath9k: Virtual interfaces and radios
  2009-03-10 12:43 Stephen Gutknecht (hilltx)
@ 2009-03-10 17:03 ` Jouni Malinen
  2009-03-10 17:23   ` Johannes Berg
  0 siblings, 1 reply; 21+ messages in thread
From: Jouni Malinen @ 2009-03-10 17:03 UTC (permalink / raw)
  To: Stephen Gutknecht (hilltx); +Cc: linux-wireless, ath9k-devel@lists.ath9k.org

On Tue, Mar 10, 2009 at 05:43:31AM -0700, Stephen Gutknecht (hilltx) wrote:

> I agree with Florian Fainelli.  As a user of ath9k I would love to see
> these patches make it into the driver.  I've not seen technical
> complaints of your patches.  Time to rebase and get them included?  Or
> at least some testing reports?

They went already in into wireless-testing git tree. I would certainly
be interested in see test reports if people are doing some experiments
with the new code. There are still number of areas that can benefit from
optimizations and combinations that do not probably work yet, so it will
be useful to know if these show up in a configuration that someone would
like to use.

-- 
Jouni Malinen                                            PGP id EFC895FA

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH 00/15] ath9k: Virtual interfaces and radios
@ 2009-03-10 12:43 Stephen Gutknecht (hilltx)
  2009-03-10 17:03 ` Jouni Malinen
  0 siblings, 1 reply; 21+ messages in thread
From: Stephen Gutknecht (hilltx) @ 2009-03-10 12:43 UTC (permalink / raw)
  To: linux-wireless; +Cc: ath9k-devel

Howdy Jouni,

I agree with Florian Fainelli.  As a user of ath9k I would love to see
these patches make it into the driver.  I've not seen technical
complaints of your patches.  Time to rebase and get them included?  Or
at least some testing reports?

I'm sure OpenWrt users would love this.

^ permalink raw reply	[flat|nested] 21+ messages in thread

end of thread, other threads:[~2009-03-12 20:19 UTC | newest]

Thread overview: 21+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2009-03-03 17:23 [PATCH 00/15] ath9k: Virtual interfaces and radios Jouni Malinen
2009-03-03 17:23 ` [PATCH 01/15] ath9k: Cleanup multiple VIF processing Jouni Malinen
2009-03-03 17:23 ` [PATCH 02/15] ath9k: Set BSSID mask based on configured interfaces Jouni Malinen
2009-03-03 17:23 ` [PATCH 03/15] ath9k: Add data structure for supporting virtual radio/wiphy operation Jouni Malinen
2009-03-03 17:23 ` [PATCH 04/15] ath9k: Add support for multiple secondary virtual wiphys Jouni Malinen
2009-03-03 17:23 ` [PATCH 05/15] ath9k: Configure RX filter for multi-BSSID broadcast Jouni Malinen
2009-03-03 17:23 ` [PATCH 06/15] ath9k: Virtual wiphy pause/unpause functionality Jouni Malinen
2009-03-03 17:23 ` [PATCH 07/15] ath9k: Add routines for switching between active virtual wiphys Jouni Malinen
2009-03-03 17:23 ` [PATCH 08/15] ath9k: Make start/stop operations aware of " Jouni Malinen
2009-03-03 17:23 ` [PATCH 09/15] ath9k: Register larger listen interval Jouni Malinen
2009-03-03 17:23 ` [PATCH 10/15] ath9k: Pause other virtual wiphys on channel change Jouni Malinen
2009-03-03 17:23 ` [PATCH 11/15] ath9k: Check virtual wiphy state on tx() Jouni Malinen
2009-03-03 17:23 ` [PATCH 12/15] ath9k: Add workaround to recover from failed channel changes Jouni Malinen
2009-03-03 17:23 ` [PATCH 13/15] ath9k: Special processing for channel changes during scan Jouni Malinen
2009-03-03 17:23 ` [PATCH 14/15] ath9k: Add a simple virtual wiphy scheduler Jouni Malinen
2009-03-03 17:23 ` [PATCH 15/15] ath9k: Add a debugfs interface for controlling virtual wiphys Jouni Malinen
2009-03-07 13:56 ` [PATCH 00/15] ath9k: Virtual interfaces and radios Florian Fainelli
2009-03-12 20:19   ` Jouni Malinen
2009-03-10 12:43 Stephen Gutknecht (hilltx)
2009-03-10 17:03 ` Jouni Malinen
2009-03-10 17:23   ` Johannes Berg

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.