summaryrefslogtreecommitdiffstats
path: root/sys/net80211/ieee80211_output.c
diff options
context:
space:
mode:
authoradrian <adrian@FreeBSD.org>2014-04-23 22:44:49 +0000
committeradrian <adrian@FreeBSD.org>2014-04-23 22:44:49 +0000
commitc5e575f92a51a6e08d6f127cfce87d7d2927449e (patch)
treee586015768575e3690ade11b7f1b40518735fd36 /sys/net80211/ieee80211_output.c
parent449cf3af57a513818b9effdb4d84bb96aab66f35 (diff)
downloadFreeBSD-src-c5e575f92a51a6e08d6f127cfce87d7d2927449e.zip
FreeBSD-src-c5e575f92a51a6e08d6f127cfce87d7d2927449e.tar.gz
Allow frames to be transmitted in either RUN or SLEEP state
Frames transmitted during SLEEP state should be queued in the power save queue before waking the unit up. Otherwise DHCP requests and such will be dropped if the NIC is asleep - the NIC will wake up but not transmit the frame.
Diffstat (limited to 'sys/net80211/ieee80211_output.c')
-rw-r--r--sys/net80211/ieee80211_output.c35
1 files changed, 25 insertions, 10 deletions
diff --git a/sys/net80211/ieee80211_output.c b/sys/net80211/ieee80211_output.c
index c6730cb..b923600 100644
--- a/sys/net80211/ieee80211_output.c
+++ b/sys/net80211/ieee80211_output.c
@@ -390,6 +390,19 @@ ieee80211_start_pkt(struct ieee80211vap *vap, struct mbuf *m)
/*
* We've resolved the sender, so attempt to transmit it.
*/
+
+ if (vap->iv_state == IEEE80211_S_SLEEP) {
+ /*
+ * In power save; queue frame and then wakeup device
+ * for transmit.
+ */
+ ic->ic_lastdata = ticks;
+ (void) ieee80211_pwrsave(ni, m);
+ ieee80211_free_node(ni);
+ ieee80211_new_state(vap, IEEE80211_S_RUN, 0);
+ return (0);
+ }
+
if (ieee80211_vap_pkt_send_dest(vap, m, ni) != 0)
return (ENOBUFS);
return (0);
@@ -420,24 +433,19 @@ ieee80211_vap_transmit(struct ifnet *ifp, struct mbuf *m)
m_freem(m);
return (EINVAL);
}
- if (vap->iv_state == IEEE80211_S_SLEEP) {
- /*
- * In power save, wakeup device for transmit.
- */
- ieee80211_new_state(vap, IEEE80211_S_RUN, 0);
- m_freem(m);
- return (0);
- }
+
/*
* No data frames go out unless we're running.
* Note in particular this covers CAC and CSA
* states (though maybe we should check muting
* for CSA).
*/
- if (vap->iv_state != IEEE80211_S_RUN) {
+ if (vap->iv_state != IEEE80211_S_RUN &&
+ vap->iv_state != IEEE80211_S_SLEEP) {
IEEE80211_LOCK(ic);
/* re-check under the com lock to avoid races */
- if (vap->iv_state != IEEE80211_S_RUN) {
+ if (vap->iv_state != IEEE80211_S_RUN &&
+ vap->iv_state != IEEE80211_S_SLEEP) {
IEEE80211_DPRINTF(vap, IEEE80211_MSG_OUTPUT,
"%s: ignore queue, in %s state\n",
__func__, ieee80211_state_name[vap->iv_state]);
@@ -477,6 +485,13 @@ ieee80211_vap_qflush(struct ifnet *ifp)
/*
* 802.11 raw output routine.
+ *
+ * XXX TODO: this (and other send routines) should correctly
+ * XXX keep the pwr mgmt bit set if it decides to call into the
+ * XXX driver to send a frame whilst the state is SLEEP.
+ *
+ * Otherwise the peer may decide that we're awake and flood us
+ * with traffic we are still too asleep to receive!
*/
int
ieee80211_raw_output(struct ieee80211vap *vap, struct ieee80211_node *ni,
OpenPOWER on IntegriCloud