net80211 hostap vs powersave

Bill Paul wpaul at FreeBSD.ORG
Sun Jan 25 02:39:06 PST 2009


So. Recently, I decided to trade in my Hiptop for a Blackberry Curve 8320.
It does many spiffy things. I even got it to do tethering with FreeBSD
via USB.

(See here for more details: http://www.freebsd.org/~wpaul/bb/ )

There was just thing I had trouble with. The 8320 is a wifi-enabled phone,
and I thought it would be nice to use it with my wifi setup at home. My
"access point" is actually a soekris board running FreeBSD 7.0 with a
Linksys cardbus adapter that uses an Atheros 5212 chipset. Normally,
I run it in adhoc mode.

Unfortunately, for whatever the reason, the Blackberry doesn't support
adhoc networks: you can only connect to BSS networks. "Well, ok," says I,
"I guess I'll set ath0 to hostap mode instead."

But that didn't work quite right either. The Blackberry could detect the
AP now, and it could associate and acquire a DHCP lease, but after that,
it seemed that it would only send frames, not receive them. Actually, it
seemed that it was the ath0 interface that wasn't sending the frames:
using tcpdump I could see traffic coming in, but nothing going out.

A bit of investigation showed that the problem had to do with the fact
that the Blackberry operates in powersave mode. This is sensible enough
since it's a portable device with limited battery power. However it
doesn't offer the option to turn it off. It looked like either the ath(4)
driver or net80211 had a problem supporting power save in hostap mode.

I dug around a bit, and there was one thing that looked a little strange
to me. In FreeBSD 7.0, the ieee80211_power.c module has the following
two functions:

void
ieee80211_power_attach(struct ieee80211com *ic)
{
        if (ic->ic_opmode == IEEE80211_M_HOSTAP ||
            ic->ic_opmode == IEEE80211_M_IBSS) {
                /* NB: driver should override */
                ic->ic_set_tim = ieee80211_set_tim;
        }
}

void
ieee80211_power_lateattach(struct ieee80211com *ic)
{
        /*
         * Allocate these only if needed.  Beware that we
         * know adhoc mode doesn't support ATIM yet...
         */
        if (ic->ic_opmode == IEEE80211_M_HOSTAP) {
                ic->ic_tim_len = howmany(ic->ic_max_aid,8) * sizeof(uint8_t);
                MALLOC(ic->ic_tim_bitmap, uint8_t *, ic->ic_tim_len,
                        M_DEVBUF, M_NOWAIT | M_ZERO);
                if (ic->ic_tim_bitmap == NULL) {
                        printf("%s: no memory for TIM bitmap!\n", __func__);
                        /* XXX good enough to keep from crashing? */
                        ic->ic_tim_len = 0;
                }
        }
}

Both of these are effectivelly called from the driver's attach routine,
which is run early on when an interface is initialized.

Here's what's strange. Both functions hook up some of the plumbing that
powersave requires, but only if ic_opmode == IEEE80211_M_HOSTAP (or in
the case of ieee80211_power_attach, if ic_opmode is either
IEEE80211_M_HOSTAP or IEEE80211_M_IBSS). But none of the wireless drivers
set ic_opmode to IEEE80211_M_HOSTAP or IEEE80211_M_IBSS by default: it's
up to the user or a startup script to change the operating mode to one of
these after the interfaces are attached. This means that at the time these
routines are run, ic_opmode can _never_ be IEEE80211_M_HOSTAP or
IEEE80211_M_IBSS, so the required setup never happens.

I changed these functions to look like this:

void
ieee80211_power_attach(struct ieee80211com *ic)
{
        if (ic->ic_caps & IEEE80211_C_HOSTAP ||
            ic->ic_caps & IEEE80211_C_IBSS) {
                /* NB: driver should override */
                ic->ic_set_tim = ieee80211_set_tim;
        }
}

void
ieee80211_power_lateattach(struct ieee80211com *ic)
{
        /*
         * Allocate these only if needed.  Beware that we
         * know adhoc mode doesn't support ATIM yet...
         */
        if (ic->ic_caps & IEEE80211_C_HOSTAP) {
                ic->ic_tim_len = howmany(ic->ic_max_aid,8) * sizeof(uint8_t);
                MALLOC(ic->ic_tim_bitmap, uint8_t *, ic->ic_tim_len,
                        M_DEVBUF, M_NOWAIT | M_ZERO);
                if (ic->ic_tim_bitmap == NULL) {
                        printf("%s: no memory for TIM bitmap!\n", __func__);
                        /* XXX good enough to keep from crashing? */
                        ic->ic_tim_len = 0;
                }
        }
}

Note that now they test the interface capabilities (ic_caps) rather than
the current operating mode (ic_opmode). With this change, the powersave
support now seems to work well enough that the Blackberry can associate
and exchange traffic with the ath0 interface as expected.

Looking at the code in -current, there have been some changes, but I
think the same bug is still there:

void
ieee80211_power_vattach(struct ieee80211vap *vap)
{
	if (vap->iv_opmode == IEEE80211_M_HOSTAP ||
	    vap->iv_opmode == IEEE80211_M_IBSS) {
		/* NB: driver should override */
		vap->iv_update_ps = ieee80211_update_ps;
		vap->iv_set_tim = ieee80211_set_tim;
	}
}

void
ieee80211_power_latevattach(struct ieee80211vap *vap)
{
	/*
	 * Allocate these only if needed.  Beware that we
	 * know adhoc mode doesn't support ATIM yet...
	 */
	if (vap->iv_opmode == IEEE80211_M_HOSTAP) {
		vap->iv_tim_len = howmany(vap->iv_max_aid,8) * sizeof(uint8_t);
		vap->iv_tim_bitmap = (uint8_t *) malloc(vap->iv_tim_len,
			M_80211_POWER, M_NOWAIT | M_ZERO);
		if (vap->iv_tim_bitmap == NULL) {
			printf("%s: no memory for TIM bitmap!\n", __func__);
			/* XXX good enough to keep from crashing? */
			vap->iv_tim_len = 0;
		}
	}
}

Unfortunately, I don't have the ability to test the same fix against
-current, but it seems to me that it still applies.

-Bill

--
=============================================================================
-Bill Paul            (510) 749-2329 | Senior Engineer, Master of Unix-Fu
                 wpaul at windriver.com | Wind River Systems
=============================================================================
   "I put a dollar in a change machine. Nothing changed." - George Carlin
=============================================================================


More information about the freebsd-current mailing list