First cut at attempting to buffer frames until we see a beacon.
The iwn(4) firmware forgets most of its channel state after an RXON command. This means that any beacons its seen on passive 5GHz channels are forgotten upon an association/authorisation request. This unfortuantely means that 5GHz association almost always fails - the assoc and/or auth frames are dropped with a status of "passive channel, haven't seen a beacon yet." (0x90.) So: * add an xmit queue, global, to buffer frames * modify the xmit path to use the mbuf tag from net80211 to specify raw frame details * buffer xmit frames from both raw and non-raw paths * if a beacon is seen in the RX path, schedule a taskqueue to send said frames and un-buffer things. * flush frames during state change back to INIT, or NIC down/up/detach. This isn't the final shape I'd like this to be in but it certainly is better than 5GHz "not working at all". Tested: * Intel 5100, STA mode (before spilling coffee) * Intel 5300, STA mode (after spilling coffee) Story: * This has been bugging me at work for months, which I just worked around by throwing an ath(4) into my Lenovo T400 cardbus slot. * Our ops director discovered indeed FreeBSD runs well on the Lenovo T420p, except for that pesky 5GHz thing. So now developers also can have a T420p running FreeBSD to do work with. Their #1 feedback to me - "boy it'd be nice if 5GHz wifi worked." * .. then, I was at NANOG but stuck with 5GHz only wifi and no ath(4) NIC to put in a laptop - and I snapped. Thus, the reason this is actually work related. MFC after: 2 weeks Sponsored by: Norse Corp, Inc.
This commit is contained in:
parent
bcb23c1e09
commit
3233d0c654
@ -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 ||
|
||||
|
@ -309,6 +309,7 @@ struct iwn_softc {
|
||||
struct task sc_radioon_task;
|
||||
struct task sc_radiooff_task;
|
||||
struct task sc_panic_task;
|
||||
struct task sc_xmit_task;
|
||||
|
||||
/* Taskqueue */
|
||||
struct taskqueue *sc_tq;
|
||||
@ -385,6 +386,9 @@ struct iwn_softc {
|
||||
/* Are we doing a scan? */
|
||||
int sc_is_scanning;
|
||||
|
||||
/* Are we waiting for a beacon before xmit? */
|
||||
int sc_beacon_wait;
|
||||
|
||||
struct ieee80211_tx_ampdu *qid2tap[IWN5000_NTXQUEUES];
|
||||
|
||||
int (*sc_ampdu_rx_start)(struct ieee80211_node *,
|
||||
@ -417,6 +421,13 @@ struct iwn_softc {
|
||||
|
||||
#define IWN_UCODE_API(ver) (((ver) & 0x0000FF00) >> 8)
|
||||
uint32_t ucode_rev;
|
||||
|
||||
/*
|
||||
* Global queue for queuing xmit frames
|
||||
* when we can't yet transmit (eg raw
|
||||
* frames whilst waiting for beacons.)
|
||||
*/
|
||||
struct mbufq sc_xmit_queue;
|
||||
};
|
||||
|
||||
#define IWN_LOCK_INIT(_sc) \
|
||||
|
Loading…
Reference in New Issue
Block a user