PERFORCE change 117438 for review
Sam Leffler
sam at FreeBSD.org
Thu Apr 5 16:10:55 UTC 2007
http://perforce.freebsd.org/chv.cgi?CH=117438
Change 117438 by sam at 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",
More information about the p4-projects
mailing list