This diff implements the bits needed to support power management on acx(4)
based APs. In other words with this I can use my OpenBSD acx111 hostap
just fine with my mobile devices that would fail before.

Tested on acx111 but not on acx100 based cards.
-- 
:wq Claudio

Index: dev/ic//acx.c
===================================================================
RCS file: /cvs/src/sys/dev/ic/acx.c,v
retrieving revision 1.97
diff -u -p -r1.97 acx.c
--- dev/ic//acx.c       27 Aug 2010 17:08:00 -0000      1.97
+++ dev/ic//acx.c       24 Oct 2012 20:35:57 -0000
@@ -202,6 +202,9 @@ int  acx_init_radio(struct acx_softc *, 
 void    acx_iter_func(void *, struct ieee80211_node *);
 void    acx_amrr_timeout(void *);
 void    acx_newassoc(struct ieee80211com *, struct ieee80211_node *, int);
+#ifndef IEEE80211_STA_ONLY
+void    acx_set_tim(struct ieee80211com *, int, int);
+#endif
 
 int            acx_beacon_intvl = 100; /* 100 TU */
 
@@ -316,6 +319,7 @@ acx_attach(struct acx_softc *sc)
 #ifndef IEEE80211_STA_ONLY
            IEEE80211_C_IBSS |                  /* IBSS mode */
            IEEE80211_C_HOSTAP |                /* Access Point */
+           IEEE80211_C_APPMGT |                /* AP Power Mgmt */
 #endif
            IEEE80211_C_SHPREAMBLE;             /* Short preamble */
 
@@ -343,6 +347,11 @@ acx_attach(struct acx_softc *sc)
        ic->ic_node_alloc = acx_node_alloc;
        ic->ic_newassoc = acx_newassoc;
 
+#ifndef IEEE80211_STA_ONLY
+       /* Override set TIM */
+       ic->ic_set_tim = acx_set_tim;
+#endif
+
        /* Override newstate */
        sc->sc_newstate = ic->ic_newstate;
        ic->ic_newstate = acx_newstate;
@@ -935,10 +944,12 @@ acx_start(struct ifnet *ifp)
             buf = &bd->tx_buf[idx]) {
                struct ieee80211_frame *wh;
                struct ieee80211_node *ni = NULL;
+               struct ieee80211_node *pni = NULL;
                struct mbuf *m;
                int rate;
 
                IF_DEQUEUE(&ic->ic_mgtq, m);
+               /* first dequeue management frames */
                if (m != NULL) {
                        ni = (struct ieee80211_node *)m->m_pkthdr.rcvif;
                        m->m_pkthdr.rcvif = NULL;
@@ -962,13 +973,20 @@ acx_start(struct ifnet *ifp)
                         */
                        rate = ni->ni_rates.rs_rates[0];
                        rate &= IEEE80211_RATE_VAL;
-               } else if (!IFQ_IS_EMPTY(&ifp->if_snd)) {
+               } else {
                        struct ether_header *eh;
 
-                       IFQ_DEQUEUE(&ifp->if_snd, m);
-                       if (m == NULL)
-                               break;
-
+                       /* then dequeue packets on the powersave queue */
+                       IF_DEQUEUE(&ic->ic_pwrsaveq, m);
+                       if (m != NULL) {
+                               ni = (struct ieee80211_node *)m->m_pkthdr.rcvif;
+                               m->m_pkthdr.rcvif = NULL;
+                               goto encapped;
+                       } else {
+                               IFQ_DEQUEUE(&ifp->if_snd, m);
+                               if (m == NULL)
+                                       break;
+                       }
                        if (ic->ic_state != IEEE80211_S_RUN) {
                                DPRINTF(("%s: data packet dropped due to "
                                    "not RUN.  Current state %d\n",
@@ -981,6 +999,8 @@ acx_start(struct ifnet *ifp)
                                m = m_pullup(m, sizeof(struct ether_header));
                                if (m == NULL) {
                                        ifp->if_oerrors++;
+                                       if (pni != NULL)
+                                               ieee80211_release_node(ic, pni);
                                        continue;
                                }
                        }
@@ -995,22 +1015,23 @@ acx_start(struct ifnet *ifp)
 
                        if ((m = ieee80211_encap(ifp, m, &ni)) == NULL) {
                                ifp->if_oerrors++;
+                               if (pni != NULL)
+                                       ieee80211_release_node(ic, pni);
                                continue;
                        }
-
-#if NBPFILTER > 0
-                       if (ic->ic_rawbpf != NULL)
-                               bpf_mtap(ic->ic_rawbpf, m, BPF_DIRECTION_OUT);
-#endif
-
+encapped:
                        if (ic->ic_fixed_rate != -1) {
                                rate = ic->ic_sup_rates[ic->ic_curmode].
                                    rs_rates[ic->ic_fixed_rate];
                        } else
                                rate = ni->ni_rates.rs_rates[ni->ni_txrate];
                        rate &= IEEE80211_RATE_VAL;
-               } else
-                       break;
+               } 
+
+#if NBPFILTER > 0
+               if (ic->ic_rawbpf != NULL)
+                       bpf_mtap(ic->ic_rawbpf, m, BPF_DIRECTION_OUT);
+#endif
 
                wh = mtod(m, struct ieee80211_frame *);
                if ((wh->i_fc[1] & IEEE80211_FC1_WEP) && !sc->chip_hw_crypt) {
@@ -1064,7 +1085,7 @@ acx_start(struct ifnet *ifp)
                 *    acx_txeof(), so it is not freed here.  acx_txeof()
                 *    will free it for us
                 */
-               trans = 1;
+               trans++;
                bd->tx_used_count++;
                idx = (idx + 1) % ACX_TX_DESC_CNT;
        }
@@ -1116,14 +1137,19 @@ acx_intr(void *arg)
                return (0);
        }
 
+       /* Acknowledge all interrupts */
+       CSR_WRITE_2(sc, ACXREG_INTR_ACK, ACXRV_INTR_ALL);
+
        intr_status &= sc->chip_intr_enable;
        if (intr_status == 0) {
                /* not interrupts we care about */
                return (1);
        }
 
-       /* Acknowledge all interrupts */
-       CSR_WRITE_2(sc, ACXREG_INTR_ACK, ACXRV_INTR_ALL);
+#ifndef IEEE80211_STA_ONLY
+       if (intr_status & ACXRV_INTR_DTIM)
+               ieee80211_notify_dtim(&sc->sc_ic);
+#endif
 
        if (intr_status & ACXRV_INTR_TX_FINI)
                acx_txeof(sc);
@@ -2733,3 +2759,23 @@ acx_newassoc(struct ieee80211com *ic, st
            i--);
        ni->ni_txrate = i;
 }
+
+#ifndef IEEE80211_STA_ONLY
+void
+acx_set_tim(struct ieee80211com *ic, int aid, int set)
+{
+       struct acx_softc *sc = ic->ic_if.if_softc;
+       struct acx_tmplt_tim tim;
+       u_int8_t *ep;
+
+       if (set)
+               setbit(ic->ic_tim_bitmap, aid & ~0xc000);
+       else
+               clrbit(ic->ic_tim_bitmap, aid & ~0xc000);
+
+       bzero(&tim, sizeof(tim));
+       ep = ieee80211_add_tim(tim.data.u_mem, ic);
+
+       acx_set_tmplt(sc, ACXCMD_TMPLT_TIM, &tim, ep - (u_int8_t *)&tim);
+}
+#endif
Index: dev/ic//acx100.c
===================================================================
RCS file: /cvs/src/sys/dev/ic/acx100.c,v
retrieving revision 1.21
diff -u -p -r1.21 acx100.c
--- dev/ic//acx100.c    20 Apr 2010 22:05:41 -0000      1.21
+++ dev/ic//acx100.c    3 Oct 2012 05:37:59 -0000
@@ -264,6 +264,9 @@ acx100_set_param(struct acx_softc *sc)
        sc->chip_ioreg = acx100_reg;
        sc->chip_hw_crypt = 1;
        sc->chip_intr_enable = ACX100_INTR_ENABLE;
+#ifndef IEEE80211_STA_ONLY
+       sc->chip_intr_enable |= ACXRV_INTR_DTIM;
+#endif
        sc->chip_intr_disable = ACX100_INTR_DISABLE;
        sc->chip_gpio_pled = ACX100_GPIO_POWER_LED;
        sc->chip_ee_eaddr_ofs = ACX100_EE_EADDR_OFS;
Index: dev/ic//acx111.c
===================================================================
RCS file: /cvs/src/sys/dev/ic/acx111.c,v
retrieving revision 1.18
diff -u -p -r1.18 acx111.c
--- dev/ic//acx111.c    20 Apr 2010 22:05:41 -0000      1.18
+++ dev/ic//acx111.c    3 Oct 2012 05:37:29 -0000
@@ -276,6 +276,9 @@ acx111_set_param(struct acx_softc *sc)
        sc->chip_mem2_rid = PCIR_BAR(1);
        sc->chip_ioreg = acx111_reg;
        sc->chip_intr_enable = ACX111_INTR_ENABLE;
+#ifndef IEEE80211_STA_ONLY
+       sc->chip_intr_enable |= ACXRV_INTR_DTIM;
+#endif
        sc->chip_intr_disable = ACX111_INTR_DISABLE;
        sc->chip_gpio_pled = ACX111_GPIO_POWER_LED;
        sc->chip_ee_eaddr_ofs = ACX111_EE_EADDR_OFS;

Reply via email to