Skip site navigation (1)Skip section navigation (2)
Date:      Thu, 5 Apr 2007 16:10:53 GMT
From:      Sam Leffler <sam@FreeBSD.org>
To:        Perforce Change Reviews <perforce@freebsd.org>
Subject:   PERFORCE change 117438 for review
Message-ID:  <200704051610.l35GArlj039926@repoman.freebsd.org>

next in thread | raw e-mail | index | archive | help
http://perforce.freebsd.org/chv.cgi?CH=117438

Change 117438 by sam@sam_ebb on 2007/04/05 16:10:37

	o revise channel mapping to honor any media mode setting that
	  might constrain promotion
	o add support for 11n channels
	o add channel flags specification syntax so you can specify
	  which of several channels to use when ambiguous; syntax
	  needs review and documentation
	
	11n support temporarily #ifdef'd until kernel support committed.

Affected files ...

.. //depot/projects/wifi/sbin/ifconfig/ifieee80211.c#64 edit

Differences ...

==== //depot/projects/wifi/sbin/ifconfig/ifieee80211.c#64 (text+ko) ====

@@ -94,13 +94,36 @@
 
 #include "ifconfig.h"
 
+/* XXX temporary compatibility shims */
+#ifndef IEEE80211_CHAN_HT
+#define	IEEE80211_CHAN_HT20	0x10000	/* HT 20 channel */
+#define	IEEE80211_CHAN_HT40U	0x20000	/* HT 40 channel w/ ext above */
+#define	IEEE80211_CHAN_HT40D	0x40000	/* HT 40 channel w/ ext below */
+
+#define	IEEE80211_CHAN_HT40	(IEEE80211_CHAN_HT40U | IEEE80211_CHAN_HT40D)
+#define	IEEE80211_CHAN_HT	(IEEE80211_CHAN_HT20 | IEEE80211_CHAN_HT40)
+
+#define	IEEE80211_CHAN_HTA \
+	(IEEE80211_CHAN_A | IEEE80211_CHAN_HT)
+#define	IEEE80211_CHAN_HTG \
+	(IEEE80211_CHAN_G | IEEE80211_CHAN_HT)
+
+#define	IEEE80211_IS_CHAN_HT(_c) \
+	(((_c)->ic_flags & IEEE80211_CHAN_HT) != 0)
+#endif
+
 static void set80211(int s, int type, int val, int len, void *data);
 static const char *get_string(const char *val, const char *sep,
     u_int8_t *buf, int *lenp);
 static void print_string(const u_int8_t *buf, int len);
 
 static struct ieee80211req_chaninfo chaninfo;
+static struct ifmediareq *ifmr;
 
+/*
+ * Collect channel info from the kernel.  We use this (mostly)
+ * to handle mapping between frequency and IEEE channel number.
+ */
 static void
 getchaninfo(int s)
 {
@@ -115,45 +138,120 @@
 	ireq.i_len = sizeof(chaninfo);
 	if (ioctl(s, SIOCG80211, &ireq) < 0)
 		errx(1, "unable to get channel information");
+
+	ifmr = ifmedia_getstate(s);
+}
+
+/*
+ * Given the channel at index i with attributes from,
+ * check if there is a channel with attributes to in
+ * the channel table.  With suitable attributes this
+ * allows the caller to look for promotion; e.g. from
+ * 11b > 11g.
+ */
+static int
+canpromote(int i, int from, int to)
+{
+	const struct ieee80211_channel *fc = &chaninfo.ic_chans[i];
+	int j;
+
+	if ((fc->ic_flags & from) != from)
+		return 0;
+	/* NB: quick check exploiting ordering of chans w/ same frequency */
+	if (i+1 < chaninfo.ic_nchans &&
+	    chaninfo.ic_chans[i+1].ic_freq == fc->ic_freq &&
+	    (chaninfo.ic_chans[i+1].ic_flags & to) == to)
+		return 1;
+	/* brute force search in case channel list is not ordered */
+	for (j = 0; j < chaninfo.ic_nchans; j++) {
+		const struct ieee80211_channel *tc = &chaninfo.ic_chans[j];
+		if (j != i &&
+		    tc->ic_freq == fc->ic_freq && (tc->ic_flags & to) == to)
+		return 1;
+	}
+	return 0;
+}
+
+/*
+ * Handle channel promotion.  When a channel is specified with
+ * only a frequency we want to promote it to the ``best'' channel
+ * available.  The channel list has separate entries for 11b, 11g,
+ * 11a, and 11n[ga] channels so specifying a frequency w/o any
+ * attributes requires we upgrade, e.g. from 11b -> 11g.  This
+ * gets complicated when the channel is specified on the same
+ * command line with a media request that constrains the available
+ * channe list (e.g. mode 11a); we want to honor that to avoid
+ * confusing behaviour.
+ */
+static int
+promote(int i)
+{
+	/*
+	 * Query the current mode of the interface in case it's
+	 * constrained (e.g. to 11a).  We must do this carefully
+	 * as there may be a pending ifmedia request in which case
+	 * asking the kernel will give us the wrong answer.  This
+	 * is an unfortunate side-effect of the way ifconfig is
+	 * structure for modularity (yech).
+	 *
+	 * NB: ifmr is actually setup in getchaninfo (above); we
+	 *     assume it's called coincident with to this call so
+	 *     we have a ``current setting''; otherwise we must pass
+	 *     the socket descriptor down to here so we can make
+	 *     the ifmedia_getstate call ourselves.
+	 */
+	int chanmode = ifmr != NULL ? IFM_MODE(ifmr->ifm_current) : IFM_AUTO;
+
+	/* when ambiguous promote to ``best'' */
+	if (chanmode != IFM_IEEE80211_11B &&
+	    canpromote(i, IEEE80211_CHAN_B, IEEE80211_CHAN_G))
+		i++;
+	if (chanmode != IFM_IEEE80211_11G &&
+	    canpromote(i, IEEE80211_CHAN_G, IEEE80211_CHAN_HTG))
+		i++;
+	if (chanmode != IFM_IEEE80211_11A &&
+	    canpromote(i, IEEE80211_CHAN_A, IEEE80211_CHAN_HTA))
+		i++;
+	return i;
 }
 
 static void
-mapfreq(struct ieee80211_channel *c, int freq, int flags)
+mapfreq(struct ieee80211_channel *chan, int freq, int flags)
 {
 	int i;
 
-	for (i = 0; i < chaninfo.ic_nchans; i++)
-		if (chaninfo.ic_chans[i].ic_freq == freq) {
-			/* when ambiguous take 11g over 11b */
-			if (flags == 0 &&
-			    IEEE80211_IS_CHAN_B(&chaninfo.ic_chans[i]) &&
-			    i+1 < chaninfo.ic_nchans &&
-			    chaninfo.ic_chans[i+1].ic_freq == freq) {
-				i++;
+	for (i = 0; i < chaninfo.ic_nchans; i++) {
+		const struct ieee80211_channel *c = &chaninfo.ic_chans[i];
+
+		if (c->ic_freq == freq && (c->ic_flags & flags) == flags) {
+			if (flags == 0) {
+				/* when ambiguous promote to ``best'' */
+				c = &chaninfo.ic_chans[promote(i)];
 			}
-			*c = chaninfo.ic_chans[i];
+			*chan = *c;
 			return;
 		}
+	}
 	errx(1, "unknown/undefined frequency %u/0x%x", freq, flags);
 }
 
 static void
-mapchan(struct ieee80211_channel *c, int ieee, int flags)
+mapchan(struct ieee80211_channel *chan, int ieee, int flags)
 {
 	int i;
 
-	for (i = 0; i < chaninfo.ic_nchans; i++)
-		if (chaninfo.ic_chans[i].ic_ieee == ieee) {
-			/* when ambiguous take 11g over 11b */
-			if (flags == 0 &&
-			    IEEE80211_IS_CHAN_B(&chaninfo.ic_chans[i]) &&
-			    i+1 < chaninfo.ic_nchans &&
-			    chaninfo.ic_chans[i+1].ic_ieee == ieee) {
-				i++;
+	for (i = 0; i < chaninfo.ic_nchans; i++) {
+		const struct ieee80211_channel *c = &chaninfo.ic_chans[i];
+
+		if (c->ic_ieee == ieee && (c->ic_flags & flags) == flags) {
+			if (flags == 0) {
+				/* when ambiguous promote to ``best'' */
+				c = &chaninfo.ic_chans[promote(i)];
 			}
-			*c = chaninfo.ic_chans[i];
+			*chan = *c;
 			return;
 		}
+	}
 	errx(1, "unknown/undefined channel number %d", ieee);
 }
 
@@ -207,6 +305,119 @@
 	set80211(s, IEEE80211_IOC_STATIONNAME, 0, len, data);
 }
 
+/*
+ * Parse a channel specification for attributes/flags.
+ * The syntax is:
+ *	freq/xx		channel width (5,10,20,40,40+,40-)
+ *	freq:mode	channel mode (a,b,g,h,n,t,s,d)
+ *
+ * These can be combined in either order; e.g. 2437:ng/40.
+ * Modes are case insensitive.
+ *
+ * The result is not validated here; it's assumed to be
+ * checked against the channel table fetched from the kernel.
+ */ 
+static int
+getchannelflags(const char *val)
+{
+#define	_CHAN_HT	0x80000000
+	const char *cp;
+	int flags;
+
+	flags = 0;
+
+	cp = strchr(val, ':');
+	if (cp != NULL) {
+		for (cp++; isalpha((int) *cp); cp++) {
+			/* accept mixed case */
+			int c = *cp;
+			if (isupper(c))
+				c = tolower(c);
+			switch (c) {
+			case 'a':		/* 802.11a */
+				flags |= IEEE80211_CHAN_A;
+				break;
+			case 'b':		/* 802.11b */
+				flags |= IEEE80211_CHAN_B;
+				break;
+			case 'g':		/* 802.11g */
+				flags |= IEEE80211_CHAN_G;
+				break;
+			case 'h':		/* ht = 802.11n */
+			case 'n':		/* 802.11n */
+				flags |= _CHAN_HT;	/* NB: private */
+				break;
+			case 'd':		/* dt = Atheros Dynamic Turbo */
+				flags |= IEEE80211_CHAN_TURBO;
+				break;
+			case 't':		/* ht, dt, st, t */
+				/* dt and unadorned t specify Dynamic Turbo */
+				if ((flags & (IEEE80211_CHAN_STURBO|_CHAN_HT)) == 0)
+					flags |= IEEE80211_CHAN_TURBO;
+				break;
+			case 's':		/* st = Atheros Static Turbo */
+				flags |= IEEE80211_CHAN_STURBO;
+				break;
+			default:
+				errx(-1, "%s: Invalid channel attribute %c\n",
+				    val, *cp);
+			}
+		}
+	}
+	cp = strchr(val, '/');
+	if (cp != NULL) {
+		char *ep;
+		u_long cw = strtoul(cp+1, &ep, 10);
+
+		switch (cw) {
+		case 5:
+			flags |= IEEE80211_CHAN_QUARTER;
+			break;
+		case 10:
+			flags |= IEEE80211_CHAN_HALF;
+			break;
+		case 20:
+			/* NB: this may be removed below */
+			flags |= IEEE80211_CHAN_HT20;
+			break;
+		case 40:
+			if (ep != NULL && *ep == '+')
+				flags |= IEEE80211_CHAN_HT40U;
+			else if (ep != NULL && *ep == '-')
+				flags |= IEEE80211_CHAN_HT40D;
+			else
+				flags |= IEEE80211_CHAN_HT40;
+			break;
+		default:
+			errx(-1, "%s: Invalid channel width\n", val);
+		}
+	}
+	/*
+	 * Cleanup specifications.
+	 */ 
+	if ((flags & _CHAN_HT) == 0) {
+		/*
+		 * If user specified freq/20 or freq/40 quietly remove
+		 * HT cw attributes depending on channel use.  To give
+		 * an explicit 20/40 width for an HT channel you must
+		 * indicate it is an HT channel since all HT channels
+		 * are also usable for legacy operation; e.g. freq:n/40.
+		 */
+		flags &= ~IEEE80211_CHAN_HT;
+	} else {
+		/*
+		 * Remove private indicator that this is an HT channel
+		 * and if no explicit channel width has been given
+		 * provide the default settings.
+		 */
+		flags &= ~_CHAN_HT;
+		if ((flags & IEEE80211_CHAN_HT) == 0)
+			flags |= IEEE80211_CHAN_HT;
+	}
+	return flags;
+#undef _CHAN_HT
+}
+
 static void
 set80211channel(const char *val, int d, int s, const struct afswtch *rafp)
 {
@@ -214,14 +425,14 @@
 
 	memset(&chan, 0, sizeof(chan));
 	if (!isanyarg(val)) {
-		/* XXX freq/width */
 		int v = atoi(val);
+		int flags = getchannelflags(val);
 
 		getchaninfo(s);
 		if (v > 255) {		/* treat as frequency */
-			mapfreq(&chan, v, 0);
+			mapfreq(&chan, v, flags);
 		} else {
-			mapchan(&chan, v, 0);
+			mapchan(&chan, v, flags);
 		}
 	} else {
 		chan.ic_freq = IEEE80211_CHAN_ANY;
@@ -806,6 +1017,12 @@
 	set80211(s, IEEE80211_IOC_BURST, d, 0, NULL);
 }
 
+static void
+set80211doth(const char *val, int d, int s, const struct afswtch *rafp)
+{
+	set80211(s, IEEE80211_IOC_DOTH, d, 0, NULL);
+}
+
 static int
 getmaxrate(uint8_t rates[15], uint8_t nrates)
 {
@@ -1441,6 +1658,8 @@
 		strlcat(buf, " 11b", bsize);
 	if (IEEE80211_IS_CHAN_TURBO(c))
 		strlcat(buf, " Turbo", bsize);
+	if (IEEE80211_IS_CHAN_HT(c))
+		strlcat(buf, " HT", bsize);
 	return buf;
 }
 
@@ -2296,6 +2515,14 @@
 		ireq.i_type = IEEE80211_IOC_DTIM_PERIOD;
 		if (ioctl(s, SIOCG80211, &ireq) != -1)
 			LINE_CHECK("dtimperiod %u", ireq.i_val);
+
+		ireq.i_type = IEEE80211_IOC_DOTH;
+		if (ioctl(s, SIOCG80211, &ireq) != -1) {
+			if (!ireq.i_val)
+				LINE_CHECK("-doth");
+			else if (verbose)
+				LINE_CHECK("doth");
+		}
 	} else {
 		ireq.i_type = IEEE80211_IOC_ROAMING;
 		if (ioctl(s, SIOCG80211, &ireq) != -1) {
@@ -2554,6 +2781,8 @@
 	DEF_CMD("-burst",	0,	set80211burst),
 	DEF_CMD_ARG("bmiss",		set80211bmissthreshold),
 	DEF_CMD_ARG("bmissthreshold",	set80211bmissthreshold),
+	DEF_CMD("doth",		1,	set80211doth),
+	DEF_CMD("-doth",	0,	set80211doth),
 };
 static struct afswtch af_ieee80211 = {
 	.af_name	= "af_ieee80211",



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?200704051610.l35GArlj039926>