From owner-p4-projects@FreeBSD.ORG Thu Apr 5 16:10:54 2007 Return-Path: X-Original-To: p4-projects@freebsd.org Delivered-To: p4-projects@freebsd.org Received: by hub.freebsd.org (Postfix, from userid 32767) id A2E7816A409; Thu, 5 Apr 2007 16:10:54 +0000 (UTC) X-Original-To: perforce@freebsd.org Delivered-To: perforce@freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [69.147.83.52]) by hub.freebsd.org (Postfix) with ESMTP id 5C8E316A408 for ; Thu, 5 Apr 2007 16:10:54 +0000 (UTC) (envelope-from sam@freebsd.org) Received: from repoman.freebsd.org (repoman.freebsd.org [69.147.83.41]) by mx1.freebsd.org (Postfix) with ESMTP id 4D56D13C44B for ; Thu, 5 Apr 2007 16:10:54 +0000 (UTC) (envelope-from sam@freebsd.org) Received: from repoman.freebsd.org (localhost [127.0.0.1]) by repoman.freebsd.org (8.13.8/8.13.8) with ESMTP id l35GAsh1039929 for ; Thu, 5 Apr 2007 16:10:54 GMT (envelope-from sam@freebsd.org) Received: (from perforce@localhost) by repoman.freebsd.org (8.13.8/8.13.8/Submit) id l35GArlj039926 for perforce@freebsd.org; Thu, 5 Apr 2007 16:10:53 GMT (envelope-from sam@freebsd.org) Date: Thu, 5 Apr 2007 16:10:53 GMT Message-Id: <200704051610.l35GArlj039926@repoman.freebsd.org> X-Authentication-Warning: repoman.freebsd.org: perforce set sender to sam@freebsd.org using -f From: Sam Leffler To: Perforce Change Reviews Cc: Subject: PERFORCE change 117438 for review X-BeenThere: p4-projects@freebsd.org X-Mailman-Version: 2.1.5 Precedence: list List-Id: p4 projects tree changes List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Thu, 05 Apr 2007 16:10:55 -0000 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",