Finally got annoyed enough that my MacBook running OS X (don't ask)
didn't work too well on my OpenBSD AP at home.  The reason is the
following caveat listed in the athn(4) man page:

     Host AP mode doesn't support power saving.  Clients attempting to use
     power saving mode may experience significant packet loss (disabling power
     saving on the client will fix this).

Unfortunately Steve doesn't allow you to disable power saving.  So
here is a diff to make athn(4) in host AP mode handle clients that use
power saving.  The Mac is much happier now.

Further testing would be welcome.  Even if you don't use clients with
power saving enabled.  So if you're running an athn(4) based AP,
please give this a spin.

Thanks,

Mark

P.S. As you can see from the diff, power saving support in the OpenBSD
802.11 network stack is pretty much complete, needing only a one-line
fix.  This means that adding power saving support for oher wireless
drivers shouldn't be too difficult.  If you're interested in a
particular driver and willing to ship hardware, please contact me.


Index: net80211/ieee80211_node.c
===================================================================
RCS file: /cvs/src/sys/net80211/ieee80211_node.c,v
retrieving revision 1.71
diff -u -p -r1.71 ieee80211_node.c
--- net80211/ieee80211_node.c   18 Jul 2012 13:24:28 -0000      1.71
+++ net80211/ieee80211_node.c   17 Aug 2012 21:54:39 -0000
@@ -204,6 +204,9 @@ ieee80211_node_lateattach(struct ifnet *
        ni->ni_chan = IEEE80211_CHAN_ANYC;
        ic->ic_bss = ieee80211_ref_node(ni);
        ic->ic_txpower = IEEE80211_TXPOWER_MAX;
+#ifndef IEEE80211_STA_ONLY
+       IFQ_SET_MAXLEN(&ni->ni_savedq, IEEE80211_PS_MAX_QUEUE);
+#endif
 }
 
 void
Index: dev/ic/ar5008.c
===================================================================
RCS file: /cvs/src/sys/dev/ic/ar5008.c,v
retrieving revision 1.20
diff -u -p -r1.20 ar5008.c
--- dev/ic/ar5008.c     19 Jun 2011 00:27:34 -0000      1.20
+++ dev/ic/ar5008.c     17 Aug 2012 21:54:41 -0000
@@ -1053,6 +1053,8 @@ int
 ar5008_swba_intr(struct athn_softc *sc)
 {
        struct ieee80211com *ic = &sc->sc_ic;
+       struct ifnet *ifp = &ic->ic_if;
+       struct ieee80211_node *ni = ic->ic_bss;
        struct athn_tx_buf *bf = sc->bcnbuf;
        struct ieee80211_frame *wh;
        struct ar_tx_desc *ds;
@@ -1060,6 +1062,11 @@ ar5008_swba_intr(struct athn_softc *sc)
        uint8_t ridx, hwrate;
        int error, totlen;
 
+       if (ic->ic_tim_mcast_pending &&
+           IF_IS_EMPTY(&ni->ni_savedq) &&
+           SIMPLEQ_EMPTY(&sc->txq[ATHN_QID_CAB].head))
+               ic->ic_tim_mcast_pending = 0;
+
        if (ic->ic_dtim_count == 0)
                ic->ic_dtim_count = ic->ic_dtim_period - 1;
        else
@@ -1133,6 +1140,26 @@ ar5008_swba_intr(struct athn_softc *sc)
 
        AR_WRITE(sc, AR_QTXDP(ATHN_QID_BEACON), bf->bf_daddr);
 
+       for(;;) {
+               if (SIMPLEQ_EMPTY(&sc->txbufs))
+                       break;
+
+               IF_DEQUEUE(&ni->ni_savedq, m);
+               if (m == NULL)
+                       break;
+               if (!IF_IS_EMPTY(&ni->ni_savedq)) {
+                       /* more queued frames, set the more data bit */
+                       wh = mtod(m, struct ieee80211_frame *);
+                       wh->i_fc[1] |= IEEE80211_FC1_MORE_DATA;
+               }
+               
+               if (sc->ops.tx(sc, m, ni, ATHN_TXFLAG_CAB) != 0) {
+                       ieee80211_release_node(ic, ni);
+                       ifp->if_oerrors++;
+                       break;
+               }
+       }
+
        /* Kick Tx. */
        AR_WRITE(sc, AR_Q_TXE, 1 << ATHN_QID_BEACON);
        AR_WRITE_BARRIER(sc);
@@ -1285,6 +1312,8 @@ ar5008_tx(struct athn_softc *sc, struct 
                qid = athn_ac2qid[ieee80211_up_to_ac(ic, tid)];
        } else if (type == AR_FRAME_TYPE_PSPOLL) {
                qid = ATHN_QID_PSPOLL;
+       } else if (txflags & ATHN_TXFLAG_CAB) {
+               qid = ATHN_QID_CAB;
        } else
                qid = ATHN_QID_AC_BE;
        txq = &sc->txq[qid];
Index: dev/ic/ar9003.c
===================================================================
RCS file: /cvs/src/sys/dev/ic/ar9003.c,v
retrieving revision 1.22
diff -u -p -r1.22 ar9003.c
--- dev/ic/ar9003.c     1 Jan 2011 13:44:42 -0000       1.22
+++ dev/ic/ar9003.c     17 Aug 2012 21:54:42 -0000
@@ -1183,6 +1183,8 @@ int
 ar9003_swba_intr(struct athn_softc *sc)
 {
        struct ieee80211com *ic = &sc->sc_ic;
+       struct ifnet *ifp = &ic->ic_if;
+       struct ieee80211_node *ni = ic->ic_bss;
        struct athn_tx_buf *bf = sc->bcnbuf;
        struct ieee80211_frame *wh;
        struct ar_tx_desc *ds;
@@ -1191,6 +1193,11 @@ ar9003_swba_intr(struct athn_softc *sc)
        uint8_t ridx, hwrate;
        int error, totlen;
 
+       if (ic->ic_tim_mcast_pending &&
+           IF_IS_EMPTY(&ni->ni_savedq) &&
+           SIMPLEQ_EMPTY(&sc->txq[ATHN_QID_CAB].head))
+               ic->ic_tim_mcast_pending = 0;
+
        if (ic->ic_dtim_count == 0)
                ic->ic_dtim_count = ic->ic_dtim_period - 1;
        else
@@ -1276,6 +1283,26 @@ ar9003_swba_intr(struct athn_softc *sc)
 
        AR_WRITE(sc, AR_QTXDP(ATHN_QID_BEACON), bf->bf_daddr);
 
+       for(;;) {
+               if (SIMPLEQ_EMPTY(&sc->txbufs))
+                       break;
+
+               IF_DEQUEUE(&ni->ni_savedq, m);
+               if (m == NULL)
+                       break;
+               if (!IF_IS_EMPTY(&ni->ni_savedq)) {
+                       /* more queued frames, set the more data bit */
+                       wh = mtod(m, struct ieee80211_frame *);
+                       wh->i_fc[1] |= IEEE80211_FC1_MORE_DATA;
+               }
+               
+               if (sc->ops.tx(sc, m, ni, ATHN_TXFLAG_CAB) != 0) {
+                       ieee80211_release_node(ic, ni);
+                       ifp->if_oerrors++;
+                       break;
+               }
+       }
+
        /* Kick Tx. */
        AR_WRITE(sc, AR_Q_TXE, 1 << ATHN_QID_BEACON);
        AR_WRITE_BARRIER(sc);
@@ -1425,6 +1452,8 @@ ar9003_tx(struct athn_softc *sc, struct 
                qid = athn_ac2qid[ieee80211_up_to_ac(ic, tid)];
        } else if (type == AR_FRAME_TYPE_PSPOLL) {
                qid = ATHN_QID_PSPOLL;
+       } else if (txflags & ATHN_TXFLAG_CAB) {
+               qid = ATHN_QID_CAB;
        } else
                qid = ATHN_QID_AC_BE;
        txq = &sc->txq[qid];
Index: dev/ic/athn.c
===================================================================
RCS file: /cvs/src/sys/dev/ic/athn.c,v
retrieving revision 1.72
diff -u -p -r1.72 athn.c
--- dev/ic/athn.c       10 Jun 2012 21:23:36 -0000      1.72
+++ dev/ic/athn.c       17 Aug 2012 21:54:43 -0000
@@ -283,6 +283,7 @@ athn_attach(struct athn_softc *sc)
            IEEE80211_C_RSN |           /* WPA/RSN. */
 #ifndef IEEE80211_STA_ONLY
            IEEE80211_C_HOSTAP |        /* Host Ap mode supported. */
+           IEEE80211_C_APPMGT |
 #endif
            IEEE80211_C_MONITOR |       /* Monitor mode supported. */
            IEEE80211_C_SHSLOT |        /* Short slot time supported. */
@@ -2539,6 +2540,14 @@ athn_start(struct ifnet *ifp)
                }
                /* Send pending management frames first. */
                IF_DEQUEUE(&ic->ic_mgtq, m);
+               if (m != NULL) {
+                       ni = (void *)m->m_pkthdr.rcvif;
+                       goto sendit;
+               }
+               if (ic->ic_state != IEEE80211_S_RUN)
+                       break;
+
+               IF_DEQUEUE(&ic->ic_pwrsaveq, m);
                if (m != NULL) {
                        ni = (void *)m->m_pkthdr.rcvif;
                        goto sendit;
Index: dev/ic/athnvar.h
===================================================================
RCS file: /cvs/src/sys/dev/ic/athnvar.h,v
retrieving revision 1.31
diff -u -p -r1.31 athnvar.h
--- dev/ic/athnvar.h    10 Jun 2012 21:23:36 -0000      1.31
+++ dev/ic/athnvar.h    17 Aug 2012 21:54:44 -0000
@@ -83,6 +83,7 @@ struct athn_tx_buf {
        struct ieee80211_node           *bf_ni;
        int                             bf_txflags;
 #define ATHN_TXFLAG_PAPRD      (1 << 0)
+#define ATHN_TXFLAG_CAB                (1 << 1) 
 };
 
 struct athn_txq {

Reply via email to