int rtwn_cmd_sleepable(struct rtwn_softc *sc, const void *ptr, size_t len, CMD_FUNC_PROTO) { struct ieee80211com *ic = &sc->sc_ic; KASSERT(len <= sizeof(union sec_param), ("buffer overflow")); RTWN_CMDQ_LOCK(sc); if (sc->sc_detached) { RTWN_CMDQ_UNLOCK(sc); return (ESHUTDOWN); } if (sc->cmdq[sc->cmdq_last].func != NULL) { device_printf(sc->sc_dev, "%s: cmdq overflow\n", __func__); RTWN_CMDQ_UNLOCK(sc); return (EAGAIN); } if (ptr != NULL) memcpy(&sc->cmdq[sc->cmdq_last].data, ptr, len); sc->cmdq[sc->cmdq_last].func = func; sc->cmdq_last = (sc->cmdq_last + 1) % RTWN_CMDQ_SIZE; RTWN_CMDQ_UNLOCK(sc); ieee80211_runtask(ic, &sc->cmdq_task); return (0); }
/* * Synchronize flag bit state in the parent ifnet structure * according to the state of all vap ifnet's. This is used, * for example, to handle IFF_PROMISC and IFF_ALLMULTI. */ void ieee80211_syncifflag_locked(struct ieee80211com *ic, int flag) { struct ifnet *ifp = ic->ic_ifp; struct ieee80211vap *vap; int bit, oflags; IEEE80211_LOCK_ASSERT(ic); bit = 0; TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) if (vap->iv_ifp->if_flags & flag) { /* * XXX the bridge sets PROMISC but we don't want to * enable it on the device, discard here so all the * drivers don't need to special-case it */ if (flag == IFF_PROMISC && !(vap->iv_opmode == IEEE80211_M_MONITOR || (vap->iv_opmode == IEEE80211_M_AHDEMO && (vap->iv_caps & IEEE80211_C_TDMA) == 0))) continue; bit = 1; break; } oflags = ifp->if_flags; if (bit) ifp->if_flags |= flag; else ifp->if_flags &= ~flag; if ((ifp->if_flags ^ oflags) & flag) { /* XXX should we return 1/0 and let caller do this? */ if (ifp->if_drv_flags & IFF_DRV_RUNNING) { if (flag == IFF_PROMISC) ieee80211_runtask(ic, &ic->ic_promisc_task); else if (flag == IFF_ALLMULTI) ieee80211_runtask(ic, &ic->ic_mcast_task); } } }
static void athp_taskq_task(void *arg, int npending) { struct ath10k *ar = arg; struct ieee80211com *ic = &ar->sc_ic; struct athp_taskq_entry *e; struct athp_taskq_head *h; int n = 0; h = ar->sc_taskq_head; if (h == NULL) return; ath10k_dbg(ar, ATH10K_DBG_TASKQ, "%s: called\n", __func__); /* * Run through the queue up to 4 at a time, and * run the callbacks. */ ATHP_TASKQ_LOCK(h); while ((n < 4) && (e = TAILQ_FIRST(&h->list)) != NULL) { TAILQ_REMOVE(&h->list, e, node); e->on_queue = 0; ATHP_TASKQ_UNLOCK(h); ath10k_dbg(ar, ATH10K_DBG_TASKQ, "%s: calling cb %s %p (ptr %p)\n", __func__, e->cb_str, e->cb, e); e->cb(ar, e, 1); athp_taskq_entry_free(ar, e); n++; ATHP_TASKQ_LOCK(h); } /* Whilst locked, see if there's any more work to do */ n = 0; if (h->is_running && TAILQ_FIRST(&h->list) != NULL) { n = 1; } ATHP_TASKQ_UNLOCK(h); if (n) ieee80211_runtask(ic, &h->run_task); }
void athp_taskq_start(struct ath10k *ar) { struct ieee80211com *ic = &ar->sc_ic; struct athp_taskq_head *h; h = ar->sc_taskq_head; if (h == NULL) return; ath10k_dbg(ar, ATH10K_DBG_TASKQ, "%s: called\n", __func__); ATHP_TASKQ_LOCK(h); h->is_running = 1; ATHP_TASKQ_UNLOCK(h); ieee80211_runtask(ic, &h->run_task); }
int athp_taskq_queue(struct ath10k *ar, struct athp_taskq_entry *e, const char *str, athp_taskq_cmd_cb *cb) { struct ieee80211com *ic = &ar->sc_ic; struct athp_taskq_head *h; int do_run = 0; h = ar->sc_taskq_head; if (h == NULL) return (EINVAL); ath10k_dbg(ar, ATH10K_DBG_TASKQ, "%s: queuing cb %s %p (ptr %p)\n", __func__, str, cb, e); e->ar = ar; e->on_queue = 1; e->cb = cb; e->cb_str = str; ATHP_TASKQ_LOCK(h); e->on_queue = 1; TAILQ_INSERT_TAIL(&h->list, e, node); if (h->is_running) do_run = 1; ATHP_TASKQ_UNLOCK(h); if (do_run) ieee80211_runtask(ic, &h->run_task); return (0); }
static void scan_end(struct ieee80211_scan_state *ss, int scandone) { struct scan_state *ss_priv = SCAN_PRIVATE(ss); struct ieee80211vap *vap = ss->ss_vap; struct ieee80211com *ic = ss->ss_ic; IEEE80211_LOCK_ASSERT(ic); IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN, "%s: out\n", __func__); if (ss_priv->ss_iflags & ISCAN_ABORT) { scan_done(ss, scandone); return; } IEEE80211_UNLOCK(ic); ic->ic_scan_end(ic); /* notify driver */ IEEE80211_LOCK(ic); /* XXX scan state can change! Re-validate scan state! */ /* * Since a cancellation may have occurred during one of the * driver calls (whilst unlocked), update scandone. */ if (scandone == 0 && (ss_priv->ss_iflags & ISCAN_CANCEL) != 0) { /* XXX printf? */ if_printf(vap->iv_ifp, "%s: OOPS! scan cancelled during driver call (1)!\n", __func__); scandone = 1; } /* * Record scan complete time. Note that we also do * this when canceled so any background scan will * not be restarted for a while. */ if (scandone) ic->ic_lastscan = ticks; /* return to the bss channel */ if (ic->ic_bsschan != IEEE80211_CHAN_ANYC && ic->ic_curchan != ic->ic_bsschan) { ieee80211_setupcurchan(ic, ic->ic_bsschan); IEEE80211_UNLOCK(ic); ic->ic_set_channel(ic); ieee80211_radiotap_chan_change(ic); IEEE80211_LOCK(ic); } /* clear internal flags and any indication of a pick */ ss_priv->ss_iflags &= ~ISCAN_REP; ss->ss_flags &= ~IEEE80211_SCAN_GOTPICK; /* * If not canceled and scan completed, do post-processing. * If the callback function returns 0, then it wants to * continue/restart scanning. Unfortunately we needed to * notify the driver to end the scan above to avoid having * rx frames alter the scan candidate list. */ if ((ss_priv->ss_iflags & ISCAN_CANCEL) == 0 && !ss->ss_ops->scan_end(ss, vap) && (ss->ss_flags & IEEE80211_SCAN_ONCE) == 0 && ieee80211_time_before(ticks + ss->ss_mindwell, ss_priv->ss_scanend)) { IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN, "%s: done, restart " "[ticks %u, dwell min %lu scanend %lu]\n", __func__, ticks, ss->ss_mindwell, ss_priv->ss_scanend); ss->ss_next = 0; /* reset to beginning */ if (ss->ss_flags & IEEE80211_SCAN_ACTIVE) vap->iv_stats.is_scan_active++; else vap->iv_stats.is_scan_passive++; ss->ss_ops->scan_restart(ss, vap); /* XXX? */ ieee80211_runtask(ic, &ss_priv->ss_scan_start); IEEE80211_UNLOCK(ic); return; } /* past here, scandone is ``true'' if not in bg mode */ if ((ss->ss_flags & IEEE80211_SCAN_BGSCAN) == 0) scandone = 1; IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN, "%s: %s, [ticks %u, dwell min %lu scanend %lu]\n", __func__, scandone ? "done" : "stopped", ticks, ss->ss_mindwell, ss_priv->ss_scanend); /* * Since a cancellation may have occurred during one of the * driver calls (whilst unlocked), update scandone. */ if (scandone == 0 && (ss_priv->ss_iflags & ISCAN_CANCEL) != 0) { /* XXX printf? */ if_printf(vap->iv_ifp, "%s: OOPS! scan cancelled during driver call (2)!\n", __func__); scandone = 1; } scan_done(ss, scandone); }
/* * Restart a previous scan. If the previous scan completed * then we start again using the existing channel list. */ static int ieee80211_swscan_bg_scan(const struct ieee80211_scanner *scan, struct ieee80211vap *vap, int flags) { struct ieee80211com *ic = vap->iv_ic; struct ieee80211_scan_state *ss = ic->ic_scan; /* XXX assert unlocked? */ // IEEE80211_UNLOCK_ASSERT(ic); IEEE80211_LOCK(ic); if ((ic->ic_flags & IEEE80211_F_SCAN) == 0) { u_int duration; /* * Go off-channel for a fixed interval that is large * enough to catch most ap's but short enough that * we can return on-channel before our listen interval * expires. */ duration = IEEE80211_SCAN_OFFCHANNEL; IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN, "%s: %s scan, ticks %u duration %u\n", __func__, ss->ss_flags & IEEE80211_SCAN_ACTIVE ? "active" : "passive", ticks, duration); ieee80211_scan_update_locked(vap, scan); if (ss->ss_ops != NULL) { ss->ss_vap = vap; /* * A background scan does not select a new sta; it * just refreshes the scan cache. Also, indicate * the scan logic should follow the beacon schedule: * we go off-channel and scan for a while, then * return to the bss channel to receive a beacon, * then go off-channel again. All during this time * we notify the ap we're in power save mode. When * the scan is complete we leave power save mode. * If any beacon indicates there are frames pending * for us then we drop out of power save mode * (and background scan) automatically by way of the * usual sta power save logic. */ ss->ss_flags |= IEEE80211_SCAN_NOPICK | IEEE80211_SCAN_BGSCAN | flags ; /* if previous scan completed, restart */ if (ss->ss_next >= ss->ss_last) { if (ss->ss_flags & IEEE80211_SCAN_ACTIVE) vap->iv_stats.is_scan_active++; else vap->iv_stats.is_scan_passive++; /* * NB: beware of the scan cache being flushed; * if the channel list is empty use the * scan_start method to populate it. */ ss->ss_next = 0; if (ss->ss_last != 0) ss->ss_ops->scan_restart(ss, vap); else { ss->ss_ops->scan_start(ss, vap); #ifdef IEEE80211_DEBUG if (ieee80211_msg_scan(vap)) ieee80211_scan_dump(ss); #endif /* IEEE80211_DEBUG */ } } ieee80211_swscan_set_scan_duration(vap, duration); ss->ss_maxdwell = duration; ic->ic_flags |= IEEE80211_F_SCAN; ic->ic_flags_ext |= IEEE80211_FEXT_BGSCAN; ieee80211_runtask(ic, &SCAN_PRIVATE(ss)->ss_scan_start); } else { /* XXX msg+stat */ } } else { IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN, "%s: %s scan already in progress\n", __func__, ss->ss_flags & IEEE80211_SCAN_ACTIVE ? "active" : "passive"); } IEEE80211_UNLOCK(ic); /* NB: racey, does it matter? */ return (ic->ic_flags & IEEE80211_F_SCAN); }
/* * Start a scan unless one is already going. */ static int ieee80211_swscan_start_scan_locked(const struct ieee80211_scanner *scan, struct ieee80211vap *vap, int flags, u_int duration, u_int mindwell, u_int maxdwell, u_int nssid, const struct ieee80211_scan_ssid ssids[]) { struct ieee80211com *ic = vap->iv_ic; struct ieee80211_scan_state *ss = ic->ic_scan; IEEE80211_LOCK_ASSERT(ic); if (ic->ic_flags & IEEE80211_F_CSAPENDING) { IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN, "%s: scan inhibited by pending channel change\n", __func__); } else if ((ic->ic_flags & IEEE80211_F_SCAN) == 0) { IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN, "%s: %s scan, duration %u mindwell %u maxdwell %u, desired mode %s, %s%s%s%s%s%s\n" , __func__ , flags & IEEE80211_SCAN_ACTIVE ? "active" : "passive" , duration, mindwell, maxdwell , ieee80211_phymode_name[vap->iv_des_mode] , flags & IEEE80211_SCAN_FLUSH ? "flush" : "append" , flags & IEEE80211_SCAN_NOPICK ? ", nopick" : "" , flags & IEEE80211_SCAN_NOJOIN ? ", nojoin" : "" , flags & IEEE80211_SCAN_NOBCAST ? ", nobcast" : "" , flags & IEEE80211_SCAN_PICK1ST ? ", pick1st" : "" , flags & IEEE80211_SCAN_ONCE ? ", once" : "" ); ieee80211_scan_update_locked(vap, scan); if (ss->ss_ops != NULL) { if ((flags & IEEE80211_SCAN_NOSSID) == 0) ieee80211_scan_copy_ssid(vap, ss, nssid, ssids); /* NB: top 4 bits for internal use */ ss->ss_flags = flags & 0xfff; if (ss->ss_flags & IEEE80211_SCAN_ACTIVE) vap->iv_stats.is_scan_active++; else vap->iv_stats.is_scan_passive++; if (flags & IEEE80211_SCAN_FLUSH) ss->ss_ops->scan_flush(ss); if (flags & IEEE80211_SCAN_BGSCAN) ic->ic_flags_ext |= IEEE80211_FEXT_BGSCAN; /* Set duration for this particular scan */ ieee80211_swscan_set_scan_duration(vap, duration); ss->ss_next = 0; ss->ss_mindwell = mindwell; ss->ss_maxdwell = maxdwell; /* NB: scan_start must be before the scan runtask */ ss->ss_ops->scan_start(ss, vap); #ifdef IEEE80211_DEBUG if (ieee80211_msg_scan(vap)) ieee80211_scan_dump(ss); #endif /* IEEE80211_DEBUG */ ic->ic_flags |= IEEE80211_F_SCAN; /* Start scan task */ ieee80211_runtask(ic, &SCAN_PRIVATE(ss)->ss_scan_start); } return 1; } else { IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN, "%s: %s scan already in progress\n", __func__, ss->ss_flags & IEEE80211_SCAN_ACTIVE ? "active" : "passive"); } return 0; }
static void scan_task(void *arg, int pending) { #define ISCAN_REP (ISCAN_MINDWELL | ISCAN_DISCARD) struct ieee80211_scan_state *ss = (struct ieee80211_scan_state *) arg; struct ieee80211vap *vap = ss->ss_vap; struct ieee80211com *ic = ss->ss_ic; struct ieee80211_channel *chan; unsigned long maxdwell, scanend; int scandone = 0; IEEE80211_LOCK(ic); if (vap == NULL || (ic->ic_flags & IEEE80211_F_SCAN) == 0 || (SCAN_PRIVATE(ss)->ss_iflags & ISCAN_ABORT)) { /* Cancelled before we started */ goto done; } if (ss->ss_next == ss->ss_last) { IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN, "%s: no channels to scan\n", __func__); goto done; } if (vap->iv_opmode == IEEE80211_M_STA && vap->iv_state == IEEE80211_S_RUN) { if ((vap->iv_bss->ni_flags & IEEE80211_NODE_PWR_MGT) == 0) { /* Enable station power save mode */ ieee80211_sta_pwrsave(vap, 1); /* * Use an 1ms delay so the null data frame has a chance * to go out. * XXX Should use M_TXCB mechanism to eliminate this. */ cv_timedwait(&SCAN_PRIVATE(ss)->ss_scan_cv, IEEE80211_LOCK_OBJ(ic), hz / 1000); if (SCAN_PRIVATE(ss)->ss_iflags & ISCAN_ABORT) goto done; } } scanend = ticks + SCAN_PRIVATE(ss)->ss_duration; IEEE80211_UNLOCK(ic); ic->ic_scan_start(ic); /* notify driver */ IEEE80211_LOCK(ic); for (;;) { scandone = (ss->ss_next >= ss->ss_last) || (SCAN_PRIVATE(ss)->ss_iflags & ISCAN_CANCEL) != 0; if (scandone || (ss->ss_flags & IEEE80211_SCAN_GOTPICK) || (SCAN_PRIVATE(ss)->ss_iflags & ISCAN_ABORT) || time_after(ticks + ss->ss_mindwell, scanend)) break; chan = ss->ss_chans[ss->ss_next++]; /* * Watch for truncation due to the scan end time. */ if (time_after(ticks + ss->ss_maxdwell, scanend)) maxdwell = scanend - ticks; else maxdwell = ss->ss_maxdwell; IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN, "%s: chan %3d%c -> %3d%c [%s, dwell min %lums max %lums]\n", __func__, ieee80211_chan2ieee(ic, ic->ic_curchan), channel_type(ic->ic_curchan), ieee80211_chan2ieee(ic, chan), channel_type(chan), (ss->ss_flags & IEEE80211_SCAN_ACTIVE) && (chan->ic_flags & IEEE80211_CHAN_PASSIVE) == 0 ? "active" : "passive", ticks_to_msecs(ss->ss_mindwell), ticks_to_msecs(maxdwell)); /* * Potentially change channel and phy mode. */ ic->ic_curchan = chan; ic->ic_rt = ieee80211_get_ratetable(chan); IEEE80211_UNLOCK(ic); /* * Perform the channel change and scan unlocked so the driver * may sleep. Once set_channel returns the hardware has * completed the channel change. */ ic->ic_set_channel(ic); ieee80211_radiotap_chan_change(ic); /* * Scan curchan. Drivers for "intelligent hardware" * override ic_scan_curchan to tell the device to do * the work. Otherwise we manage the work outselves; * sending a probe request (as needed), and arming the * timeout to switch channels after maxdwell ticks. * * scan_curchan should only pause for the time required to * prepare/initiate the hardware for the scan (if at all), the * below condvar is used to sleep for the channels dwell time * and allows it to be signalled for abort. */ ic->ic_scan_curchan(ss, maxdwell); IEEE80211_LOCK(ic); SCAN_PRIVATE(ss)->ss_chanmindwell = ticks + ss->ss_mindwell; /* clear mindwell lock and initial channel change flush */ SCAN_PRIVATE(ss)->ss_iflags &= ~ISCAN_REP; if ((SCAN_PRIVATE(ss)->ss_iflags & (ISCAN_CANCEL|ISCAN_ABORT))) continue; /* Wait to be signalled to scan the next channel */ cv_wait(&SCAN_PRIVATE(ss)->ss_scan_cv, IEEE80211_LOCK_OBJ(ic)); } if (SCAN_PRIVATE(ss)->ss_iflags & ISCAN_ABORT) goto done; IEEE80211_UNLOCK(ic); ic->ic_scan_end(ic); /* notify driver */ IEEE80211_LOCK(ic); /* * Record scan complete time. Note that we also do * this when canceled so any background scan will * not be restarted for a while. */ if (scandone) ic->ic_lastscan = ticks; /* return to the bss channel */ if (ic->ic_bsschan != IEEE80211_CHAN_ANYC && ic->ic_curchan != ic->ic_bsschan) { ieee80211_setupcurchan(ic, ic->ic_bsschan); IEEE80211_UNLOCK(ic); ic->ic_set_channel(ic); ieee80211_radiotap_chan_change(ic); IEEE80211_LOCK(ic); } /* clear internal flags and any indication of a pick */ SCAN_PRIVATE(ss)->ss_iflags &= ~ISCAN_REP; ss->ss_flags &= ~IEEE80211_SCAN_GOTPICK; /* * If not canceled and scan completed, do post-processing. * If the callback function returns 0, then it wants to * continue/restart scanning. Unfortunately we needed to * notify the driver to end the scan above to avoid having * rx frames alter the scan candidate list. */ if ((SCAN_PRIVATE(ss)->ss_iflags & ISCAN_CANCEL) == 0 && !ss->ss_ops->scan_end(ss, vap) && (ss->ss_flags & IEEE80211_SCAN_ONCE) == 0 && time_before(ticks + ss->ss_mindwell, scanend)) { IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN, "%s: done, restart " "[ticks %u, dwell min %lu scanend %lu]\n", __func__, ticks, ss->ss_mindwell, scanend); ss->ss_next = 0; /* reset to begining */ if (ss->ss_flags & IEEE80211_SCAN_ACTIVE) vap->iv_stats.is_scan_active++; else vap->iv_stats.is_scan_passive++; ss->ss_ops->scan_restart(ss, vap); /* XXX? */ ieee80211_runtask(ic, &SCAN_PRIVATE(ss)->ss_scan_task); IEEE80211_UNLOCK(ic); return; } /* past here, scandone is ``true'' if not in bg mode */ if ((ss->ss_flags & IEEE80211_SCAN_BGSCAN) == 0) scandone = 1; IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN, "%s: %s, [ticks %u, dwell min %lu scanend %lu]\n", __func__, scandone ? "done" : "stopped", ticks, ss->ss_mindwell, scanend); /* * Clear the SCAN bit first in case frames are * pending on the station power save queue. If * we defer this then the dispatch of the frames * may generate a request to cancel scanning. */ done: ic->ic_flags &= ~IEEE80211_F_SCAN; /* * Drop out of power save mode when a scan has * completed. If this scan was prematurely terminated * because it is a background scan then don't notify * the ap; we'll either return to scanning after we * receive the beacon frame or we'll drop out of power * save mode because the beacon indicates we have frames * waiting for us. */ if (scandone) { ieee80211_sta_pwrsave(vap, 0); if (ss->ss_next >= ss->ss_last) { ieee80211_notify_scan_done(vap); ic->ic_flags_ext &= ~IEEE80211_FEXT_BGSCAN; } } SCAN_PRIVATE(ss)->ss_iflags &= ~(ISCAN_CANCEL|ISCAN_ABORT); ss->ss_flags &= ~(IEEE80211_SCAN_ONCE | IEEE80211_SCAN_PICK1ST); IEEE80211_UNLOCK(ic); #undef ISCAN_REP }