summaryrefslogtreecommitdiffstats
path: root/sys/dev/iwn/if_iwn.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/dev/iwn/if_iwn.c')
-rw-r--r--sys/dev/iwn/if_iwn.c140
1 files changed, 140 insertions, 0 deletions
diff --git a/sys/dev/iwn/if_iwn.c b/sys/dev/iwn/if_iwn.c
index a04d446..f3cf2ae 100644
--- a/sys/dev/iwn/if_iwn.c
+++ b/sys/dev/iwn/if_iwn.c
@@ -232,6 +232,7 @@ static int iwn_tx_data(struct iwn_softc *, struct mbuf *,
static int iwn_tx_data_raw(struct iwn_softc *, struct mbuf *,
struct ieee80211_node *,
const struct ieee80211_bpf_params *params);
+static void iwn_xmit_task(void *arg0, int pending);
static int iwn_raw_xmit(struct ieee80211_node *, struct mbuf *,
const struct ieee80211_bpf_params *);
static void iwn_start(struct ifnet *);
@@ -682,6 +683,9 @@ iwn_attach(device_t dev)
TASK_INIT(&sc->sc_radioon_task, 0, iwn_radio_on, sc);
TASK_INIT(&sc->sc_radiooff_task, 0, iwn_radio_off, sc);
TASK_INIT(&sc->sc_panic_task, 0, iwn_panicked, sc);
+ TASK_INIT(&sc->sc_xmit_task, 0, iwn_xmit_task, sc);
+
+ mbufq_init(&sc->sc_xmit_queue, 1024);
sc->sc_tq = taskqueue_create("iwn_taskq", M_WAITOK,
taskqueue_thread_enqueue, &sc->sc_tq);
@@ -1360,6 +1364,28 @@ iwn_vap_delete(struct ieee80211vap *vap)
free(ivp, M_80211_VAP);
}
+static void
+iwn_xmit_queue_drain(struct iwn_softc *sc)
+{
+ struct mbuf *m;
+ struct ieee80211_node *ni;
+
+ IWN_LOCK_ASSERT(sc);
+ while ((m = mbufq_dequeue(&sc->sc_xmit_queue)) != NULL) {
+ ni = (struct ieee80211_node *)m->m_pkthdr.rcvif;
+ ieee80211_free_node(ni);
+ m_freem(m);
+ }
+}
+
+static int
+iwn_xmit_queue_enqueue(struct iwn_softc *sc, struct mbuf *m)
+{
+
+ IWN_LOCK_ASSERT(sc);
+ return (mbufq_enqueue(&sc->sc_xmit_queue, m));
+}
+
static int
iwn_detach(device_t dev)
{
@@ -1373,6 +1399,11 @@ iwn_detach(device_t dev)
if (ifp != NULL) {
ic = ifp->if_l2com;
+ /* Free the mbuf queue and node references */
+ IWN_LOCK(sc);
+ iwn_xmit_queue_drain(sc);
+ IWN_UNLOCK(sc);
+
ieee80211_draintask(ic, &sc->sc_reinit_task);
ieee80211_draintask(ic, &sc->sc_radioon_task);
ieee80211_draintask(ic, &sc->sc_radiooff_task);
@@ -2831,6 +2862,9 @@ iwn_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
sc->rxon->filter &= ~htole32(IWN_FILTER_BSS);
sc->calib.state = IWN_CALIB_STATE_INIT;
+ /* Wait until we hear a beacon before we transmit */
+ sc->sc_beacon_wait = 1;
+
if ((error = iwn_auth(sc, vap)) != 0) {
device_printf(sc->sc_dev,
"%s: could not move to auth state\n", __func__);
@@ -2846,6 +2880,9 @@ iwn_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
break;
}
+ /* Wait until we hear a beacon before we transmit */
+ sc->sc_beacon_wait = 1;
+
/*
* !RUN -> RUN requires setting the association id
* which is done with a firmware cmd. We also defer
@@ -2859,6 +2896,12 @@ iwn_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
case IEEE80211_S_INIT:
sc->calib.state = IWN_CALIB_STATE_INIT;
+ /*
+ * Purge the xmit queue so we don't have old frames
+ * during a new association attempt.
+ */
+ sc->sc_beacon_wait = 0;
+ iwn_xmit_queue_drain(sc);
break;
default:
@@ -3066,6 +3109,32 @@ iwn_rx_done(struct iwn_softc *sc, struct iwn_rx_desc *desc,
}
}
+ /*
+ * If it's a beacon and we're waiting, then do the
+ * wakeup. This should unblock raw_xmit/start.
+ */
+ if (sc->sc_beacon_wait) {
+ uint8_t type, subtype;
+ /* NB: Re-assign wh */
+ wh = mtod(m, struct ieee80211_frame *);
+ type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK;
+ subtype = wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK;
+ /*
+ * This assumes at this point we've received our own
+ * beacon.
+ */
+ DPRINTF(sc, IWN_DEBUG_TRACE,
+ "%s: beacon_wait, type=%d, subtype=%d\n",
+ __func__, type, subtype);
+ if (type == IEEE80211_FC0_TYPE_MGT &&
+ subtype == IEEE80211_FC0_SUBTYPE_BEACON) {
+ DPRINTF(sc, IWN_DEBUG_TRACE | IWN_DEBUG_XMIT,
+ "%s: waking things up\n", __func__);
+ /* queue taskqueue to transmit! */
+ taskqueue_enqueue(sc->sc_tq, &sc->sc_xmit_task);
+ }
+ }
+
IWN_UNLOCK(sc);
/* Send the frame to the 802.11 layer. */
@@ -4802,6 +4871,51 @@ iwn_tx_data_raw(struct iwn_softc *sc, struct mbuf *m,
return 0;
}
+static void
+iwn_xmit_task(void *arg0, int pending)
+{
+ struct iwn_softc *sc = arg0;
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211_node *ni;
+ struct mbuf *m;
+ int error;
+ struct ieee80211_bpf_params p;
+ int have_p;
+
+ DPRINTF(sc, IWN_DEBUG_XMIT, "%s: called\n", __func__);
+
+ IWN_LOCK(sc);
+ /*
+ * Dequeue frames, attempt to transmit,
+ * then disable beaconwait when we're done.
+ */
+ while ((m = mbufq_dequeue(&sc->sc_xmit_queue)) != NULL) {
+ have_p = 0;
+ ni = (struct ieee80211_node *)m->m_pkthdr.rcvif;
+
+ /* Get xmit params if appropriate */
+ if (ieee80211_get_xmit_params(m, &p) == 0)
+ have_p = 1;
+
+ DPRINTF(sc, IWN_DEBUG_XMIT, "%s: m=%p, have_p=%d\n",
+ __func__, m, have_p);
+
+ /* If we have xmit params, use them */
+ if (have_p)
+ error = iwn_tx_data_raw(sc, m, ni, &p);
+ else
+ error = iwn_tx_data(sc, m, ni);
+
+ if (error != 0) {
+ ieee80211_free_node(ni);
+ if_inc_counter(ifp, IFCOUNTER_OERRORS, 1);
+ }
+ }
+
+ sc->sc_beacon_wait = 0;
+ IWN_UNLOCK(sc);
+}
+
static int
iwn_raw_xmit(struct ieee80211_node *ni, struct mbuf *m,
const struct ieee80211_bpf_params *params)
@@ -4819,7 +4933,25 @@ iwn_raw_xmit(struct ieee80211_node *ni, struct mbuf *m,
return ENETDOWN;
}
+ /* XXX? net80211 doesn't set this on xmit'ed raw frames? */
+ m->m_pkthdr.rcvif = (void *) ni;
+
IWN_LOCK(sc);
+
+ /* queue frame if we have to */
+ if (sc->sc_beacon_wait) {
+ if (iwn_xmit_queue_enqueue(sc, m) != 0) {
+ m_freem(m);
+ ieee80211_free_node(ni);
+ if_inc_counter(ifp, IFCOUNTER_OERRORS, 1);
+ IWN_UNLOCK(sc);
+ return (ENOBUFS);
+ }
+ /* Queued, so just return OK */
+ IWN_UNLOCK(sc);
+ return (0);
+ }
+
if (params == NULL) {
/*
* Legacy path; interpret frame contents to decide
@@ -4866,6 +4998,14 @@ iwn_start_locked(struct ifnet *ifp)
IWN_LOCK_ASSERT(sc);
+ /*
+ * If we're waiting for a beacon, we can just exit out here
+ * and wait for the taskqueue to be kicked.
+ */
+ if (sc->sc_beacon_wait) {
+ return;
+ }
+
DPRINTF(sc, IWN_DEBUG_XMIT, "%s: called\n", __func__);
if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0 ||
OpenPOWER on IntegriCloud