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;