Create a taskqueue for each wireless interface which provides a serialised

sleepable context for net80211 driver callbacks. This removes the need for USB
and firmware based drivers to roll their own code to defer the chip programming
for state changes, scan requests, channel changes and mcast/promisc updates.
When a driver callback completes the hardware state is now guaranteed to have
been updated and is in sync with net80211 layer.

This nukes around 1300 lines of code from the wireless device drivers making
them more readable and less race prone.

The net80211 layer has been updated as follows
 - all state/channel changes are serialised on the taskqueue.
 - ieee80211_new_state() always queues and can now be called from any context
 - scanning runs from a single taskq function and executes to completion. driver
   callbacks are synchronous so the channel, phy mode and rx filters are
   guaranteed to be set in hardware before probe request frames are
   transmitted.

Help and contributions from Sam Leffler.

Reviewed by:	sam
This commit is contained in:
Andrew Thompson 2009-05-02 15:14:18 +00:00
parent a40d9024df
commit 5efea30f03
31 changed files with 1136 additions and 2230 deletions

View File

@ -50,7 +50,6 @@ __FBSDID("$FreeBSD$");
#include <sys/proc.h>
#include <sys/sysctl.h>
#include <sys/kthread.h>
#include <sys/taskqueue.h>
#include <net/if.h>
@ -173,7 +172,7 @@ static int ndis_newstate (struct ieee80211vap *, enum ieee80211_state,
int);
static int ndis_nettype_chan (uint32_t);
static int ndis_nettype_mode (uint32_t);
static void ndis_scan (void *, int);
static void ndis_scan (void *);
static void ndis_scan_results (struct ndis_softc *);
static void ndis_scan_start (struct ieee80211com *);
static void ndis_scan_end (struct ieee80211com *);
@ -184,8 +183,6 @@ static void ndis_init (void *);
static void ndis_stop (struct ndis_softc *);
static int ndis_ifmedia_upd (struct ifnet *);
static void ndis_ifmedia_sts (struct ifnet *, struct ifmediareq *);
static void ndis_auth (void *, int);
static void ndis_assoc (void *, int);
static int ndis_get_assoc (struct ndis_softc *, ndis_wlan_bssid_ex **);
static int ndis_probe_offload (struct ndis_softc *);
static int ndis_set_offload (struct ndis_softc *);
@ -741,13 +738,7 @@ ndis_attach(dev)
uint32_t arg;
int r;
sc->ndis_tq = taskqueue_create("nids_taskq", M_NOWAIT | M_ZERO,
taskqueue_thread_enqueue, &sc->ndis_tq);
taskqueue_start_threads(&sc->ndis_tq, 1, PI_NET, "%s taskq",
device_get_nameunit(dev));
TASK_INIT(&sc->ndis_scantask, 0, ndis_scan, sc);
TASK_INIT(&sc->ndis_authtask, 0, ndis_auth, sc);
TASK_INIT(&sc->ndis_assoctask, 0, ndis_assoc, sc);
callout_init(&sc->ndis_scan_callout, CALLOUT_MPSAFE);
ifp->if_ioctl = ndis_ioctl_80211;
ic->ic_ifp = ifp;
@ -1054,12 +1045,6 @@ ndis_detach(dev)
} else
NDIS_UNLOCK(sc);
if (sc->ndis_80211) {
taskqueue_drain(sc->ndis_tq, &sc->ndis_scantask);
taskqueue_drain(sc->ndis_tq, &sc->ndis_authtask);
taskqueue_drain(sc->ndis_tq, &sc->ndis_assoctask);
}
if (sc->ndis_tickitem != NULL)
IoFreeWorkItem(sc->ndis_tickitem);
if (sc->ndis_startitem != NULL)
@ -1121,8 +1106,6 @@ ndis_detach(dev)
if (sc->ndis_iftype == PCIBus)
bus_dma_tag_destroy(sc->ndis_parent_tag);
if (sc->ndis_80211)
taskqueue_free(sc->ndis_tq);
return(0);
}
@ -2418,30 +2401,6 @@ ndis_setstate_80211(sc)
}
static void
ndis_auth(void *arg, int npending)
{
struct ndis_softc *sc = arg;
struct ifnet *ifp = sc->ifp;
struct ieee80211com *ic = ifp->if_l2com;
struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
vap->iv_state = IEEE80211_S_AUTH;
ndis_auth_and_assoc(sc, vap);
}
static void
ndis_assoc(void *arg, int npending)
{
struct ndis_softc *sc = arg;
struct ifnet *ifp = sc->ifp;
struct ieee80211com *ic = ifp->if_l2com;
struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
vap->iv_state = IEEE80211_S_ASSOC;
ndis_auth_and_assoc(sc, vap);
}
static void
ndis_auth_and_assoc(sc, vap)
struct ndis_softc *sc;
@ -2656,9 +2615,6 @@ ndis_auth_and_assoc(sc, vap)
if (rval)
device_printf (sc->ndis_dev, "set ssid failed: %d\n", rval);
if (vap->iv_state == IEEE80211_S_AUTH)
ieee80211_new_state(vap, IEEE80211_S_ASSOC, 0);
return;
}
@ -3304,13 +3260,18 @@ ndis_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
return nvp->newstate(vap, nstate, arg);
case IEEE80211_S_ASSOC:
if (ostate != IEEE80211_S_AUTH) {
taskqueue_enqueue(sc->ndis_tq, &sc->ndis_assoctask);
return EINPROGRESS;
IEEE80211_UNLOCK(ic);
ndis_auth_and_assoc(sc, vap);
IEEE80211_LOCK(ic);
}
break;
case IEEE80211_S_AUTH:
taskqueue_enqueue(sc->ndis_tq, &sc->ndis_authtask);
return EINPROGRESS;
IEEE80211_UNLOCK(ic);
ndis_auth_and_assoc(sc, vap);
if (vap->iv_state == IEEE80211_S_AUTH) /* XXX */
ieee80211_new_state(vap, IEEE80211_S_ASSOC, 0);
IEEE80211_LOCK(ic);
break;
default:
break;
}
@ -3318,54 +3279,18 @@ ndis_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
}
static void
ndis_scan(void *arg, int npending)
ndis_scan(void *arg)
{
struct ndis_softc *sc = arg;
struct ieee80211com *ic;
struct ieee80211vap *vap;
struct ieee80211_scan_state *ss;
ndis_80211_ssid ssid;
int error, len;
ic = sc->ifp->if_l2com;
ss = ic->ic_scan;
vap = TAILQ_FIRST(&ic->ic_vaps);
if (!NDIS_INITIALIZED(sc)) {
DPRINTF(("%s: scan aborted\n", __func__));
ieee80211_cancel_scan(vap);
return;
}
len = sizeof(ssid);
bzero((char *)&ssid, len);
if (ss->ss_nssid == 0)
ssid.ns_ssidlen = 1;
else {
/* Perform a directed scan */
ssid.ns_ssidlen = ss->ss_ssid[0].len;
bcopy(ss->ss_ssid[0].ssid, ssid.ns_ssid, ssid.ns_ssidlen);
}
error = ndis_set_info(sc, OID_802_11_SSID, &ssid, &len);
if (error)
DPRINTF(("%s: set ESSID failed\n", __func__));
len = 0;
error = ndis_set_info(sc, OID_802_11_BSSID_LIST_SCAN,
NULL, &len);
if (error) {
DPRINTF(("%s: scan command failed\n", __func__));
ieee80211_cancel_scan(vap);
return;
}
pause("ssidscan", hz * 3);
if (!NDIS_INITIALIZED(sc))
/* The interface was downed while we were sleeping */
return;
NDIS_LOCK(sc);
ndis_scan_results(sc);
NDIS_UNLOCK(sc);
ieee80211_scan_done(vap);
}
@ -3496,8 +3421,48 @@ ndis_scan_start(struct ieee80211com *ic)
{
struct ifnet *ifp = ic->ic_ifp;
struct ndis_softc *sc = ifp->if_softc;
struct ieee80211vap *vap;
struct ieee80211_scan_state *ss;
ndis_80211_ssid ssid;
int error, len;
taskqueue_enqueue(sc->ndis_tq, &sc->ndis_scantask);
ss = ic->ic_scan;
vap = TAILQ_FIRST(&ic->ic_vaps);
NDIS_LOCK(sc);
if (!NDIS_INITIALIZED(sc)) {
DPRINTF(("%s: scan aborted\n", __func__));
NDIS_UNLOCK(sc);
ieee80211_cancel_scan(vap);
return;
}
len = sizeof(ssid);
bzero((char *)&ssid, len);
if (ss->ss_nssid == 0)
ssid.ns_ssidlen = 1;
else {
/* Perform a directed scan */
ssid.ns_ssidlen = ss->ss_ssid[0].len;
bcopy(ss->ss_ssid[0].ssid, ssid.ns_ssid, ssid.ns_ssidlen);
}
error = ndis_set_info(sc, OID_802_11_SSID, &ssid, &len);
if (error)
DPRINTF(("%s: set ESSID failed\n", __func__));
len = 0;
error = ndis_set_info(sc, OID_802_11_BSSID_LIST_SCAN,
NULL, &len);
if (error) {
DPRINTF(("%s: scan command failed\n", __func__));
NDIS_UNLOCK(sc);
ieee80211_cancel_scan(vap);
return;
}
NDIS_UNLOCK(sc);
/* Set a timer to collect the results */
callout_reset(&sc->ndis_scan_callout, hz * 3, ndis_scan, sc);
}
static void

View File

@ -180,6 +180,7 @@ struct ndis_softc {
ndis_miniport_block *ndis_block;
ndis_miniport_characteristics *ndis_chars;
interface_type ndis_type;
struct callout ndis_scan_callout;
struct callout ndis_stat_callout;
int ndis_maxpkts;
ndis_oid *ndis_oids;
@ -219,10 +220,6 @@ struct ndis_softc {
struct ifqueue ndis_rxqueue;
kspin_lock ndis_rxlock;
struct taskqueue *ndis_tq; /* private task queue */
struct task ndis_scantask;
struct task ndis_authtask;
struct task ndis_assoctask;
int (*ndis_newstate)(struct ieee80211com *,
enum ieee80211_state, int);
int ndis_tx_timer;

View File

@ -118,10 +118,6 @@ static void ipw_media_status(struct ifnet *, struct ifmediareq *);
static int ipw_newstate(struct ieee80211vap *, enum ieee80211_state, int);
static uint16_t ipw_read_prom_word(struct ipw_softc *, uint8_t);
static void ipw_rx_cmd_intr(struct ipw_softc *, struct ipw_soft_buf *);
static void ipw_assocsuccess(void *, int);
static void ipw_assocfailed(void *, int);
static void ipw_scandone(void *, int);
static void ipw_bmiss(void *, int);
static void ipw_rx_newstate_intr(struct ipw_softc *, struct ipw_soft_buf *);
static void ipw_rx_data_intr(struct ipw_softc *, struct ipw_status *,
struct ipw_soft_bd *, struct ipw_soft_buf *);
@ -147,8 +143,8 @@ static int ipw_reset(struct ipw_softc *);
static int ipw_load_ucode(struct ipw_softc *, const char *, int);
static int ipw_load_firmware(struct ipw_softc *, const char *, int);
static int ipw_config(struct ipw_softc *);
static void ipw_assoc_task(void *, int);
static void ipw_disassoc_task(void *, int);
static void ipw_assoc(struct ieee80211com *, struct ieee80211vap *);
static void ipw_disassoc(struct ieee80211com *, struct ieee80211vap *);
static void ipw_init_task(void *, int);
static void ipw_init(void *);
static void ipw_init_locked(struct ipw_softc *);
@ -166,7 +162,6 @@ static void ipw_read_mem_1(struct ipw_softc *, bus_size_t, uint8_t *,
#endif
static void ipw_write_mem_1(struct ipw_softc *, bus_size_t,
const uint8_t *, bus_size_t);
static void ipw_scan_task(void *, int);
static int ipw_scan(struct ipw_softc *);
static void ipw_scan_start(struct ieee80211com *);
static void ipw_scan_end(struct ieee80211com *);
@ -239,8 +234,6 @@ ipw_attach(device_t dev)
MTX_DEF | MTX_RECURSE);
TASK_INIT(&sc->sc_init_task, 0, ipw_init_task, sc);
TASK_INIT(&sc->sc_scan_task, 0, ipw_scan_task, sc);
TASK_INIT(&sc->sc_bmiss_task, 0, ipw_bmiss, sc);
callout_init_mtx(&sc->sc_wdtimer, &sc->sc_mtx, 0);
if (pci_get_powerstate(dev) != PCI_POWERSTATE_D0) {
@ -417,9 +410,7 @@ ipw_detach(device_t dev)
ieee80211_ifdetach(ic);
callout_drain(&sc->sc_wdtimer);
taskqueue_drain(taskqueue_swi, &sc->sc_init_task);
taskqueue_drain(taskqueue_swi, &sc->sc_scan_task);
taskqueue_drain(taskqueue_swi, &sc->sc_bmiss_task);
ieee80211_draintask(ic, &sc->sc_init_task);
ipw_release(sc);
@ -511,12 +502,6 @@ ipw_vap_create(struct ieee80211com *ic,
return NULL;
vap = &ivp->vap;
TASK_INIT(&ivp->assoc_task, 0, ipw_assoc_task, vap);
TASK_INIT(&ivp->disassoc_task, 0, ipw_disassoc_task, vap);
TASK_INIT(&ivp->assoc_success_task, 0, ipw_assocsuccess, vap);
TASK_INIT(&ivp->assoc_failed_task, 0, ipw_assocfailed, vap);
TASK_INIT(&ivp->scandone_task, 0, ipw_scandone, vap);
ieee80211_vap_setup(ic, vap, name, unit, opmode, flags, bssid, mac);
/* override with driver methods */
ivp->newstate = vap->iv_newstate;
@ -894,11 +879,15 @@ ipw_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
struct ieee80211com *ic = vap->iv_ic;
struct ifnet *ifp = ic->ic_ifp;
struct ipw_softc *sc = ifp->if_softc;
enum ieee80211_state ostate;
DPRINTF(("%s: %s -> %s flags 0x%x\n", __func__,
ieee80211_state_name[vap->iv_state],
ieee80211_state_name[nstate], sc->flags));
ostate = vap->iv_state;
IEEE80211_UNLOCK(ic);
switch (nstate) {
case IEEE80211_S_RUN:
if (ic->ic_opmode == IEEE80211_M_IBSS) {
@ -910,39 +899,33 @@ ipw_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
* AUTH -> RUN transition and we want to do nothing.
* This is all totally bogus and needs to be redone.
*/
if (vap->iv_state == IEEE80211_S_SCAN) {
taskqueue_enqueue(taskqueue_swi,
&IPW_VAP(vap)->assoc_task);
return EINPROGRESS;
}
if (ostate == IEEE80211_S_SCAN)
ipw_assoc(ic, vap);
}
break;
case IEEE80211_S_INIT:
if (sc->flags & IPW_FLAG_ASSOCIATED)
taskqueue_enqueue(taskqueue_swi,
&IPW_VAP(vap)->disassoc_task);
ipw_disassoc(ic, vap);
break;
case IEEE80211_S_AUTH:
taskqueue_enqueue(taskqueue_swi, &IPW_VAP(vap)->assoc_task);
return EINPROGRESS;
ipw_assoc(ic, vap);
break;
case IEEE80211_S_ASSOC:
/*
* If we are not transitioning from AUTH the resend the
* association request.
*/
if (vap->iv_state != IEEE80211_S_AUTH) {
taskqueue_enqueue(taskqueue_swi,
&IPW_VAP(vap)->assoc_task);
return EINPROGRESS;
}
if (ostate != IEEE80211_S_AUTH)
ipw_assoc(ic, vap);
break;
default:
break;
}
IEEE80211_LOCK(ic);
return ivp->newstate(vap, nstate, arg);
}
@ -1019,38 +1002,6 @@ ipw_rx_cmd_intr(struct ipw_softc *sc, struct ipw_soft_buf *sbuf)
wakeup(sc);
}
static void
ipw_assocsuccess(void *arg, int npending)
{
struct ieee80211vap *vap = arg;
ieee80211_new_state(vap, IEEE80211_S_RUN, -1);
}
static void
ipw_assocfailed(void *arg, int npending)
{
struct ieee80211vap *vap = arg;
ieee80211_new_state(vap, IEEE80211_S_SCAN, -1);
}
static void
ipw_scandone(void *arg, int npending)
{
struct ieee80211vap *vap = arg;
ieee80211_scan_done(vap);
}
static void
ipw_bmiss(void *arg, int npending)
{
struct ieee80211com *ic = arg;
ieee80211_beacon_miss(ic);
}
static void
ipw_rx_newstate_intr(struct ipw_softc *sc, struct ipw_soft_buf *sbuf)
{
@ -1076,8 +1027,7 @@ ipw_rx_newstate_intr(struct ipw_softc *sc, struct ipw_soft_buf *sbuf)
}
sc->flags &= ~IPW_FLAG_ASSOCIATING;
sc->flags |= IPW_FLAG_ASSOCIATED;
taskqueue_enqueue(taskqueue_swi,
&IPW_VAP(vap)->assoc_success_task);
ieee80211_new_state(vap, IEEE80211_S_RUN, -1);
break;
case IPW_STATE_SCANNING:
@ -1091,7 +1041,7 @@ ipw_rx_newstate_intr(struct ipw_softc *sc, struct ipw_soft_buf *sbuf)
*/
if (sc->flags & IPW_FLAG_ASSOCIATED) {
/* XXX probably need to issue disassoc to fw */
taskqueue_enqueue(taskqueue_swi, &sc->sc_bmiss_task);
ieee80211_beacon_miss(ic);
}
break;
@ -1110,8 +1060,7 @@ ipw_rx_newstate_intr(struct ipw_softc *sc, struct ipw_soft_buf *sbuf)
break;
}
if (sc->flags & IPW_FLAG_SCANNING) {
taskqueue_enqueue(taskqueue_swi,
&IPW_VAP(vap)->scandone_task);
ieee80211_scan_done(vap);
sc->flags &= ~IPW_FLAG_SCANNING;
sc->sc_scan_timer = 0;
}
@ -1122,8 +1071,7 @@ ipw_rx_newstate_intr(struct ipw_softc *sc, struct ipw_soft_buf *sbuf)
IEEESTATE(vap), sc->flags));
sc->flags &= ~(IPW_FLAG_ASSOCIATING | IPW_FLAG_ASSOCIATED);
if (vap->iv_state == IEEE80211_S_RUN)
taskqueue_enqueue(taskqueue_swi,
&IPW_VAP(vap)->assoc_failed_task);
ieee80211_new_state(vap, IEEE80211_S_SCAN, -1);
break;
case IPW_STATE_DISABLED:
@ -1431,6 +1379,16 @@ ipw_tx_intr(struct ipw_softc *sc)
ipw_start_locked(ifp);
}
static void
ipw_fatal_error_intr(struct ipw_softc *sc)
{
struct ifnet *ifp = sc->sc_ifp;
struct ieee80211com *ic = ifp->if_l2com;
device_printf(sc->sc_dev, "firmware error\n");
ieee80211_runtask(ic, &sc->sc_init_task);
}
static void
ipw_intr(void *arg)
{
@ -1440,10 +1398,9 @@ ipw_intr(void *arg)
IPW_LOCK(sc);
if ((r = CSR_READ_4(sc, IPW_CSR_INTR)) == 0 || r == 0xffffffff) {
IPW_UNLOCK(sc);
return;
}
r = CSR_READ_4(sc, IPW_CSR_INTR);
if (r == 0 || r == 0xffffffff)
goto done;
/* disable interrupts */
CSR_WRITE_4(sc, IPW_CSR_INTR_MASK, 0);
@ -1452,9 +1409,8 @@ ipw_intr(void *arg)
CSR_WRITE_4(sc, IPW_CSR_INTR, r);
if (r & (IPW_INTR_FATAL_ERROR | IPW_INTR_PARITY_ERROR)) {
device_printf(sc->sc_dev, "firmware error\n");
taskqueue_enqueue(taskqueue_swi, &sc->sc_init_task);
r = 0; /* don't process more interrupts */
ipw_fatal_error_intr(sc);
goto done;
}
if (r & IPW_INTR_FW_INIT_DONE)
@ -1468,7 +1424,7 @@ ipw_intr(void *arg)
/* re-enable interrupts */
CSR_WRITE_4(sc, IPW_CSR_INTR_MASK, IPW_INTR_MASK);
done:
IPW_UNLOCK(sc);
}
@ -2181,20 +2137,6 @@ ipw_setscanopts(struct ipw_softc *sc, uint32_t chanmask, uint32_t flags)
return ipw_cmd(sc, IPW_CMD_SET_SCAN_OPTIONS, &opts, sizeof(opts));
}
/*
* Handler for sc_scan_task. This is a simple wrapper around ipw_scan().
*/
static void
ipw_scan_task(void *context, int pending)
{
struct ipw_softc *sc = context;
IPW_LOCK_DECL;
IPW_LOCK(sc);
ipw_scan(sc);
IPW_UNLOCK(sc);
}
static int
ipw_scan(struct ipw_softc *sc)
{
@ -2258,11 +2200,9 @@ ipw_setchannel(struct ipw_softc *sc, struct ieee80211_channel *chan)
}
static void
ipw_assoc_task(void *context, int pending)
ipw_assoc(struct ieee80211com *ic, struct ieee80211vap *vap)
{
struct ieee80211vap *vap = context;
struct ifnet *ifp = vap->iv_ic->ic_ifp;
struct ieee80211com *ic = ifp->if_l2com;
struct ipw_softc *sc = ifp->if_softc;
struct ieee80211_node *ni = vap->iv_bss;
struct ipw_security security;
@ -2353,9 +2293,8 @@ ipw_assoc_task(void *context, int pending)
}
static void
ipw_disassoc_task(void *context, int pending)
ipw_disassoc(struct ieee80211com *ic, struct ieee80211vap *vap)
{
struct ieee80211vap *vap = context;
struct ifnet *ifp = vap->iv_ic->ic_ifp;
struct ieee80211_node *ni = vap->iv_bss;
struct ipw_softc *sc = ifp->if_softc;
@ -2729,8 +2668,7 @@ ipw_scan_start(struct ieee80211com *ic)
IPW_LOCK_DECL;
IPW_LOCK(sc);
if (!(sc->flags & IPW_FLAG_SCANNING))
taskqueue_enqueue(taskqueue_swi, &sc->sc_scan_task);
ipw_scan(sc);
IPW_UNLOCK(sc);
}

View File

@ -78,11 +78,6 @@ struct ipw_tx_radiotap_header {
struct ipw_vap {
struct ieee80211vap vap;
struct task assoc_task;
struct task disassoc_task;
struct task assoc_success_task;
struct task assoc_failed_task;
struct task scandone_task;
int (*newstate)(struct ieee80211vap *,
enum ieee80211_state, int);
@ -95,9 +90,6 @@ struct ipw_softc {
struct mtx sc_mtx;
struct task sc_init_task;
struct task sc_scan_task;
struct task sc_chan_task;
struct task sc_bmiss_task;
struct callout sc_wdtimer; /* watchdog timer */
uint32_t flags;

View File

@ -158,9 +158,6 @@ static int iwi_wme_update(struct ieee80211com *);
static uint16_t iwi_read_prom_word(struct iwi_softc *, uint8_t);
static void iwi_frame_intr(struct iwi_softc *, struct iwi_rx_data *, int,
struct iwi_frame *);
static void iwi_authsuccess(void *, int);
static void iwi_assocsuccess(void *, int);
static void iwi_assocfailed(void *, int);
static void iwi_notification_intr(struct iwi_softc *, struct iwi_notif *);
static void iwi_rx_intr(struct iwi_softc *);
static void iwi_tx_intr(struct iwi_softc *, struct iwi_tx_ring *);
@ -186,16 +183,11 @@ static void iwi_put_firmware(struct iwi_softc *);
static int iwi_scanchan(struct iwi_softc *, unsigned long, int);
static void iwi_scan_start(struct ieee80211com *);
static void iwi_scan_end(struct ieee80211com *);
static void iwi_scanabort(void *, int);
static void iwi_set_channel(struct ieee80211com *);
static void iwi_scan_curchan(struct ieee80211_scan_state *, unsigned long maxdwell);
#if 0
static void iwi_scan_allchan(struct ieee80211com *, unsigned long maxdwell);
#endif
static void iwi_scan_mindwell(struct ieee80211_scan_state *);
static void iwi_ops(void *, int);
static int iwi_queue_cmd(struct iwi_softc *, int, unsigned long);
static int iwi_auth_and_assoc(struct iwi_softc *, struct ieee80211vap *);
static void iwi_disassoc(void *, int);
static int iwi_disassociate(struct iwi_softc *, int quiet);
static void iwi_init_locked(struct iwi_softc *);
static void iwi_init(void *);
@ -292,24 +284,14 @@ iwi_attach(device_t dev)
ic = ifp->if_l2com;
IWI_LOCK_INIT(sc);
IWI_CMD_LOCK_INIT(sc);
sc->sc_unr = new_unrhdr(1, IWI_MAX_IBSSNODE-1, &sc->sc_mtx);
sc->sc_tq = taskqueue_create("iwi_taskq", M_NOWAIT | M_ZERO,
taskqueue_thread_enqueue, &sc->sc_tq);
taskqueue_start_threads(&sc->sc_tq, 1, PI_NET, "%s taskq",
device_get_nameunit(dev));
sc->sc_tq2 = taskqueue_create("iwi_taskq2", M_NOWAIT | M_ZERO,
taskqueue_thread_enqueue, &sc->sc_tq2);
taskqueue_start_threads(&sc->sc_tq2, 1, PI_NET, "%s taskq2",
device_get_nameunit(dev));
TASK_INIT(&sc->sc_radiontask, 0, iwi_radio_on, sc);
TASK_INIT(&sc->sc_radiofftask, 0, iwi_radio_off, sc);
TASK_INIT(&sc->sc_restarttask, 0, iwi_restart, sc);
TASK_INIT(&sc->sc_opstask, 0, iwi_ops, sc);
TASK_INIT(&sc->sc_scanaborttask, 0, iwi_scanabort, sc);
TASK_INIT(&sc->sc_disassoctask, 0, iwi_disassoc, sc);
callout_init_mtx(&sc->sc_wdtimer, &sc->sc_mtx, 0);
callout_init_mtx(&sc->sc_rftimer, &sc->sc_mtx, 0);
@ -483,8 +465,10 @@ iwi_detach(device_t dev)
ieee80211_ifdetach(ic);
/* NB: do early to drain any pending tasks */
taskqueue_free(sc->sc_tq);
taskqueue_free(sc->sc_tq2);
ieee80211_draintask(ic, &sc->sc_radiontask);
ieee80211_draintask(ic, &sc->sc_radiofftask);
ieee80211_draintask(ic, &sc->sc_restarttask);
ieee80211_draintask(ic, &sc->sc_disassoctask);
iwi_put_firmware(sc);
iwi_release_fw_dma(sc);
@ -504,7 +488,6 @@ iwi_detach(device_t dev)
delete_unrhdr(sc->sc_unr);
IWI_LOCK_DESTROY(sc);
IWI_CMD_LOCK_DESTROY(sc);
if_free(ifp);
@ -552,10 +535,6 @@ iwi_vap_create(struct ieee80211com *ic,
ivp->iwi_newstate = vap->iv_newstate;
vap->iv_newstate = iwi_newstate;
TASK_INIT(&ivp->iwi_authsuccess_task, 0, iwi_authsuccess, vap);
TASK_INIT(&ivp->iwi_assocsuccess_task, 0, iwi_assocsuccess, vap);
TASK_INIT(&ivp->iwi_assocfailed_task, 0, iwi_assocfailed, vap);
/* complete setup */
ieee80211_vap_attach(vap, ieee80211_media_change, iwi_media_status);
ic->ic_opmode = opmode;
@ -987,21 +966,21 @@ iwi_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
ieee80211_state_name[vap->iv_state],
ieee80211_state_name[nstate], sc->flags));
IEEE80211_UNLOCK(ic);
IWI_LOCK(sc);
switch (nstate) {
case IEEE80211_S_INIT:
IWI_LOCK(sc);
/*
* NB: don't try to do this if iwi_stop_master has
* shutdown the firmware and disabled interrupts.
*/
if (vap->iv_state == IEEE80211_S_RUN &&
(sc->flags & IWI_FLAG_FW_INITED))
iwi_queue_cmd(sc, IWI_DISASSOC, 1);
IWI_UNLOCK(sc);
iwi_disassociate(sc, 0);
break;
case IEEE80211_S_AUTH:
iwi_queue_cmd(sc, IWI_AUTH, arg);
return EINPROGRESS;
iwi_auth_and_assoc(sc, vap);
break;
case IEEE80211_S_RUN:
if (vap->iv_opmode == IEEE80211_M_IBSS &&
vap->iv_state == IEEE80211_S_SCAN) {
@ -1013,8 +992,7 @@ iwi_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
* AUTH -> RUN transition and we want to do nothing.
* This is all totally bogus and needs to be redone.
*/
iwi_queue_cmd(sc, IWI_ASSOC, 0);
return EINPROGRESS;
iwi_auth_and_assoc(sc, vap);
}
break;
case IEEE80211_S_ASSOC:
@ -1025,11 +1003,13 @@ iwi_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
*/
if (vap->iv_state == IEEE80211_S_AUTH)
break;
iwi_queue_cmd(sc, IWI_ASSOC, arg);
return EINPROGRESS;
iwi_auth_and_assoc(sc, vap);
break;
default:
break;
}
IWI_UNLOCK(sc);
IEEE80211_LOCK(ic);
return ivp->iwi_newstate(vap, nstate, arg);
}
@ -1106,6 +1086,7 @@ static int
iwi_wme_update(struct ieee80211com *ic)
{
struct iwi_softc *sc = ic->ic_ifp->if_softc;
struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
/*
* We may be called to update the WME parameters in
@ -1115,7 +1096,9 @@ iwi_wme_update(struct ieee80211com *ic)
* will get sent down to the adapter as part of the
* work iwi_auth_and_assoc does.
*/
return iwi_queue_cmd(sc, IWI_SET_WME, 0);
if (vap->iv_state == IEEE80211_S_RUN)
(void) iwi_wme_setparams(sc, ic);
return (0);
}
static int
@ -1388,30 +1371,6 @@ iwi_checkforqos(struct ieee80211vap *vap,
* Task queue callbacks for iwi_notification_intr used to avoid LOR's.
*/
static void
iwi_authsuccess(void *arg, int npending)
{
struct ieee80211vap *vap = arg;
ieee80211_new_state(vap, IEEE80211_S_ASSOC, -1);
}
static void
iwi_assocsuccess(void *arg, int npending)
{
struct ieee80211vap *vap = arg;
ieee80211_new_state(vap, IEEE80211_S_RUN, -1);
}
static void
iwi_assocfailed(void *arg, int npending)
{
struct ieee80211vap *vap = arg;
ieee80211_new_state(vap, IEEE80211_S_SCAN, -1);
}
static void
iwi_notification_intr(struct iwi_softc *sc, struct iwi_notif *notif)
{
@ -1454,8 +1413,7 @@ iwi_notification_intr(struct iwi_softc *sc, struct iwi_notif *notif)
switch (auth->state) {
case IWI_AUTH_SUCCESS:
DPRINTFN(2, ("Authentication succeeeded\n"));
taskqueue_enqueue(taskqueue_swi,
&IWI_VAP(vap)->iwi_authsuccess_task);
ieee80211_new_state(vap, IEEE80211_S_ASSOC, -1);
break;
case IWI_AUTH_FAIL:
/*
@ -1472,8 +1430,7 @@ iwi_notification_intr(struct iwi_softc *sc, struct iwi_notif *notif)
DPRINTFN(2, ("Deauthenticated\n"));
vap->iv_stats.is_rx_deauth++;
}
taskqueue_enqueue(taskqueue_swi,
&IWI_VAP(vap)->iwi_assocfailed_task);
ieee80211_new_state(vap, IEEE80211_S_SCAN, -1);
break;
case IWI_AUTH_SENT_1:
case IWI_AUTH_RECV_2:
@ -1506,8 +1463,7 @@ iwi_notification_intr(struct iwi_softc *sc, struct iwi_notif *notif)
iwi_checkforqos(vap,
(const struct ieee80211_frame *)(assoc+1),
le16toh(notif->len) - sizeof(*assoc));
taskqueue_enqueue(taskqueue_swi,
&IWI_VAP(vap)->iwi_assocsuccess_task);
ieee80211_new_state(vap, IEEE80211_S_RUN, -1);
break;
case IWI_ASSOC_INIT:
sc->flags &= ~IWI_FLAG_ASSOCIATED;
@ -1515,16 +1471,14 @@ iwi_notification_intr(struct iwi_softc *sc, struct iwi_notif *notif)
case IWI_FW_ASSOCIATING:
DPRINTFN(2, ("Association failed\n"));
IWI_STATE_END(sc, IWI_FW_ASSOCIATING);
taskqueue_enqueue(taskqueue_swi,
&IWI_VAP(vap)->iwi_assocfailed_task);
ieee80211_new_state(vap, IEEE80211_S_SCAN, -1);
break;
case IWI_FW_DISASSOCIATING:
DPRINTFN(2, ("Dissassociated\n"));
IWI_STATE_END(sc, IWI_FW_DISASSOCIATING);
vap->iv_stats.is_rx_disassoc++;
taskqueue_enqueue(taskqueue_swi,
&IWI_VAP(vap)->iwi_assocfailed_task);
ieee80211_new_state(vap, IEEE80211_S_SCAN, -1);
break;
}
break;
@ -1563,7 +1517,7 @@ iwi_notification_intr(struct iwi_softc *sc, struct iwi_notif *notif)
* to disassociate and then on completion we'll
* kick the state machine to scan.
*/
iwi_queue_cmd(sc, IWI_DISASSOC, 1);
ieee80211_runtask(ic, &sc->sc_disassoctask);
}
}
break;
@ -1663,6 +1617,29 @@ iwi_tx_intr(struct iwi_softc *sc, struct iwi_tx_ring *txq)
iwi_start_locked(ifp);
}
static void
iwi_fatal_error_intr(struct iwi_softc *sc)
{
struct ifnet *ifp = sc->sc_ifp;
struct ieee80211com *ic = ifp->if_l2com;
device_printf(sc->sc_dev, "firmware error\n");
ieee80211_runtask(ic, &sc->sc_restarttask);
sc->flags &= ~IWI_FLAG_BUSY;
sc->sc_busy_timer = 0;
wakeup(sc);
}
static void
iwi_radio_off_intr(struct iwi_softc *sc)
{
struct ifnet *ifp = sc->sc_ifp;
struct ieee80211com *ic = ifp->if_l2com;
ieee80211_runtask(ic, &sc->sc_radiofftask);
}
static void
iwi_intr(void *arg)
{
@ -1681,12 +1658,8 @@ iwi_intr(void *arg)
CSR_WRITE_4(sc, IWI_CSR_INTR, r);
if (r & IWI_INTR_FATAL_ERROR) {
device_printf(sc->sc_dev, "firmware error\n");
taskqueue_enqueue(sc->sc_tq2, &sc->sc_restarttask);
sc->flags &= ~IWI_FLAG_BUSY;
sc->sc_busy_timer = 0;
wakeup(sc);
iwi_fatal_error_intr(sc);
goto done;
}
if (r & IWI_INTR_FW_INITED) {
@ -1695,7 +1668,7 @@ iwi_intr(void *arg)
}
if (r & IWI_INTR_RADIO_OFF)
taskqueue_enqueue(sc->sc_tq, &sc->sc_radiofftask);
iwi_radio_off_intr(sc);
if (r & IWI_INTR_CMD_DONE) {
sc->flags &= ~IWI_FLAG_BUSY;
@ -1722,7 +1695,7 @@ iwi_intr(void *arg)
/* XXX rate-limit */
device_printf(sc->sc_dev, "parity error\n");
}
done:
IWI_UNLOCK(sc);
}
@ -2008,6 +1981,7 @@ iwi_watchdog(void *arg)
{
struct iwi_softc *sc = arg;
struct ifnet *ifp = sc->sc_ifp;
struct ieee80211com *ic = ifp->if_l2com;
IWI_LOCK_ASSERT(sc);
@ -2015,25 +1989,25 @@ iwi_watchdog(void *arg)
if (--sc->sc_tx_timer == 0) {
if_printf(ifp, "device timeout\n");
ifp->if_oerrors++;
taskqueue_enqueue(sc->sc_tq2, &sc->sc_restarttask);
ieee80211_runtask(ic, &sc->sc_restarttask);
}
}
if (sc->sc_state_timer > 0) {
if (--sc->sc_state_timer == 0) {
if_printf(ifp, "firmware stuck in state %d, resetting\n",
sc->fw_state);
taskqueue_enqueue(sc->sc_tq2, &sc->sc_restarttask);
if (sc->fw_state == IWI_FW_SCANNING) {
struct ieee80211com *ic = ifp->if_l2com;
ieee80211_cancel_scan(TAILQ_FIRST(&ic->ic_vaps));
}
ieee80211_runtask(ic, &sc->sc_restarttask);
sc->sc_state_timer = 3;
}
}
if (sc->sc_busy_timer > 0) {
if (--sc->sc_busy_timer == 0) {
if_printf(ifp, "firmware command timeout, resetting\n");
taskqueue_enqueue(sc->sc_tq2, &sc->sc_restarttask);
ieee80211_runtask(ic, &sc->sc_restarttask);
}
}
callout_reset(&sc->sc_wdtimer, hz, iwi_watchdog, sc);
@ -2665,7 +2639,7 @@ scan_band(const struct ieee80211_channel *c)
* Start a scan on the current channel or all channels.
*/
static int
iwi_scanchan(struct iwi_softc *sc, unsigned long maxdwell, int mode)
iwi_scanchan(struct iwi_softc *sc, unsigned long maxdwell, int allchan)
{
struct ieee80211com *ic;
struct ieee80211_channel *chan;
@ -2712,7 +2686,7 @@ iwi_scanchan(struct iwi_softc *sc, unsigned long maxdwell, int mode)
return (error);
}
if (mode == IWI_SCAN_ALLCHAN) {
if (allchan) {
int i, next, band, b, bstart;
/*
* Convert scan list to run-length encoded channel list
@ -2781,20 +2755,6 @@ iwi_scanchan(struct iwi_softc *sc, unsigned long maxdwell, int mode)
return (iwi_cmd(sc, IWI_CMD_SCAN_EXT, &scan, sizeof scan));
}
static void
iwi_scanabort(void *arg, int npending)
{
struct iwi_softc *sc = arg;
IWI_LOCK_DECL;
IWI_LOCK(sc);
sc->flags &= ~IWI_FLAG_CHANNEL_SCAN;
/* NB: make sure we're still scanning */
if (sc->fw_state == IWI_FW_SCANNING)
iwi_cmd(sc, IWI_CMD_ABORT_SCAN, NULL, 0);
IWI_UNLOCK(sc);
}
static int
iwi_set_sensitivity(struct iwi_softc *sc, int8_t rssi_dbm)
{
@ -2986,6 +2946,17 @@ iwi_auth_and_assoc(struct iwi_softc *sc, struct ieee80211vap *vap)
return (error);
}
static void
iwi_disassoc(void *arg, int pending)
{
struct iwi_softc *sc = arg;
IWI_LOCK_DECL;
IWI_LOCK(sc);
iwi_disassociate(sc, 0);
IWI_UNLOCK(sc);
}
static int
iwi_disassociate(struct iwi_softc *sc, int quiet)
{
@ -3086,9 +3057,6 @@ iwi_init_locked(struct iwi_softc *sc)
IWI_STATE_BEGIN(sc, IWI_FW_LOADING);
taskqueue_unblock(sc->sc_tq);
taskqueue_unblock(sc->sc_tq2);
if (iwi_reset(sc) != 0) {
device_printf(sc->sc_dev, "could not reset adapter\n");
goto fail;
@ -3183,8 +3151,6 @@ iwi_stop_locked(void *priv)
ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE);
taskqueue_block(sc->sc_tq);
taskqueue_block(sc->sc_tq2);
if (sc->sc_softled) {
callout_stop(&sc->sc_ledtimer);
sc->sc_blinking = 0;
@ -3204,7 +3170,6 @@ iwi_stop_locked(void *priv)
iwi_reset_tx_ring(sc, &sc->txq[3]);
iwi_reset_rx_ring(sc, &sc->rxq);
memset(sc->sc_cmd, 0, sizeof(sc->sc_cmd));
sc->sc_tx_timer = 0;
sc->sc_state_timer = 0;
sc->sc_busy_timer = 0;
@ -3266,8 +3231,10 @@ iwi_rfkill_poll(void *arg)
* it is enabled so we must poll for the latter.
*/
if (!iwi_getrfkill(sc)) {
taskqueue_unblock(sc->sc_tq);
taskqueue_enqueue(sc->sc_tq, &sc->sc_radiontask);
struct ifnet *ifp = sc->sc_ifp;
struct ieee80211com *ic = ifp->if_l2com;
ieee80211_runtask(ic, &sc->sc_radiontask);
return;
}
callout_reset(&sc->sc_rftimer, 2*hz, iwi_rfkill_poll, sc);
@ -3532,115 +3499,10 @@ iwi_ledattach(struct iwi_softc *sc)
}
}
static void
iwi_ops(void *arg0, int npending)
{
static const char *opnames[] = {
[IWI_CMD_FREE] = "FREE",
[IWI_SCAN_START] = "SCAN_START",
[IWI_SET_CHANNEL] = "SET_CHANNEL",
[IWI_AUTH] = "AUTH",
[IWI_ASSOC] = "ASSOC",
[IWI_DISASSOC] = "DISASSOC",
[IWI_SCAN_CURCHAN] = "SCAN_CURCHAN",
[IWI_SCAN_ALLCHAN] = "SCAN_ALLCHAN",
[IWI_SET_WME] = "SET_WME",
};
struct iwi_softc *sc = arg0;
struct ifnet *ifp = sc->sc_ifp;
struct ieee80211com *ic = ifp->if_l2com;
struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
IWI_LOCK_DECL;
int cmd;
unsigned long arg;
again:
IWI_CMD_LOCK(sc);
cmd = sc->sc_cmd[sc->sc_cmd_cur];
if (cmd == IWI_CMD_FREE) {
/* No more commands to process */
IWI_CMD_UNLOCK(sc);
return;
}
arg = sc->sc_arg[sc->sc_cmd_cur];
sc->sc_cmd[sc->sc_cmd_cur] = IWI_CMD_FREE; /* free the slot */
sc->sc_cmd_cur = (sc->sc_cmd_cur + 1) % IWI_CMD_MAXOPS;
IWI_CMD_UNLOCK(sc);
IWI_LOCK(sc);
while (sc->fw_state != IWI_FW_IDLE || (sc->flags & IWI_FLAG_BUSY)) {
msleep(sc, &sc->sc_mtx, 0, "iwicmd", hz/10);
}
if (!(ifp->if_drv_flags & IFF_DRV_RUNNING)) {
IWI_UNLOCK(sc);
return;
}
DPRINTF(("%s: %s arg %lu\n", __func__, opnames[cmd], arg));
switch (cmd) {
case IWI_AUTH:
case IWI_ASSOC:
if (cmd == IWI_AUTH)
vap->iv_state = IEEE80211_S_AUTH;
else
vap->iv_state = IEEE80211_S_ASSOC;
iwi_auth_and_assoc(sc, vap);
/* NB: completion done in iwi_notification_intr */
break;
case IWI_DISASSOC:
iwi_disassociate(sc, 0);
break;
case IWI_SET_WME:
if (vap->iv_state == IEEE80211_S_RUN)
(void) iwi_wme_setparams(sc, ic);
break;
case IWI_SCAN_START:
sc->flags |= IWI_FLAG_CHANNEL_SCAN;
break;
case IWI_SCAN_CURCHAN:
case IWI_SCAN_ALLCHAN:
if (!(sc->flags & IWI_FLAG_CHANNEL_SCAN)) {
DPRINTF(("%s: ic_scan_curchan while not scanning\n",
__func__));
goto done;
}
if (iwi_scanchan(sc, arg, cmd))
ieee80211_cancel_scan(vap);
break;
}
done:
IWI_UNLOCK(sc);
/* Take another pass */
goto again;
}
static int
iwi_queue_cmd(struct iwi_softc *sc, int cmd, unsigned long arg)
{
IWI_CMD_LOCK(sc);
if (sc->sc_cmd[sc->sc_cmd_next] != 0) {
IWI_CMD_UNLOCK(sc);
DPRINTF(("%s: command %d dropped\n", __func__, cmd));
return (EBUSY);
}
sc->sc_cmd[sc->sc_cmd_next] = cmd;
sc->sc_arg[sc->sc_cmd_next] = arg;
sc->sc_cmd_next = (sc->sc_cmd_next + 1) % IWI_CMD_MAXOPS;
taskqueue_enqueue(sc->sc_tq, &sc->sc_opstask);
IWI_CMD_UNLOCK(sc);
return (0);
}
static void
iwi_scan_start(struct ieee80211com *ic)
{
struct ifnet *ifp = ic->ic_ifp;
struct iwi_softc *sc = ifp->if_softc;
iwi_queue_cmd(sc, IWI_SCAN_START, 0);
/* ignore */
}
static void
@ -3658,21 +3520,14 @@ iwi_scan_curchan(struct ieee80211_scan_state *ss, unsigned long maxdwell)
struct ieee80211vap *vap = ss->ss_vap;
struct ifnet *ifp = vap->iv_ic->ic_ifp;
struct iwi_softc *sc = ifp->if_softc;
IWI_LOCK_DECL;
iwi_queue_cmd(sc, IWI_SCAN_CURCHAN, maxdwell);
IWI_LOCK(sc);
if (iwi_scanchan(sc, maxdwell, 0))
ieee80211_cancel_scan(vap);
IWI_UNLOCK(sc);
}
#if 0
static void
iwi_scan_allchan(struct ieee80211com *ic, unsigned long maxdwell)
{
struct ifnet *ifp = ic->ic_ifp;
struct iwi_softc *sc = ifp->if_softc;
iwi_queue_cmd(sc, IWI_SCAN_ALLCHAN, maxdwell);
}
#endif
static void
iwi_scan_mindwell(struct ieee80211_scan_state *ss)
{
@ -3684,6 +3539,12 @@ iwi_scan_end(struct ieee80211com *ic)
{
struct ifnet *ifp = ic->ic_ifp;
struct iwi_softc *sc = ifp->if_softc;
IWI_LOCK_DECL;
taskqueue_enqueue(sc->sc_tq2, &sc->sc_scanaborttask);
IWI_LOCK(sc);
sc->flags &= ~IWI_FLAG_CHANNEL_SCAN;
/* NB: make sure we're still scanning */
if (sc->fw_state == IWI_FW_SCANNING)
iwi_cmd(sc, IWI_CMD_ABORT_SCAN, NULL, 0);
IWI_UNLOCK(sc);
}

View File

@ -116,9 +116,6 @@ struct iwi_fw {
struct iwi_vap {
struct ieee80211vap iwi_vap;
struct task iwi_authsuccess_task;
struct task iwi_assocsuccess_task;
struct task iwi_assocfailed_task;
int (*iwi_newstate)(struct ieee80211vap *,
enum ieee80211_state, int);
@ -131,12 +128,8 @@ struct iwi_softc {
device_t sc_dev;
struct mtx sc_mtx;
struct mtx sc_cmdlock;
char sc_cmdname[12]; /* e.g. "iwi0_cmd" */
uint8_t sc_mcast[IEEE80211_ADDR_LEN];
struct unrhdr *sc_unr;
struct taskqueue *sc_tq; /* private task queue */
struct taskqueue *sc_tq2; /* reset task queue */
uint32_t flags;
#define IWI_FLAG_FW_INITED (1 << 0)
@ -195,9 +188,8 @@ struct iwi_softc {
struct task sc_radiontask; /* radio on processing */
struct task sc_radiofftask; /* radio off processing */
struct task sc_scanaborttask; /* cancel active scan */
struct task sc_restarttask; /* restart adapter processing */
struct task sc_opstask; /* scan / auth processing */
struct task sc_disassoctask;
unsigned int sc_softled : 1, /* enable LED gpio status */
sc_ledstate: 1, /* LED on/off state */
@ -219,21 +211,6 @@ struct iwi_softc {
int sc_state_timer; /* firmware state timer */
int sc_busy_timer; /* firmware cmd timer */
#define IWI_CMD_MAXOPS 10
int sc_cmd[IWI_CMD_MAXOPS];
unsigned long sc_arg[IWI_CMD_MAXOPS];
int sc_cmd_cur; /* current queued scan task */
int sc_cmd_next; /* last queued scan task */
#define IWI_CMD_FREE 0 /* for marking slots unused */
#define IWI_SCAN_START 1
#define IWI_SET_CHANNEL 2
#define IWI_AUTH 3
#define IWI_ASSOC 4
#define IWI_DISASSOC 5
#define IWI_SCAN_CURCHAN 6
#define IWI_SCAN_ALLCHAN 7
#define IWI_SET_WME 8
struct iwi_rx_radiotap_header sc_rxtap;
int sc_rxtap_len;
@ -277,11 +254,3 @@ struct iwi_softc {
if (!__waslocked) \
mtx_unlock(&(sc)->sc_mtx); \
} while (0)
#define IWI_CMD_LOCK_INIT(sc) do { \
snprintf((sc)->sc_cmdname, sizeof((sc)->sc_cmdname), "%s_cmd", \
device_get_nameunit((sc)->sc_dev)); \
mtx_init(&(sc)->sc_cmdlock, (sc)->sc_cmdname, NULL, MTX_DEF); \
} while (0)
#define IWI_CMD_LOCK_DESTROY(sc) mtx_destroy(&(sc)->sc_cmdlock)
#define IWI_CMD_LOCK(sc) mtx_lock(&(sc)->sc_cmdlock)
#define IWI_CMD_UNLOCK(sc) mtx_unlock(&(sc)->sc_cmdlock)

View File

@ -129,7 +129,6 @@ void iwn_rx_intr(struct iwn_softc *, struct iwn_rx_desc *,
void iwn_rx_statistics(struct iwn_softc *, struct iwn_rx_desc *);
void iwn_tx_intr(struct iwn_softc *, struct iwn_rx_desc *);
void iwn_cmd_intr(struct iwn_softc *, struct iwn_rx_desc *);
static void iwn_bmiss(void *, int);
void iwn_notif_intr(struct iwn_softc *);
void iwn_intr(void *);
void iwn_read_eeprom(struct iwn_softc *,
@ -166,8 +165,8 @@ void iwn_compute_differential_gain(struct iwn_softc *,
void iwn_tune_sensitivity(struct iwn_softc *,
const struct iwn_rx_stats *);
int iwn_send_sensitivity(struct iwn_softc *);
int iwn_auth(struct iwn_softc *);
int iwn_run(struct iwn_softc *);
int iwn_auth(struct iwn_softc *, struct ieee80211vap *);
int iwn_run(struct iwn_softc *, struct ieee80211vap *);
int iwn_scan(struct iwn_softc *);
int iwn_config(struct iwn_softc *);
void iwn_post_alive(struct iwn_softc *);
@ -183,8 +182,9 @@ static void iwn_scan_end(struct ieee80211com *);
static void iwn_set_channel(struct ieee80211com *);
static void iwn_scan_curchan(struct ieee80211_scan_state *, unsigned long);
static void iwn_scan_mindwell(struct ieee80211_scan_state *);
static void iwn_ops(void *, int);
static int iwn_queue_cmd( struct iwn_softc *, int, int, int);
static void iwn_hwreset(void *, int);
static void iwn_radioon(void *, int);
static void iwn_radiooff(void *, int);
static void iwn_bpfattach(struct iwn_softc *);
static void iwn_sysctlattach(struct iwn_softc *);
@ -213,7 +213,6 @@ enum {
printf(fmt, __VA_ARGS__); \
} while (0)
static const char *iwn_ops_str(int);
static const char *iwn_intr_str(uint8_t);
#else
#define DPRINTF(sc, m, fmt, ...) do { (void) sc; } while (0)
@ -296,20 +295,10 @@ iwn_attach(device_t dev)
}
IWN_LOCK_INIT(sc);
IWN_CMD_LOCK_INIT(sc);
callout_init_mtx(&sc->sc_timer_to, &sc->sc_mtx, 0);
/*
* Create the taskqueues used by the driver. Primarily
* sc_tq handles most the task
*/
sc->sc_tq = taskqueue_create("iwn_taskq", M_NOWAIT | M_ZERO,
taskqueue_thread_enqueue, &sc->sc_tq);
taskqueue_start_threads(&sc->sc_tq, 1, PI_NET, "%s taskq",
device_get_nameunit(dev));
TASK_INIT(&sc->sc_ops_task, 0, iwn_ops, sc );
TASK_INIT(&sc->sc_bmiss_task, 0, iwn_bmiss, sc);
TASK_INIT(&sc->sc_reinit_task, 0, iwn_hwreset, sc );
TASK_INIT(&sc->sc_radioon_task, 0, iwn_radioon, sc );
TASK_INIT(&sc->sc_radiooff_task, 0, iwn_radiooff, sc );
/*
* Put adapter into a known state.
@ -475,6 +464,10 @@ iwn_cleanup(device_t dev)
struct ieee80211com *ic = ifp->if_l2com;
int i;
ieee80211_draintask(ic, &sc->sc_reinit_task);
ieee80211_draintask(ic, &sc->sc_radioon_task);
ieee80211_draintask(ic, &sc->sc_radiooff_task);
if (ifp != NULL) {
iwn_stop(sc);
callout_drain(&sc->sc_timer_to);
@ -499,8 +492,6 @@ iwn_cleanup(device_t dev)
bus_release_resource(dev, SYS_RES_MEMORY, sc->mem_rid, sc->mem);
if (ifp != NULL)
if_free(ifp);
taskqueue_free(sc->sc_tq);
IWN_CMD_LOCK_DESTROY(sc);
IWN_LOCK_DESTROY(sc);
return 0;
}
@ -983,20 +974,13 @@ iwn_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
ieee80211_state_name[vap->iv_state],
ieee80211_state_name[nstate]);
IEEE80211_UNLOCK(ic);
IWN_LOCK(sc);
callout_stop(&sc->sc_timer_to);
IWN_UNLOCK(sc);
/*
* Some state transitions require issuing a configure request
* to the adapter. This must be done in a blocking context
* so we toss control to the task q thread where the state
* change will be finished after the command completes.
*/
if (nstate == IEEE80211_S_AUTH && vap->iv_state != IEEE80211_S_AUTH) {
/* !AUTH -> AUTH requires adapter config */
error = iwn_queue_cmd(sc, IWN_AUTH, arg, IWN_QUEUE_NORMAL);
return (error != 0 ? error : EINPROGRESS);
error = iwn_auth(sc, vap);
}
if (nstate == IEEE80211_S_RUN && vap->iv_state != IEEE80211_S_RUN) {
/*
@ -1004,8 +988,7 @@ iwn_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
* which is done with a firmware cmd. We also defer
* starting the timers until that work is done.
*/
error = iwn_queue_cmd(sc, IWN_RUN, arg, IWN_QUEUE_NORMAL);
return (error != 0 ? error : EINPROGRESS);
error = iwn_run(sc, vap);
}
if (nstate == IEEE80211_S_RUN) {
/*
@ -1013,6 +996,8 @@ iwn_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
*/
iwn_calib_reset(sc);
}
IWN_UNLOCK(sc);
IEEE80211_LOCK(ic);
return ivp->iv_newstate(vap, nstate, arg);
}
@ -1657,15 +1642,6 @@ iwn_cmd_intr(struct iwn_softc *sc, struct iwn_rx_desc *desc)
wakeup(&ring->cmd[desc->idx]);
}
static void
iwn_bmiss(void *arg, int npending)
{
struct iwn_softc *sc = arg;
struct ieee80211com *ic = sc->sc_ifp->if_l2com;
ieee80211_beacon_miss(ic);
}
void
iwn_notif_intr(struct iwn_softc *sc)
{
@ -1726,8 +1702,7 @@ iwn_notif_intr(struct iwn_softc *sc)
if (vap->iv_state == IEEE80211_S_RUN && misses > 5)
(void) iwn_init_sensitivity(sc);
if (misses >= vap->iv_bmissthreshold)
taskqueue_enqueue(taskqueue_swi,
&sc->sc_bmiss_task);
ieee80211_beacon_miss(ic);
break;
}
case IWN_UC_READY: {
@ -1780,7 +1755,7 @@ iwn_notif_intr(struct iwn_softc *sc)
"scan finished nchan=%d status=%d chan=%d\n",
scan->nchan, scan->status, scan->chan);
iwn_queue_cmd(sc, IWN_SCAN_NEXT, 0, IWN_QUEUE_NORMAL);
ieee80211_scan_next(vap);
break;
}
}
@ -1792,6 +1767,36 @@ iwn_notif_intr(struct iwn_softc *sc)
IWN_WRITE(sc, IWN_RX_WIDX, hw & ~7);
}
static void
iwn_rftoggle_intr(struct iwn_softc *sc)
{
struct ifnet *ifp = sc->sc_ifp;
struct ieee80211com *ic = ifp->if_l2com;
uint32_t tmp = IWN_READ(sc, IWN_GPIO_CTL);
IWN_LOCK_ASSERT(sc);
device_printf(sc->sc_dev, "RF switch: radio %s\n",
(tmp & IWN_GPIO_RF_ENABLED) ? "enabled" : "disabled");
if (tmp & IWN_GPIO_RF_ENABLED)
ieee80211_runtask(ic, &sc->sc_radioon_task);
else
ieee80211_runtask(ic, &sc->sc_radiooff_task);
}
static void
iwn_error_intr(struct iwn_softc *sc, uint32_t r1, uint32_t r2)
{
struct ifnet *ifp = sc->sc_ifp;
struct ieee80211com *ic = ifp->if_l2com;
IWN_LOCK_ASSERT(sc);
device_printf(sc->sc_dev, "error, INTR=%b STATUS=0x%x\n",
r1, IWN_INTR_BITS, r2);
ieee80211_runtask(ic, &sc->sc_reinit_task);
}
void
iwn_intr(void *arg)
{
@ -1820,21 +1825,12 @@ iwn_intr(void *arg)
DPRINTF(sc, IWN_DEBUG_INTR, "interrupt reg1=%x reg2=%x\n", r1, r2);
if (r1 & IWN_RF_TOGGLED) {
uint32_t tmp = IWN_READ(sc, IWN_GPIO_CTL);
device_printf(sc->sc_dev, "RF switch: radio %s\n",
(tmp & IWN_GPIO_RF_ENABLED) ? "enabled" : "disabled");
if (tmp & IWN_GPIO_RF_ENABLED)
iwn_queue_cmd(sc, IWN_RADIO_ENABLE, 0, IWN_QUEUE_CLEAR);
else
iwn_queue_cmd(sc, IWN_RADIO_DISABLE, 0, IWN_QUEUE_CLEAR);
}
if (r1 & IWN_RF_TOGGLED)
iwn_rftoggle_intr(sc);
if (r1 & IWN_CT_REACHED)
device_printf(sc->sc_dev, "critical temperature reached!\n");
if (r1 & (IWN_SW_ERROR | IWN_HW_ERROR)) {
device_printf(sc->sc_dev, "error, INTR=%b STATUS=0x%x\n",
r1, IWN_INTR_BITS, r2);
iwn_queue_cmd(sc, IWN_REINIT, 0, IWN_QUEUE_CLEAR);
iwn_error_intr(sc, r1, r2);
goto done;
}
if ((r1 & (IWN_RX_INTR | IWN_SW_RX_INTR)) || (r2 & IWN_RX_STATUS_INTR))
@ -2361,9 +2357,10 @@ iwn_watchdog(struct iwn_softc *sc)
{
if (sc->sc_tx_timer > 0 && --sc->sc_tx_timer == 0) {
struct ifnet *ifp = sc->sc_ifp;
struct ieee80211com *ic = ifp->if_l2com;
if_printf(ifp, "device timeout\n");
iwn_queue_cmd(sc, IWN_REINIT, 0, IWN_QUEUE_CLEAR);
ieee80211_runtask(ic, &sc->sc_reinit_task);
}
}
@ -3446,11 +3443,10 @@ iwn_send_sensitivity(struct iwn_softc *sc)
}
int
iwn_auth(struct iwn_softc *sc)
iwn_auth(struct iwn_softc *sc, struct ieee80211vap *vap)
{
struct ifnet *ifp = sc->sc_ifp;
struct ieee80211com *ic = ifp->if_l2com;
struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); /*XXX*/
struct ieee80211_node *ni = vap->iv_bss;
struct iwn_node_info node;
int error;
@ -3539,12 +3535,11 @@ iwn_auth(struct iwn_softc *sc)
* Configure the adapter for associated state.
*/
int
iwn_run(struct iwn_softc *sc)
iwn_run(struct iwn_softc *sc, struct ieee80211vap *vap)
{
#define MS(v,x) (((v) & x) >> x##_S)
struct ifnet *ifp = sc->sc_ifp;
struct ieee80211com *ic = ifp->if_l2com;
struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); /*XXX*/
struct ieee80211_node *ni = vap->iv_bss;
struct iwn_node_info node;
int error, maxrxampdu, ampdudensity;
@ -4270,9 +4265,6 @@ iwn_stop_locked(struct iwn_softc *sc)
IWN_WRITE(sc, IWN_INTR, 0xffffffff);
IWN_WRITE(sc, IWN_INTR_STATUS, 0xffffffff);
/* Clear any commands left in the taskq command buffer */
memset(sc->sc_cmd, 0, sizeof(sc->sc_cmd));
/* reset all Tx rings */
for (i = 0; i < IWN_NTXQUEUES; i++)
iwn_reset_tx_ring(sc, &sc->txq[i]);
@ -4308,7 +4300,10 @@ iwn_scan_start(struct ieee80211com *ic)
struct ifnet *ifp = ic->ic_ifp;
struct iwn_softc *sc = ifp->if_softc;
iwn_queue_cmd(sc, IWN_SCAN_START, 0, IWN_QUEUE_NORMAL);
IWN_LOCK(sc);
/* make the link LED blink while we're scanning */
iwn_set_led(sc, IWN_LED_LINK, 20, 2);
IWN_UNLOCK(sc);
}
/*
@ -4317,10 +4312,7 @@ iwn_scan_start(struct ieee80211com *ic)
static void
iwn_scan_end(struct ieee80211com *ic)
{
struct ifnet *ifp = ic->ic_ifp;
struct iwn_softc *sc = ifp->if_softc;
iwn_queue_cmd(sc, IWN_SCAN_STOP, 0, IWN_QUEUE_NORMAL);
/* ignore */
}
/*
@ -4331,15 +4323,29 @@ iwn_set_channel(struct ieee80211com *ic)
{
struct ifnet *ifp = ic->ic_ifp;
struct iwn_softc *sc = ifp->if_softc;
struct ieee80211vap *vap;
const struct ieee80211_channel *c = ic->ic_curchan;
int error;
vap = TAILQ_FIRST(&ic->ic_vaps); /* XXX */
IWN_LOCK(sc);
if (c != sc->sc_curchan) {
sc->sc_rxtap.wr_chan_freq = htole16(c->ic_freq);
sc->sc_rxtap.wr_chan_flags = htole16(c->ic_flags);
sc->sc_txtap.wt_chan_freq = htole16(c->ic_freq);
sc->sc_txtap.wt_chan_flags = htole16(c->ic_flags);
iwn_queue_cmd(sc, IWN_SET_CHAN, 0, IWN_QUEUE_NORMAL);
error = iwn_config(sc);
if (error != 0) {
DPRINTF(sc, IWN_DEBUG_STATE,
"%s: set chan failed, cancel scan\n",
__func__);
//XXX Handle failed scan correctly
ieee80211_cancel_scan(vap);
}
}
IWN_UNLOCK(sc);
}
/*
@ -4350,8 +4356,13 @@ iwn_scan_curchan(struct ieee80211_scan_state *ss, unsigned long maxdwell)
{
struct ieee80211vap *vap = ss->ss_vap;
struct iwn_softc *sc = vap->iv_ic->ic_ifp->if_softc;
int error;
iwn_queue_cmd(sc, IWN_SCAN_CURCHAN, 0, IWN_QUEUE_NORMAL);
IWN_LOCK(sc);
error = iwn_scan(sc);
IWN_UNLOCK(sc);
if (error != 0)
ieee80211_cancel_scan(vap);
}
/*
@ -4365,149 +4376,36 @@ iwn_scan_mindwell(struct ieee80211_scan_state *ss)
/* NB: don't try to abort scan; wait for firmware to finish */
}
/*
* Carry out work in the taskq context.
*/
static void
iwn_ops(void *arg0, int pending)
iwn_hwreset(void *arg0, int pending)
{
struct iwn_softc *sc = arg0;
struct ifnet *ifp = sc->sc_ifp;
struct ieee80211com *ic = ifp->if_l2com;
struct ieee80211vap *vap;
int cmd, arg, error;
enum ieee80211_state nstate;
for (;;) {
IWN_CMD_LOCK(sc);
cmd = sc->sc_cmd[sc->sc_cmd_cur];
if (cmd == 0) {
/* No more commands to process */
IWN_CMD_UNLOCK(sc);
return;
}
if ((sc->sc_ifp->if_drv_flags & IFF_DRV_RUNNING) == 0 &&
cmd != IWN_RADIO_ENABLE ) {
IWN_CMD_UNLOCK(sc);
return;
}
arg = sc->sc_cmd_arg[sc->sc_cmd_cur];
sc->sc_cmd[sc->sc_cmd_cur] = 0; /* free the slot */
sc->sc_cmd_cur = (sc->sc_cmd_cur + 1) % IWN_CMD_MAXOPS;
IWN_CMD_UNLOCK(sc);
IWN_LOCK(sc); /* NB: sync debug printfs on smp */
DPRINTF(sc, IWN_DEBUG_OPS, "%s: %s (cmd 0x%x)\n",
__func__, iwn_ops_str(cmd), cmd);
vap = TAILQ_FIRST(&ic->ic_vaps); /* XXX */
switch (cmd) {
case IWN_SCAN_START:
/* make the link LED blink while we're scanning */
iwn_set_led(sc, IWN_LED_LINK, 20, 2);
break;
case IWN_SCAN_STOP:
break;
case IWN_SCAN_NEXT:
ieee80211_scan_next(vap);
break;
case IWN_SCAN_CURCHAN:
error = iwn_scan(sc);
if (error != 0) {
IWN_UNLOCK(sc);
ieee80211_cancel_scan(vap);
IWN_LOCK(sc);
return;
}
break;
case IWN_SET_CHAN:
error = iwn_config(sc);
if (error != 0) {
DPRINTF(sc, IWN_DEBUG_STATE,
"%s: set chan failed, cancel scan\n",
__func__);
IWN_UNLOCK(sc);
//XXX Handle failed scan correctly
ieee80211_cancel_scan(vap);
return;
}
break;
case IWN_AUTH:
case IWN_RUN:
if (cmd == IWN_AUTH) {
error = iwn_auth(sc);
nstate = IEEE80211_S_AUTH;
} else {
error = iwn_run(sc);
nstate = IEEE80211_S_RUN;
}
if (error == 0) {
IWN_UNLOCK(sc);
IEEE80211_LOCK(ic);
IWN_VAP(vap)->iv_newstate(vap, nstate, arg);
if (vap->iv_newstate_cb != NULL)
vap->iv_newstate_cb(vap, nstate, arg);
IEEE80211_UNLOCK(ic);
IWN_LOCK(sc);
} else {
device_printf(sc->sc_dev,
"%s: %s state change failed, error %d\n",
__func__, ieee80211_state_name[nstate],
error);
}
break;
case IWN_REINIT:
IWN_UNLOCK(sc);
iwn_init(sc);
IWN_LOCK(sc);
ieee80211_notify_radio(ic, 1);
break;
case IWN_RADIO_ENABLE:
KASSERT(sc->fw_fp != NULL,
("Fware Not Loaded, can't load from tq"));
IWN_UNLOCK(sc);
iwn_init(sc);
IWN_LOCK(sc);
break;
case IWN_RADIO_DISABLE:
ieee80211_notify_radio(ic, 0);
iwn_stop_locked(sc);
break;
}
IWN_UNLOCK(sc);
}
iwn_init(sc);
ieee80211_notify_radio(ic, 1);
}
/*
* Queue a command for execution in the taskq thread.
* This is needed as the net80211 callbacks do not allow
* sleeping, since we need to sleep to confirm commands have
* been processed by the firmware, we must defer execution to
* a sleep enabled thread.
*/
static int
iwn_queue_cmd(struct iwn_softc *sc, int cmd, int arg, int clear)
static void
iwn_radioon(void *arg0, int pending)
{
IWN_CMD_LOCK(sc);
if (clear) {
sc->sc_cmd[0] = cmd;
sc->sc_cmd_arg[0] = arg;
sc->sc_cmd_cur = 0;
sc->sc_cmd_next = 1;
} else {
if (sc->sc_cmd[sc->sc_cmd_next] != 0) {
IWN_CMD_UNLOCK(sc);
DPRINTF(sc, IWN_DEBUG_ANY, "%s: command %d dropped\n",
__func__, cmd);
return EBUSY;
}
sc->sc_cmd[sc->sc_cmd_next] = cmd;
sc->sc_cmd_arg[sc->sc_cmd_next] = arg;
sc->sc_cmd_next = (sc->sc_cmd_next + 1) % IWN_CMD_MAXOPS;
}
taskqueue_enqueue(sc->sc_tq, &sc->sc_ops_task);
IWN_CMD_UNLOCK(sc);
return 0;
struct iwn_softc *sc = arg0;
iwn_init(sc);
}
static void
iwn_radiooff(void *arg0, int pending)
{
struct iwn_softc *sc = arg0;
struct ifnet *ifp = sc->sc_ifp;
struct ieee80211com *ic = ifp->if_l2com;
IWN_LOCK(sc);
ieee80211_notify_radio(ic, 0);
iwn_stop_locked(sc);
IWN_UNLOCK(sc);
}
static void
@ -4541,24 +4439,6 @@ iwn_sysctlattach(struct iwn_softc *sc)
}
#ifdef IWN_DEBUG
static const char *
iwn_ops_str(int cmd)
{
switch (cmd) {
case IWN_SCAN_START: return "SCAN_START";
case IWN_SCAN_CURCHAN: return "SCAN_CURCHAN";
case IWN_SCAN_STOP: return "SCAN_STOP";
case IWN_SET_CHAN: return "SET_CHAN";
case IWN_AUTH: return "AUTH";
case IWN_SCAN_NEXT: return "SCAN_NEXT";
case IWN_RUN: return "RUN";
case IWN_RADIO_ENABLE: return "RADIO_ENABLE";
case IWN_RADIO_DISABLE: return "RADIO_DISABLE";
case IWN_REINIT: return "REINIT";
}
return "UNKNOWN COMMAND";
}
static const char *
iwn_intr_str(uint8_t cmd)
{

View File

@ -180,33 +180,10 @@ struct iwn_softc {
void *sc_ih;
bus_size_t sc_sz;
/* command queue related variables */
#define IWN_SCAN_START (1<<0)
#define IWN_SCAN_CURCHAN (1<<1)
#define IWN_SCAN_STOP (1<<2)
#define IWN_SET_CHAN (1<<3)
#define IWN_AUTH (1<<4)
#define IWN_SCAN_NEXT (1<<5)
#define IWN_RUN (1<<6)
#define IWN_RADIO_ENABLE (1<<7)
#define IWN_RADIO_DISABLE (1<<8)
#define IWN_REINIT (1<<9)
#define IWN_CMD_MAXOPS 10
/* command queuing request type */
#define IWN_QUEUE_NORMAL 0
#define IWN_QUEUE_CLEAR 1
int sc_cmd[IWN_CMD_MAXOPS];
int sc_cmd_arg[IWN_CMD_MAXOPS];
int sc_cmd_cur; /* current queued scan task */
int sc_cmd_next; /* last queued scan task */
struct mtx sc_cmdlock;
/* Task queues used to control the driver */
struct taskqueue *sc_tq; /* Main command task queue */
/* Tasks used by the driver */
struct task sc_ops_task; /* deferred ops */
struct task sc_bmiss_task; /* beacon miss */
struct task sc_reinit_task;
struct task sc_radioon_task;
struct task sc_radiooff_task;
/* Thermal calibration */
int calib_cnt;
@ -234,9 +211,3 @@ struct iwn_softc {
#define IWN_LOCK_ASSERT(_sc) mtx_assert(&(_sc)->sc_mtx, MA_OWNED)
#define IWN_UNLOCK(_sc) mtx_unlock(&(_sc)->sc_mtx)
#define IWN_LOCK_DESTROY(_sc) mtx_destroy(&(_sc)->sc_mtx)
#define IWN_CMD_LOCK_INIT(_sc) \
mtx_init(&(_sc)->sc_cmdlock, device_get_nameunit((_sc)->sc_dev), \
NULL, MTX_DEF);
#define IWN_CMD_LOCK_DESTROY(_sc) mtx_destroy(&(_sc)->sc_cmdlock)
#define IWN_CMD_LOCK(_sc) mtx_lock(&(_sc)->sc_cmdlock)
#define IWN_CMD_UNLOCK(_sc) mtx_unlock(&(_sc)->sc_cmdlock)

View File

@ -26,22 +26,58 @@ __FBSDID("$FreeBSD$");
* http://www.ralinktech.com.tw/
*/
#include "usbdevs.h"
#include <dev/usb/usb.h>
#include <dev/usb/usb_mfunc.h>
#include <dev/usb/usb_error.h>
#include <sys/param.h>
#include <sys/sockio.h>
#include <sys/sysctl.h>
#include <sys/lock.h>
#include <sys/mutex.h>
#include <sys/mbuf.h>
#include <sys/kernel.h>
#include <sys/socket.h>
#include <sys/systm.h>
#include <sys/malloc.h>
#include <sys/module.h>
#include <sys/bus.h>
#include <sys/endian.h>
#include <sys/kdb.h>
#include <machine/bus.h>
#include <machine/resource.h>
#include <sys/rman.h>
#include <net/bpf.h>
#include <net/if.h>
#include <net/if_arp.h>
#include <net/ethernet.h>
#include <net/if_dl.h>
#include <net/if_media.h>
#include <net/if_types.h>
#ifdef INET
#include <netinet/in.h>
#include <netinet/in_systm.h>
#include <netinet/in_var.h>
#include <netinet/if_ether.h>
#include <netinet/ip.h>
#endif
#include <net80211/ieee80211_var.h>
#include <net80211/ieee80211_regdomain.h>
#include <net80211/ieee80211_radiotap.h>
#include <net80211/ieee80211_amrr.h>
#define USB_DEBUG_VAR rum_debug
#include <dev/usb/usb.h>
#include <dev/usb/usb_error.h>
#include <dev/usb/usb_core.h>
#include <dev/usb/usb_lookup.h>
#include <dev/usb/usb_process.h>
#include <dev/usb/usb_debug.h>
#include <dev/usb/usb_request.h>
#include <dev/usb/usb_busdma.h>
#include <dev/usb/usb_util.h>
#include "usbdevs.h"
#include <dev/usb/wlan/usb_wlan.h>
#include <dev/usb/wlan/if_rumreg.h>
#include <dev/usb/wlan/if_rumvar.h>
#include <dev/usb/wlan/if_rumfw.h>
@ -116,16 +152,6 @@ static device_detach_t rum_detach;
static usb2_callback_t rum_bulk_read_callback;
static usb2_callback_t rum_bulk_write_callback;
static usb2_proc_callback_t rum_command_wrapper;
static usb2_proc_callback_t rum_attach_post;
static usb2_proc_callback_t rum_task;
static usb2_proc_callback_t rum_scantask;
static usb2_proc_callback_t rum_promisctask;
static usb2_proc_callback_t rum_amrr_task;
static usb2_proc_callback_t rum_init_task;
static usb2_proc_callback_t rum_stop_task;
static usb2_proc_callback_t rum_flush_task;
static usb2_error_t rum_do_request(struct rum_softc *sc,
struct usb2_device_request *req, void *data);
static struct ieee80211vap *rum_vap_create(struct ieee80211com *,
@ -174,10 +200,13 @@ static void rum_update_slot(struct ifnet *);
static void rum_set_bssid(struct rum_softc *, const uint8_t *);
static void rum_set_macaddr(struct rum_softc *, const uint8_t *);
static void rum_update_promisc(struct ifnet *);
static void rum_setpromisc(struct rum_softc *);
static const char *rum_get_rf(int);
static void rum_read_eeprom(struct rum_softc *);
static int rum_bbp_init(struct rum_softc *);
static void rum_init_locked(struct rum_softc *);
static void rum_init(void *);
static void rum_stop(struct rum_softc *);
static void rum_load_microcode(struct rum_softc *, const uint8_t *,
size_t);
static int rum_prepare_beacon(struct rum_softc *,
@ -194,10 +223,8 @@ static int rum_get_rssi(struct rum_softc *, uint8_t);
static void rum_amrr_start(struct rum_softc *,
struct ieee80211_node *);
static void rum_amrr_timeout(void *);
static void rum_amrr_task(void *, int);
static int rum_pause(struct rum_softc *, int);
static void rum_queue_command(struct rum_softc *,
usb2_proc_callback_t *, struct usb2_proc_msg *,
struct usb2_proc_msg *);
static const struct {
uint32_t reg;
@ -398,8 +425,11 @@ rum_attach(device_t self)
{
struct usb2_attach_arg *uaa = device_get_ivars(self);
struct rum_softc *sc = device_get_softc(self);
uint8_t iface_index;
int error;
struct ieee80211com *ic;
struct ifnet *ifp;
uint8_t iface_index, bands;
uint32_t tmp;
int error, ntries;
device_set_usb2_desc(self);
sc->sc_udev = uaa->device;
@ -408,8 +438,6 @@ rum_attach(device_t self)
mtx_init(&sc->sc_mtx, device_get_nameunit(self),
MTX_NETWORK_LOCK, MTX_DEF);
cv_init(&sc->sc_cmd_cv, "wtxdone");
iface_index = RT2573_IFACE_INDEX;
error = usb2_transfer_setup(uaa->device, &iface_index,
sc->sc_xfer, rum_config, RUM_N_TRANSFER, sc, &sc->sc_mtx);
@ -418,37 +446,8 @@ rum_attach(device_t self)
"err=%s\n", usb2_errstr(error));
goto detach;
}
error = usb2_proc_create(&sc->sc_tq, &sc->sc_mtx,
device_get_nameunit(self), USB_PRI_MED);
if (error) {
device_printf(self, "could not setup config thread!\n");
goto detach;
}
/* fork rest of the attach code */
RUM_LOCK(sc);
rum_queue_command(sc, rum_attach_post,
&sc->sc_synctask[0].hdr,
&sc->sc_synctask[1].hdr);
RUM_UNLOCK(sc);
return (0);
detach:
rum_detach(self);
return (ENXIO); /* failure */
}
static void
rum_attach_post(struct usb2_proc_msg *pm)
{
struct rum_task *task = (struct rum_task *)pm;
struct rum_softc *sc = task->sc;
struct ifnet *ifp;
struct ieee80211com *ic;
unsigned int ntries;
uint32_t tmp;
uint8_t bands;
/* retrieve RT2573 rev. no */
for (ntries = 0; ntries < 100; ntries++) {
if ((tmp = rum_read(sc, RT2573_MAC_CSR0)) != 0)
@ -458,7 +457,8 @@ rum_attach_post(struct usb2_proc_msg *pm)
}
if (ntries == 100) {
device_printf(sc->sc_dev, "timeout waiting for chip to settle\n");
return;
RUM_UNLOCK(sc);
goto detach;
}
/* retrieve MAC address and various other things from EEPROM */
@ -468,18 +468,12 @@ rum_attach_post(struct usb2_proc_msg *pm)
tmp, rum_get_rf(sc->rf_rev));
rum_load_microcode(sc, rt2573_ucode, sizeof(rt2573_ucode));
/* XXX Async attach race */
if (usb2_proc_is_gone(&sc->sc_tq))
return;
RUM_UNLOCK(sc);
ifp = sc->sc_ifp = if_alloc(IFT_IEEE80211);
if (ifp == NULL) {
device_printf(sc->sc_dev, "can not if_alloc()\n");
RUM_LOCK(sc);
return;
goto detach;
}
ic = ifp->if_l2com;
@ -542,7 +536,11 @@ rum_attach_post(struct usb2_proc_msg *pm)
if (bootverbose)
ieee80211_announce(ic);
RUM_LOCK(sc);
return (0);
detach:
rum_detach(self);
return (ENXIO); /* failure */
}
static int
@ -552,12 +550,8 @@ rum_detach(device_t self)
struct ifnet *ifp = sc->sc_ifp;
struct ieee80211com *ic;
/* wait for any post attach or other command to complete */
usb2_proc_drain(&sc->sc_tq);
/* stop all USB transfers */
usb2_transfer_unsetup(sc->sc_xfer, RUM_N_TRANSFER);
usb2_proc_free(&sc->sc_tq);
/* free TX list, if any */
RUM_LOCK(sc);
@ -570,7 +564,6 @@ rum_detach(device_t self)
ieee80211_ifdetach(ic);
if_free(ifp);
}
cv_destroy(&sc->sc_cmd_cv);
mtx_destroy(&sc->sc_mtx);
return (0);
@ -584,7 +577,7 @@ rum_do_request(struct rum_softc *sc,
int ntries = 10;
while (ntries--) {
err = usb2_do_request_proc(sc->sc_udev, &sc->sc_tq,
err = usb2_do_request_flags(sc->sc_udev, &sc->sc_mtx,
req, data, 0, NULL, 250 /* ms */);
if (err == 0)
break;
@ -622,8 +615,8 @@ rum_vap_create(struct ieee80211com *ic,
rvp->newstate = vap->iv_newstate;
vap->iv_newstate = rum_newstate;
rvp->sc = sc;
usb2_callout_init_mtx(&rvp->amrr_ch, &sc->sc_mtx, 0);
TASK_INIT(&rvp->amrr_task, 0, rum_amrr_task, rvp);
ieee80211_amrr_init(&rvp->amrr, vap,
IEEE80211_AMRR_MIN_SUCCESS_THRESHOLD,
IEEE80211_AMRR_MAX_SUCCESS_THRESHOLD,
@ -635,26 +628,14 @@ rum_vap_create(struct ieee80211com *ic,
return vap;
}
static void
rum_flush_task(struct usb2_proc_msg *pm)
{
/* Nothing to do */
}
static void
rum_vap_delete(struct ieee80211vap *vap)
{
struct rum_vap *rvp = RUM_VAP(vap);
struct rum_softc *sc = rvp->sc;
RUM_LOCK(sc);
/* wait for any pending tasks to complete */
rum_queue_command(sc, rum_flush_task,
&sc->sc_synctask[0].hdr,
&sc->sc_synctask[1].hdr);
RUM_UNLOCK(sc);
struct ieee80211com *ic = vap->iv_ic;
usb2_callout_drain(&rvp->amrr_ch);
ieee80211_draintask(ic, &rvp->amrr_task);
ieee80211_amrr_cleanup(&rvp->amrr);
ieee80211_vap_detach(vap);
free(rvp, M_80211_VAP);
@ -724,23 +705,27 @@ rum_unsetup_tx_list(struct rum_softc *sc)
}
}
static void
rum_task(struct usb2_proc_msg *pm)
static int
rum_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
{
struct rum_task *task = (struct rum_task *)pm;
struct rum_softc *sc = task->sc;
struct ifnet *ifp = sc->sc_ifp;
struct ieee80211com *ic = ifp->if_l2com;
struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
struct rum_vap *rvp = RUM_VAP(vap);
struct ieee80211com *ic = vap->iv_ic;
struct rum_softc *sc = ic->ic_ifp->if_softc;
const struct ieee80211_txparam *tp;
enum ieee80211_state ostate;
struct ieee80211_node *ni;
uint32_t tmp;
ostate = vap->iv_state;
DPRINTF("%s -> %s\n",
ieee80211_state_name[ostate],
ieee80211_state_name[nstate]);
switch (sc->sc_state) {
IEEE80211_UNLOCK(ic);
RUM_LOCK(sc);
usb2_callout_stop(&rvp->amrr_ch);
switch (nstate) {
case IEEE80211_S_INIT:
if (ostate == IEEE80211_S_RUN) {
/* abort TSF synchronization */
@ -776,45 +761,9 @@ rum_task(struct usb2_proc_msg *pm)
default:
break;
}
RUM_UNLOCK(sc);
IEEE80211_LOCK(ic);
rvp->newstate(vap, sc->sc_state, sc->sc_arg);
if (vap->iv_newstate_cb != NULL)
vap->iv_newstate_cb(vap, sc->sc_state, sc->sc_arg);
IEEE80211_UNLOCK(ic);
RUM_LOCK(sc);
}
static int
rum_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
{
struct rum_vap *rvp = RUM_VAP(vap);
struct ieee80211com *ic = vap->iv_ic;
struct rum_softc *sc = ic->ic_ifp->if_softc;
DPRINTF("%s -> %s\n",
ieee80211_state_name[vap->iv_state],
ieee80211_state_name[nstate]);
RUM_LOCK(sc);
usb2_callout_stop(&rvp->amrr_ch);
/* do it in a process context */
sc->sc_state = nstate;
sc->sc_arg = arg;
RUM_UNLOCK(sc);
if (nstate == IEEE80211_S_INIT) {
rvp->newstate(vap, nstate, arg);
return 0;
} else {
RUM_LOCK(sc);
rum_queue_command(sc, rum_task, &sc->sc_task[0].hdr,
&sc->sc_task[1].hdr);
RUM_UNLOCK(sc);
return EINPROGRESS;
}
return (rvp->newstate(vap, nstate, arg));
}
static void
@ -828,10 +777,6 @@ rum_bulk_write_callback(struct usb2_xfer *xfer)
struct mbuf *m;
unsigned int len;
/* wakeup waiting command, if any */
if (sc->sc_last_task != NULL)
cv_signal(&sc->sc_cmd_cv);
switch (USB_GET_STATE(xfer)) {
case USB_ST_TRANSFERRED:
DPRINTFN(11, "transfer complete, %d bytes\n", xfer->actlen);
@ -847,10 +792,6 @@ rum_bulk_write_callback(struct usb2_xfer *xfer)
/* FALLTHROUGH */
case USB_ST_SETUP:
tr_setup:
/* wait for command to complete, if any */
if (sc->sc_last_task != NULL)
break;
data = STAILQ_FIRST(&sc->tx_q);
if (data) {
STAILQ_REMOVE_HEAD(&sc->tx_q, next);
@ -1377,20 +1318,13 @@ rum_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
RUM_LOCK(sc);
if (ifp->if_flags & IFF_UP) {
if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) {
rum_queue_command(sc, rum_init_task,
&sc->sc_synctask[0].hdr,
&sc->sc_synctask[1].hdr);
rum_init_locked(sc);
startall = 1;
} else
rum_queue_command(sc, rum_promisctask,
&sc->sc_promisctask[0].hdr,
&sc->sc_promisctask[1].hdr);
rum_setpromisc(sc);
} else {
if (ifp->if_drv_flags & IFF_DRV_RUNNING) {
rum_queue_command(sc, rum_stop_task,
&sc->sc_synctask[0].hdr,
&sc->sc_synctask[1].hdr);
}
if (ifp->if_drv_flags & IFF_DRV_RUNNING)
rum_stop(sc);
}
RUM_UNLOCK(sc);
if (startall)
@ -1844,10 +1778,8 @@ rum_set_macaddr(struct rum_softc *sc, const uint8_t *addr)
}
static void
rum_promisctask(struct usb2_proc_msg *pm)
rum_setpromisc(struct rum_softc *sc)
{
struct rum_task *task = (struct rum_task *)pm;
struct rum_softc *sc = task->sc;
struct ifnet *ifp = sc->sc_ifp;
uint32_t tmp;
@ -1872,9 +1804,7 @@ rum_update_promisc(struct ifnet *ifp)
return;
RUM_LOCK(sc);
rum_queue_command(sc, rum_promisctask,
&sc->sc_promisctask[0].hdr,
&sc->sc_promisctask[1].hdr);
rum_setpromisc(sc);
RUM_UNLOCK(sc);
}
@ -2008,11 +1938,9 @@ rum_bbp_init(struct rum_softc *sc)
}
static void
rum_init_task(struct usb2_proc_msg *pm)
rum_init_locked(struct rum_softc *sc)
{
#define N(a) (sizeof (a) / sizeof ((a)[0]))
struct rum_task *task = (struct rum_task *)pm;
struct rum_softc *sc = task->sc;
struct ifnet *ifp = sc->sc_ifp;
struct ieee80211com *ic = ifp->if_l2com;
uint32_t tmp;
@ -2021,7 +1949,7 @@ rum_init_task(struct usb2_proc_msg *pm)
RUM_LOCK_ASSERT(sc, MA_OWNED);
rum_stop_task(pm);
rum_stop(sc);
/* initialize MAC registers to default values */
for (i = 0; i < N(rum_def_mac); i++)
@ -2086,7 +2014,7 @@ rum_init_task(struct usb2_proc_msg *pm)
usb2_transfer_start(sc->sc_xfer[RUM_BULK_RD]);
return;
fail: rum_stop_task(pm);
fail: rum_stop(sc);
#undef N
}
@ -2098,9 +2026,7 @@ rum_init(void *priv)
struct ieee80211com *ic = ifp->if_l2com;
RUM_LOCK(sc);
rum_queue_command(sc, rum_init_task,
&sc->sc_synctask[0].hdr,
&sc->sc_synctask[1].hdr);
rum_init_locked(sc);
RUM_UNLOCK(sc);
if (ifp->if_drv_flags & IFF_DRV_RUNNING)
@ -2108,10 +2034,8 @@ rum_init(void *priv)
}
static void
rum_stop_task(struct usb2_proc_msg *pm)
rum_stop(struct rum_softc *sc)
{
struct rum_task *task = (struct rum_task *)pm;
struct rum_softc *sc = task->sc;
struct ifnet *ifp = sc->sc_ifp;
uint32_t tmp;
@ -2271,24 +2195,24 @@ static void
rum_amrr_timeout(void *arg)
{
struct rum_vap *rvp = arg;
struct rum_softc *sc = rvp->sc;
struct ieee80211vap *vap = &rvp->vap;
struct ieee80211com *ic = vap->iv_ic;
rum_queue_command(sc, rum_amrr_task,
&rvp->amrr_task[0].hdr, &rvp->amrr_task[1].hdr);
ieee80211_runtask(ic, &rvp->amrr_task);
}
static void
rum_amrr_task(struct usb2_proc_msg *pm)
rum_amrr_task(void *arg, int pending)
{
struct rum_task *task = (struct rum_task *)pm;
struct rum_softc *sc = task->sc;
struct ifnet *ifp = sc->sc_ifp;
struct ieee80211com *ic = ifp->if_l2com;
struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
struct rum_vap *rvp = RUM_VAP(vap);
struct rum_vap *rvp = arg;
struct ieee80211vap *vap = &rvp->vap;
struct ieee80211com *ic = vap->iv_ic;
struct ifnet *ifp = ic->ic_ifp;
struct rum_softc *sc = ifp->if_softc;
struct ieee80211_node *ni = vap->iv_bss;
int ok, fail;
RUM_LOCK(sc);
/* read and clear statistic registers (STA_CSR0 to STA_CSR10) */
rum_read_multi(sc, RT2573_STA_CSR0, sc->sta, sizeof(sc->sta));
@ -2303,6 +2227,7 @@ rum_amrr_task(struct usb2_proc_msg *pm)
ifp->if_oerrors += fail; /* count TX retry-fail as Tx errors */
usb2_callout_reset(&rvp->amrr_ch, hz, rum_amrr_timeout, rvp);
RUM_UNLOCK(sc);
}
/* ARGUSED */
@ -2327,13 +2252,15 @@ rum_newassoc(struct ieee80211_node *ni, int isnew)
static void
rum_scan_start(struct ieee80211com *ic)
{
struct rum_softc *sc = ic->ic_ifp->if_softc;
struct ifnet *ifp = ic->ic_ifp;
struct rum_softc *sc = ifp->if_softc;
uint32_t tmp;
RUM_LOCK(sc);
/* do it in a process context */
sc->sc_scan_action = RUM_SCAN_START;
rum_queue_command(sc, rum_scantask,
&sc->sc_scantask[0].hdr, &sc->sc_scantask[1].hdr);
/* abort TSF synchronization */
tmp = rum_read(sc, RT2573_TXRX_CSR9);
rum_write(sc, RT2573_TXRX_CSR9, tmp & ~0x00ffffff);
rum_set_bssid(sc, ifp->if_broadcastaddr);
RUM_UNLOCK(sc);
}
@ -2344,10 +2271,8 @@ rum_scan_end(struct ieee80211com *ic)
struct rum_softc *sc = ic->ic_ifp->if_softc;
RUM_LOCK(sc);
/* do it in a process context */
sc->sc_scan_action = RUM_SCAN_END;
rum_queue_command(sc, rum_scantask,
&sc->sc_scantask[0].hdr, &sc->sc_scantask[1].hdr);
rum_enable_tsf_sync(sc);
rum_set_bssid(sc, sc->sc_bssid);
RUM_UNLOCK(sc);
}
@ -2358,43 +2283,10 @@ rum_set_channel(struct ieee80211com *ic)
struct rum_softc *sc = ic->ic_ifp->if_softc;
RUM_LOCK(sc);
/* do it in a process context */
sc->sc_scan_action = RUM_SET_CHANNEL;
rum_queue_command(sc, rum_scantask,
&sc->sc_scantask[0].hdr, &sc->sc_scantask[1].hdr);
rum_set_chan(sc, ic->ic_curchan);
RUM_UNLOCK(sc);
}
static void
rum_scantask(struct usb2_proc_msg *pm)
{
struct rum_task *task = (struct rum_task *)pm;
struct rum_softc *sc = task->sc;
struct ifnet *ifp = sc->sc_ifp;
struct ieee80211com *ic = ifp->if_l2com;
uint32_t tmp;
RUM_LOCK_ASSERT(sc, MA_OWNED);
switch (sc->sc_scan_action) {
case RUM_SCAN_START:
/* abort TSF synchronization */
tmp = rum_read(sc, RT2573_TXRX_CSR9);
rum_write(sc, RT2573_TXRX_CSR9, tmp & ~0x00ffffff);
rum_set_bssid(sc, ifp->if_broadcastaddr);
break;
case RUM_SET_CHANNEL:
rum_set_chan(sc, ic->ic_curchan);
break;
default: /* RUM_SCAN_END */
rum_enable_tsf_sync(sc);
rum_set_bssid(sc, sc->sc_bssid);
break;
}
}
static int
rum_get_rssi(struct rum_softc *sc, uint8_t raw)
{
@ -2445,72 +2337,11 @@ rum_get_rssi(struct rum_softc *sc, uint8_t raw)
static int
rum_pause(struct rum_softc *sc, int timeout)
{
if (usb2_proc_is_gone(&sc->sc_tq))
return (1);
usb2_pause_mtx(&sc->sc_mtx, timeout);
return (0);
}
static void
rum_command_wrapper(struct usb2_proc_msg *pm)
{
struct rum_task *task = (struct rum_task *)pm;
struct rum_softc *sc = task->sc;
struct ifnet *ifp;
/* wait for pending transfer, if any */
while (usb2_transfer_pending(sc->sc_xfer[RUM_BULK_WR]))
cv_wait(&sc->sc_cmd_cv, &sc->sc_mtx);
/* make sure any hardware buffers are emptied */
rum_pause(sc, hz / 1000);
/* execute task */
task->func(pm);
/* check if this is the last task executed */
if (sc->sc_last_task == task) {
sc->sc_last_task = NULL;
ifp = sc->sc_ifp;
/* re-start TX, if any */
if ((ifp != NULL) && (ifp->if_drv_flags & IFF_DRV_RUNNING))
usb2_transfer_start(sc->sc_xfer[RUM_BULK_WR]);
}
}
static void
rum_queue_command(struct rum_softc *sc, usb2_proc_callback_t *fn,
struct usb2_proc_msg *t0, struct usb2_proc_msg *t1)
{
struct rum_task *task;
RUM_LOCK_ASSERT(sc, MA_OWNED);
/*
* NOTE: The task cannot get executed before we drop the
* "sc_mtx" mutex. It is safe to update fields in the message
* structure after that the message got queued.
*/
task = (struct rum_task *)
usb2_proc_msignal(&sc->sc_tq, t0, t1);
/* Setup callback and softc pointers */
task->hdr.pm_callback = rum_command_wrapper;
task->func = fn;
task->sc = sc;
/* Make sure that any TX operation will stop */
sc->sc_last_task = task;
/*
* Init, stop and flush must be synchronous!
*/
if ((fn == rum_init_task) || (fn == rum_stop_task) ||
(fn == rum_flush_task))
usb2_proc_mwait(&sc->sc_tq, t0, t1);
}
static device_method_t rum_methods[] = {
/* Device interface */
DEVMETHOD(device_probe, rum_match),

View File

@ -54,12 +54,6 @@ struct rum_tx_radiotap_header {
struct rum_softc;
struct rum_task {
struct usb2_proc_msg hdr;
usb2_proc_callback_t *func;
struct rum_softc *sc;
};
struct rum_tx_data {
STAILQ_ENTRY(rum_tx_data) next;
struct rum_softc *sc;
@ -78,11 +72,10 @@ struct rum_node {
struct rum_vap {
struct ieee80211vap vap;
struct rum_softc *sc;
struct ieee80211_beacon_offsets bo;
struct ieee80211_amrr amrr;
struct usb2_callout amrr_ch;
struct rum_task amrr_task[2];
struct task amrr_task;
int (*newstate)(struct ieee80211vap *,
enum ieee80211_state, int);
@ -99,32 +92,18 @@ struct rum_softc {
struct ifnet *sc_ifp;
device_t sc_dev;
struct usb2_device *sc_udev;
struct usb2_process sc_tq;
struct usb2_xfer *sc_xfer[RUM_N_TRANSFER];
struct rum_task *sc_last_task;
uint8_t rf_rev;
uint8_t rffreq;
enum ieee80211_state sc_state;
int sc_arg;
struct rum_task sc_synctask[2];
struct rum_task sc_task[2];
struct rum_task sc_promisctask[2];
struct rum_task sc_scantask[2];
int sc_scan_action;
#define RUM_SCAN_START 0
#define RUM_SCAN_END 1
#define RUM_SET_CHANNEL 2
struct rum_tx_data tx_data[RUM_TX_LIST_COUNT];
rum_txdhead tx_q;
rum_txdhead tx_free;
int tx_nfree;
struct rum_rx_desc sc_rx_desc;
struct cv sc_cmd_cv;
struct mtx sc_mtx;
uint32_t sta[6];

View File

@ -1602,6 +1602,7 @@ uath_tx_start(struct uath_softc *sc, struct mbuf *m0, struct ieee80211_node *ni,
{
struct ifnet *ifp = sc->sc_ifp;
struct ieee80211com *ic = ifp->if_l2com;
struct ieee80211vap *vap = ni->ni_vap;
struct uath_chunk *chunk;
struct uath_tx_desc *desc;
const struct ieee80211_frame *wh;
@ -1677,9 +1678,9 @@ uath_tx_start(struct uath_softc *sc, struct mbuf *m0, struct ieee80211_node *ni,
m_freem(m0);
return (EIO);
}
if (sc->sc_state == IEEE80211_S_AUTH ||
sc->sc_state == IEEE80211_S_ASSOC ||
sc->sc_state == IEEE80211_S_RUN)
if (vap->iv_state == IEEE80211_S_AUTH ||
vap->iv_state == IEEE80211_S_ASSOC ||
vap->iv_state == IEEE80211_S_RUN)
desc->connid = htobe32(UATH_ID_BSS);
else
desc->connid = htobe32(UATH_ID_INVALID);
@ -2061,11 +2062,10 @@ uath_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
"%s: %s -> %s\n", __func__, ieee80211_state_name[vap->iv_state],
ieee80211_state_name[nstate]);
IEEE80211_UNLOCK(ic);
UATH_LOCK(sc);
callout_stop(&sc->stat_ch);
callout_stop(&sc->watchdog_ch);
sc->sc_state = nstate;
switch (nstate) {
case IEEE80211_S_INIT:
@ -2139,14 +2139,8 @@ uath_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
break;
}
UATH_UNLOCK(sc);
IEEE80211_LOCK(ic);
uvp->newstate(vap, nstate, arg);
if (vap->iv_newstate_cb != NULL)
vap->iv_newstate_cb(vap, nstate, arg);
IEEE80211_UNLOCK(ic);
return (0);
return (uvp->newstate(vap, nstate, arg));
}
static int

View File

@ -183,7 +183,6 @@ struct uath_softc {
struct uath_stat sc_stat;
int (*sc_newstate)(struct ieee80211com *,
enum ieee80211_state, int);
enum ieee80211_state sc_state;
struct usb2_xfer *sc_xfer[UATH_N_XFERS];
struct uath_cmd sc_cmd[UATH_CMD_LIST_COUNT];

View File

@ -28,22 +28,59 @@ __FBSDID("$FreeBSD$");
* http://www.ralinktech.com/
*/
#include "usbdevs.h"
#include <dev/usb/usb.h>
#include <dev/usb/usb_mfunc.h>
#include <dev/usb/usb_error.h>
#include <sys/param.h>
#include <sys/sockio.h>
#include <sys/sysctl.h>
#include <sys/lock.h>
#include <sys/mutex.h>
#include <sys/mbuf.h>
#include <sys/kernel.h>
#include <sys/socket.h>
#include <sys/systm.h>
#include <sys/malloc.h>
#include <sys/module.h>
#include <sys/bus.h>
#include <sys/endian.h>
#include <sys/kdb.h>
#include <machine/bus.h>
#include <machine/resource.h>
#include <sys/rman.h>
#include <net/bpf.h>
#include <net/if.h>
#include <net/if_arp.h>
#include <net/ethernet.h>
#include <net/if_dl.h>
#include <net/if_media.h>
#include <net/if_types.h>
#ifdef INET
#include <netinet/in.h>
#include <netinet/in_systm.h>
#include <netinet/in_var.h>
#include <netinet/if_ether.h>
#include <netinet/ip.h>
#endif
#include <net80211/ieee80211_var.h>
#include <net80211/ieee80211_regdomain.h>
#include <net80211/ieee80211_radiotap.h>
#include <net80211/ieee80211_amrr.h>
#define USB_DEBUG_VAR ural_debug
#include <dev/usb/usb.h>
#include <dev/usb/usb_error.h>
#include <dev/usb/usb_core.h>
#include <dev/usb/usb_lookup.h>
#include <dev/usb/usb_process.h>
#include <dev/usb/usb_debug.h>
#include <dev/usb/usb_request.h>
#include <dev/usb/usb_busdma.h>
#include <dev/usb/usb_util.h>
#include "usbdevs.h"
#include <dev/usb/wlan/usb_wlan.h>
#include <dev/usb/wlan/if_uralreg.h>
#include <dev/usb/wlan/if_uralvar.h>
@ -95,16 +132,6 @@ static const struct usb2_device_id ural_devs[] = {
static usb2_callback_t ural_bulk_read_callback;
static usb2_callback_t ural_bulk_write_callback;
static usb2_proc_callback_t ural_command_wrapper;
static usb2_proc_callback_t ural_attach_post;
static usb2_proc_callback_t ural_task;
static usb2_proc_callback_t ural_scantask;
static usb2_proc_callback_t ural_promisctask;
static usb2_proc_callback_t ural_amrr_task;
static usb2_proc_callback_t ural_init_task;
static usb2_proc_callback_t ural_stop_task;
static usb2_proc_callback_t ural_flush_task;
static usb2_error_t ural_do_request(struct ural_softc *sc,
struct usb2_device_request *req, void *data);
static struct ieee80211vap *ural_vap_create(struct ieee80211com *,
@ -156,21 +183,22 @@ static void ural_set_basicrates(struct ural_softc *,
static void ural_set_bssid(struct ural_softc *, const uint8_t *);
static void ural_set_macaddr(struct ural_softc *, uint8_t *);
static void ural_update_promisc(struct ifnet *);
static void ural_setpromisc(struct ural_softc *);
static const char *ural_get_rf(int);
static void ural_read_eeprom(struct ural_softc *);
static int ural_bbp_init(struct ural_softc *);
static void ural_set_txantenna(struct ural_softc *, int);
static void ural_set_rxantenna(struct ural_softc *, int);
static void ural_init_locked(struct ural_softc *);
static void ural_init(void *);
static void ural_stop(struct ural_softc *);
static int ural_raw_xmit(struct ieee80211_node *, struct mbuf *,
const struct ieee80211_bpf_params *);
static void ural_amrr_start(struct ural_softc *,
struct ieee80211_node *);
static void ural_amrr_timeout(void *);
static void ural_amrr_task(void *, int);
static int ural_pause(struct ural_softc *sc, int timeout);
static void ural_queue_command(struct ural_softc *,
usb2_proc_callback_t *, struct usb2_proc_msg *,
struct usb2_proc_msg *);
/*
* Default values for MAC registers; values taken from the reference driver.
@ -400,8 +428,10 @@ ural_attach(device_t self)
{
struct usb2_attach_arg *uaa = device_get_ivars(self);
struct ural_softc *sc = device_get_softc(self);
struct ifnet *ifp;
struct ieee80211com *ic;
uint8_t iface_index, bands;
int error;
uint8_t iface_index;
device_set_usb2_desc(self);
sc->sc_udev = uaa->device;
@ -410,8 +440,6 @@ ural_attach(device_t self)
mtx_init(&sc->sc_mtx, device_get_nameunit(self),
MTX_NETWORK_LOCK, MTX_DEF);
cv_init(&sc->sc_cmd_cv, "wtxdone");
iface_index = RAL_IFACE_INDEX;
error = usb2_transfer_setup(uaa->device,
&iface_index, sc->sc_xfer, ural_config,
@ -421,55 +449,22 @@ ural_attach(device_t self)
"err=%s\n", usb2_errstr(error));
goto detach;
}
error = usb2_proc_create(&sc->sc_tq, &sc->sc_mtx,
device_get_nameunit(self), USB_PRI_MED);
if (error) {
device_printf(self, "could not setup config thread!\n");
goto detach;
}
/* fork rest of the attach code */
RAL_LOCK(sc);
ural_queue_command(sc, ural_attach_post,
&sc->sc_synctask[0].hdr,
&sc->sc_synctask[1].hdr);
RAL_UNLOCK(sc);
return (0);
detach:
ural_detach(self);
return (ENXIO); /* failure */
}
static void
ural_attach_post(struct usb2_proc_msg *pm)
{
struct ural_task *task = (struct ural_task *)pm;
struct ural_softc *sc = task->sc;
struct ifnet *ifp;
struct ieee80211com *ic;
uint8_t bands;
/* retrieve RT2570 rev. no */
sc->asic_rev = ural_read(sc, RAL_MAC_CSR0);
/* retrieve MAC address and various other things from EEPROM */
ural_read_eeprom(sc);
/* XXX Async attach race */
if (usb2_proc_is_gone(&sc->sc_tq))
return;
RAL_UNLOCK(sc);
device_printf(sc->sc_dev, "MAC/BBP RT2570 (rev 0x%02x), RF %s\n",
device_printf(self, "MAC/BBP RT2570 (rev 0x%02x), RF %s\n",
sc->asic_rev, ural_get_rf(sc->rf_rev));
ifp = sc->sc_ifp = if_alloc(IFT_IEEE80211);
if (ifp == NULL) {
device_printf(sc->sc_dev, "can not if_alloc()\n");
RAL_LOCK(sc);
return;
goto detach;
}
ic = ifp->if_l2com;
@ -532,7 +527,11 @@ ural_attach_post(struct usb2_proc_msg *pm)
if (bootverbose)
ieee80211_announce(ic);
RAL_LOCK(sc);
return (0);
detach:
ural_detach(self);
return (ENXIO); /* failure */
}
static int
@ -542,12 +541,8 @@ ural_detach(device_t self)
struct ifnet *ifp = sc->sc_ifp;
struct ieee80211com *ic;
/* wait for any post attach or other command to complete */
usb2_proc_drain(&sc->sc_tq);
/* stop all USB transfers */
usb2_transfer_unsetup(sc->sc_xfer, URAL_N_TRANSFER);
usb2_proc_free(&sc->sc_tq);
/* free TX list, if any */
RAL_LOCK(sc);
@ -560,7 +555,6 @@ ural_detach(device_t self)
ieee80211_ifdetach(ic);
if_free(ifp);
}
cv_destroy(&sc->sc_cmd_cv);
mtx_destroy(&sc->sc_mtx);
return (0);
@ -574,7 +568,7 @@ ural_do_request(struct ural_softc *sc,
int ntries = 10;
while (ntries--) {
err = usb2_do_request_proc(sc->sc_udev, &sc->sc_tq,
err = usb2_do_request_flags(sc->sc_udev, &sc->sc_mtx,
req, data, 0, NULL, 250 /* ms */);
if (err == 0)
break;
@ -612,8 +606,8 @@ ural_vap_create(struct ieee80211com *ic,
uvp->newstate = vap->iv_newstate;
vap->iv_newstate = ural_newstate;
uvp->sc = sc;
usb2_callout_init_mtx(&uvp->amrr_ch, &sc->sc_mtx, 0);
TASK_INIT(&uvp->amrr_task, 0, ural_amrr_task, uvp);
ieee80211_amrr_init(&uvp->amrr, vap,
IEEE80211_AMRR_MIN_SUCCESS_THRESHOLD,
IEEE80211_AMRR_MAX_SUCCESS_THRESHOLD,
@ -625,26 +619,14 @@ ural_vap_create(struct ieee80211com *ic,
return vap;
}
static void
ural_flush_task(struct usb2_proc_msg *pm)
{
/* nothing to do */
}
static void
ural_vap_delete(struct ieee80211vap *vap)
{
struct ural_vap *uvp = URAL_VAP(vap);
struct ural_softc *sc = uvp->sc;
RAL_LOCK(sc);
/* wait for any pending tasks to complete */
ural_queue_command(sc, ural_flush_task,
&sc->sc_synctask[0].hdr,
&sc->sc_synctask[1].hdr);
RAL_UNLOCK(sc);
struct ieee80211com *ic = vap->iv_ic;
usb2_callout_drain(&uvp->amrr_ch);
ieee80211_draintask(ic, &uvp->amrr_task);
ieee80211_amrr_cleanup(&uvp->amrr);
ieee80211_vap_detach(vap);
free(uvp, M_80211_VAP);
@ -714,25 +696,27 @@ ural_unsetup_tx_list(struct ural_softc *sc)
}
}
static void
ural_task(struct usb2_proc_msg *pm)
static int
ural_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
{
struct ural_task *task = (struct ural_task *)pm;
struct ural_softc *sc = task->sc;
struct ifnet *ifp = sc->sc_ifp;
struct ieee80211com *ic = ifp->if_l2com;
struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
struct ural_vap *uvp = URAL_VAP(vap);
struct ieee80211com *ic = vap->iv_ic;
struct ural_softc *sc = ic->ic_ifp->if_softc;
const struct ieee80211_txparam *tp;
enum ieee80211_state ostate;
struct ieee80211_node *ni;
struct mbuf *m;
ostate = vap->iv_state;
DPRINTF("%s -> %s\n",
ieee80211_state_name[vap->iv_state],
ieee80211_state_name[nstate]);
switch (sc->sc_state) {
IEEE80211_UNLOCK(ic);
RAL_LOCK(sc);
usb2_callout_stop(&uvp->amrr_ch);
switch (nstate) {
case IEEE80211_S_INIT:
if (ostate == IEEE80211_S_RUN) {
if (vap->iv_state == IEEE80211_S_RUN) {
/* abort TSF synchronization */
ural_write(sc, RAL_TXRX_CSR19, 0);
@ -758,13 +742,17 @@ ural_task(struct usb2_proc_msg *pm)
if (m == NULL) {
device_printf(sc->sc_dev,
"could not allocate beacon\n");
return;
RAL_UNLOCK(sc);
IEEE80211_LOCK(ic);
return (-1);
}
ieee80211_ref_node(ni);
if (ural_tx_bcn(sc, m, ni) != 0) {
device_printf(sc->sc_dev,
"could not send beacon\n");
return;
RAL_UNLOCK(sc);
IEEE80211_LOCK(ic);
return (-1);
}
}
@ -784,75 +772,9 @@ ural_task(struct usb2_proc_msg *pm)
default:
break;
}
RAL_UNLOCK(sc);
IEEE80211_LOCK(ic);
uvp->newstate(vap, sc->sc_state, sc->sc_arg);
if (vap->iv_newstate_cb != NULL)
vap->iv_newstate_cb(vap, sc->sc_state, sc->sc_arg);
IEEE80211_UNLOCK(ic);
RAL_LOCK(sc);
}
static void
ural_scantask(struct usb2_proc_msg *pm)
{
struct ural_task *task = (struct ural_task *)pm;
struct ural_softc *sc = task->sc;
struct ifnet *ifp = sc->sc_ifp;
struct ieee80211com *ic = ifp->if_l2com;
RAL_LOCK_ASSERT(sc, MA_OWNED);
switch (sc->sc_scan_action) {
case URAL_SCAN_START:
/* abort TSF synchronization */
DPRINTF("starting scan\n");
ural_write(sc, RAL_TXRX_CSR19, 0);
ural_set_bssid(sc, ifp->if_broadcastaddr);
break;
case URAL_SET_CHANNEL:
ural_set_chan(sc, ic->ic_curchan);
break;
default: /* URAL_SCAN_END */
DPRINTF("stopping scan\n");
ural_enable_tsf_sync(sc);
ural_set_bssid(sc, sc->sc_bssid);
break;
}
}
static int
ural_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
{
struct ural_vap *uvp = URAL_VAP(vap);
struct ieee80211com *ic = vap->iv_ic;
struct ural_softc *sc = ic->ic_ifp->if_softc;
DPRINTF("%s -> %s\n",
ieee80211_state_name[vap->iv_state],
ieee80211_state_name[nstate]);
RAL_LOCK(sc);
usb2_callout_stop(&uvp->amrr_ch);
/* do it in a process context */
sc->sc_state = nstate;
sc->sc_arg = arg;
RAL_UNLOCK(sc);
if (nstate == IEEE80211_S_INIT) {
uvp->newstate(vap, nstate, arg);
return 0;
} else {
RAL_LOCK(sc);
ural_queue_command(sc, ural_task, &sc->sc_task[0].hdr,
&sc->sc_task[1].hdr);
RAL_UNLOCK(sc);
return EINPROGRESS;
}
return (uvp->newstate(vap, nstate, arg));
}
@ -867,10 +789,6 @@ ural_bulk_write_callback(struct usb2_xfer *xfer)
struct mbuf *m;
unsigned int len;
/* wakeup waiting command, if any */
if (sc->sc_last_task != NULL)
cv_signal(&sc->sc_cmd_cv);
switch (USB_GET_STATE(xfer)) {
case USB_ST_TRANSFERRED:
DPRINTFN(11, "transfer complete, %d bytes\n", xfer->actlen);
@ -886,10 +804,6 @@ ural_bulk_write_callback(struct usb2_xfer *xfer)
/* FALLTHROUGH */
case USB_ST_SETUP:
tr_setup:
/* wait for command to complete, if any */
if (sc->sc_last_task != NULL)
break;
data = STAILQ_FIRST(&sc->tx_q);
if (data) {
STAILQ_REMOVE_HEAD(&sc->tx_q, next);
@ -1458,20 +1372,13 @@ ural_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
RAL_LOCK(sc);
if (ifp->if_flags & IFF_UP) {
if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) {
ural_queue_command(sc, ural_init_task,
&sc->sc_synctask[0].hdr,
&sc->sc_synctask[1].hdr);
ural_init_locked(sc);
startall = 1;
} else
ural_queue_command(sc, ural_promisctask,
&sc->sc_promisctask[0].hdr,
&sc->sc_promisctask[1].hdr);
ural_setpromisc(sc);
} else {
if (ifp->if_drv_flags & IFF_DRV_RUNNING) {
ural_queue_command(sc, ural_stop_task,
&sc->sc_synctask[0].hdr,
&sc->sc_synctask[1].hdr);
}
if (ifp->if_drv_flags & IFF_DRV_RUNNING)
ural_stop(sc);
}
RAL_UNLOCK(sc);
if (startall)
@ -1699,15 +1606,13 @@ ural_newassoc(struct ieee80211_node *ni, int isnew)
static void
ural_scan_start(struct ieee80211com *ic)
{
struct ural_softc *sc = ic->ic_ifp->if_softc;
struct ifnet *ifp = ic->ic_ifp;
struct ural_softc *sc = ifp->if_softc;
RAL_LOCK(sc);
/* do it in a process context */
sc->sc_scan_action = URAL_SCAN_START;
ural_queue_command(sc, ural_scantask,
&sc->sc_scantask[0].hdr, &sc->sc_scantask[1].hdr);
ural_write(sc, RAL_TXRX_CSR19, 0);
ural_set_bssid(sc, ifp->if_broadcastaddr);
RAL_UNLOCK(sc);
}
static void
@ -1716,10 +1621,8 @@ ural_scan_end(struct ieee80211com *ic)
struct ural_softc *sc = ic->ic_ifp->if_softc;
RAL_LOCK(sc);
/* do it in a process context */
sc->sc_scan_action = URAL_SCAN_END;
ural_queue_command(sc, ural_scantask,
&sc->sc_scantask[0].hdr, &sc->sc_scantask[1].hdr);
ural_enable_tsf_sync(sc);
ural_set_bssid(sc, sc->sc_bssid);
RAL_UNLOCK(sc);
}
@ -1730,10 +1633,7 @@ ural_set_channel(struct ieee80211com *ic)
struct ural_softc *sc = ic->ic_ifp->if_softc;
RAL_LOCK(sc);
/* do it in a process context */
sc->sc_scan_action = URAL_SET_CHANNEL;
ural_queue_command(sc, ural_scantask,
&sc->sc_scantask[0].hdr, &sc->sc_scantask[1].hdr);
ural_set_chan(sc, ic->ic_curchan);
RAL_UNLOCK(sc);
}
@ -1994,10 +1894,8 @@ ural_set_macaddr(struct ural_softc *sc, uint8_t *addr)
}
static void
ural_promisctask(struct usb2_proc_msg *pm)
ural_setpromisc(struct ural_softc *sc)
{
struct ural_task *task = (struct ural_task *)pm;
struct ural_softc *sc = task->sc;
struct ifnet *ifp = sc->sc_ifp;
uint32_t tmp;
@ -2022,9 +1920,7 @@ ural_update_promisc(struct ifnet *ifp)
return;
RAL_LOCK(sc);
ural_queue_command(sc, ural_promisctask,
&sc->sc_promisctask[0].hdr,
&sc->sc_promisctask[1].hdr);
ural_setpromisc(sc);
RAL_UNLOCK(sc);
}
@ -2152,11 +2048,9 @@ ural_set_rxantenna(struct ural_softc *sc, int antenna)
}
static void
ural_init_task(struct usb2_proc_msg *pm)
ural_init_locked(struct ural_softc *sc)
{
#define N(a) (sizeof (a) / sizeof ((a)[0]))
struct ural_task *task = (struct ural_task *)pm;
struct ural_softc *sc = task->sc;
struct ifnet *ifp = sc->sc_ifp;
struct ieee80211com *ic = ifp->if_l2com;
uint16_t tmp;
@ -2167,7 +2061,7 @@ ural_init_task(struct usb2_proc_msg *pm)
ural_set_testmode(sc);
ural_write(sc, 0x308, 0x00f0); /* XXX magic */
ural_stop_task(pm);
ural_stop(sc);
/* initialize MAC registers to default values */
for (i = 0; i < N(ural_def_mac); i++)
@ -2229,7 +2123,7 @@ ural_init_task(struct usb2_proc_msg *pm)
usb2_transfer_start(sc->sc_xfer[URAL_BULK_RD]);
return;
fail: ural_stop_task(pm);
fail: ural_stop(sc);
#undef N
}
@ -2241,9 +2135,7 @@ ural_init(void *priv)
struct ieee80211com *ic = ifp->if_l2com;
RAL_LOCK(sc);
ural_queue_command(sc, ural_init_task,
&sc->sc_synctask[0].hdr,
&sc->sc_synctask[1].hdr);
ural_init_locked(sc);
RAL_UNLOCK(sc);
if (ifp->if_drv_flags & IFF_DRV_RUNNING)
@ -2251,10 +2143,8 @@ ural_init(void *priv)
}
static void
ural_stop_task(struct usb2_proc_msg *pm)
ural_stop(struct ural_softc *sc)
{
struct ural_task *task = (struct ural_task *)pm;
struct ural_softc *sc = task->sc;
struct ifnet *ifp = sc->sc_ifp;
RAL_LOCK_ASSERT(sc, MA_OWNED);
@ -2350,24 +2240,24 @@ static void
ural_amrr_timeout(void *arg)
{
struct ural_vap *uvp = arg;
struct ural_softc *sc = uvp->sc;
struct ieee80211vap *vap = &uvp->vap;
struct ieee80211com *ic = vap->iv_ic;
ural_queue_command(sc, ural_amrr_task,
&uvp->amrr_task[0].hdr, &uvp->amrr_task[1].hdr);
ieee80211_runtask(ic, &uvp->amrr_task);
}
static void
ural_amrr_task(struct usb2_proc_msg *pm)
ural_amrr_task(void *arg, int pending)
{
struct ural_task *task = (struct ural_task *)pm;
struct ural_softc *sc = task->sc;
struct ifnet *ifp = sc->sc_ifp;
struct ieee80211com *ic = ifp->if_l2com;
struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
struct ural_vap *uvp = URAL_VAP(vap);
struct ural_vap *uvp = arg;
struct ieee80211vap *vap = &uvp->vap;
struct ieee80211com *ic = vap->iv_ic;
struct ifnet *ifp = ic->ic_ifp;
struct ural_softc *sc = ifp->if_softc;
struct ieee80211_node *ni = vap->iv_bss;
int ok, fail;
RAL_LOCK(sc);
/* read and clear statistic registers (STA_CSR0 to STA_CSR10) */
ural_read_multi(sc, RAL_STA_CSR0, sc->sta, sizeof(sc->sta));
@ -2382,73 +2272,13 @@ ural_amrr_task(struct usb2_proc_msg *pm)
ifp->if_oerrors += fail; /* count TX retry-fail as Tx errors */
usb2_callout_reset(&uvp->amrr_ch, hz, ural_amrr_timeout, uvp);
RAL_UNLOCK(sc);
}
static int
ural_pause(struct ural_softc *sc, int timeout)
{
if (usb2_proc_is_gone(&sc->sc_tq))
return (1);
usb2_pause_mtx(&sc->sc_mtx, timeout);
return (0);
}
static void
ural_command_wrapper(struct usb2_proc_msg *pm)
{
struct ural_task *task = (struct ural_task *)pm;
struct ural_softc *sc = task->sc;
struct ifnet *ifp;
/* wait for pending transfer, if any */
while (usb2_transfer_pending(sc->sc_xfer[URAL_BULK_WR]))
cv_wait(&sc->sc_cmd_cv, &sc->sc_mtx);
/* make sure any hardware FIFOs are emptied */
ural_pause(sc, hz / 1000);
/* execute task */
task->func(pm);
/* check if this is the last task executed */
if (sc->sc_last_task == task) {
sc->sc_last_task = NULL;
ifp = sc->sc_ifp;
/* re-start TX, if any */
if ((ifp != NULL) && (ifp->if_drv_flags & IFF_DRV_RUNNING))
usb2_transfer_start(sc->sc_xfer[URAL_BULK_WR]);
}
}
static void
ural_queue_command(struct ural_softc *sc, usb2_proc_callback_t *fn,
struct usb2_proc_msg *t0, struct usb2_proc_msg *t1)
{
struct ural_task *task;
RAL_LOCK_ASSERT(sc, MA_OWNED);
/*
* NOTE: The task cannot get executed before we drop the
* "sc_mtx" mutex. It is safe to update fields in the message
* structure after that the message got queued.
*/
task = (struct ural_task *)
usb2_proc_msignal(&sc->sc_tq, t0, t1);
/* Setup callback and softc pointers */
task->hdr.pm_callback = ural_command_wrapper;
task->func = fn;
task->sc = sc;
/* Make sure that any TX operation will stop */
sc->sc_last_task = task;
/*
* Init, stop and flush must be synchronous!
*/
if ((fn == ural_init_task) || (fn == ural_stop_task) ||
(fn == ural_stop_task))
usb2_proc_mwait(&sc->sc_tq, t0, t1);
}

View File

@ -59,12 +59,6 @@ struct ural_tx_radiotap_header {
struct ural_softc;
struct ural_task {
struct usb2_proc_msg hdr;
usb2_proc_callback_t *func;
struct ural_softc *sc;
};
struct ural_tx_data {
STAILQ_ENTRY(ural_tx_data) next;
struct ural_softc *sc;
@ -83,11 +77,10 @@ struct ural_node {
struct ural_vap {
struct ieee80211vap vap;
struct ural_softc *sc;
struct ieee80211_beacon_offsets bo;
struct ieee80211_amrr amrr;
struct usb2_callout amrr_ch;
struct ural_task amrr_task[2];
struct task amrr_task;
int (*newstate)(struct ieee80211vap *,
enum ieee80211_state, int);
@ -104,21 +97,11 @@ struct ural_softc {
struct ifnet *sc_ifp;
device_t sc_dev;
struct usb2_device *sc_udev;
struct usb2_process sc_tq;
uint32_t asic_rev;
uint8_t rf_rev;
struct usb2_xfer *sc_xfer[URAL_N_TRANSFER];
struct ural_task *sc_last_task;
enum ieee80211_state sc_state;
int sc_arg;
int sc_scan_action; /* should be an enum */
struct ural_task sc_synctask[2];
struct ural_task sc_task[2];
struct ural_task sc_promisctask[2];
struct ural_task sc_scantask[2];
struct ural_tx_data tx_data[RAL_TX_LIST_COUNT];
ural_txdhead tx_q;
@ -127,7 +110,6 @@ struct ural_softc {
struct ural_rx_desc sc_rx_desc;
struct mtx sc_mtx;
struct cv sc_cmd_cv;
uint16_t sta[11];
uint32_t rf_regs[4];

View File

@ -26,20 +26,56 @@ __FBSDID("$FreeBSD$");
* ZyDAS ZD1211/ZD1211B USB WLAN driver.
*/
#include "usbdevs.h"
#include <dev/usb/usb.h>
#include <dev/usb/usb_mfunc.h>
#include <dev/usb/usb_error.h>
#include <sys/param.h>
#include <sys/sockio.h>
#include <sys/sysctl.h>
#include <sys/lock.h>
#include <sys/mutex.h>
#include <sys/mbuf.h>
#include <sys/kernel.h>
#include <sys/socket.h>
#include <sys/systm.h>
#include <sys/malloc.h>
#include <sys/module.h>
#include <sys/bus.h>
#include <sys/endian.h>
#include <sys/kdb.h>
#include <machine/bus.h>
#include <machine/resource.h>
#include <sys/rman.h>
#include <net/bpf.h>
#include <net/if.h>
#include <net/if_arp.h>
#include <net/ethernet.h>
#include <net/if_dl.h>
#include <net/if_media.h>
#include <net/if_types.h>
#ifdef INET
#include <netinet/in.h>
#include <netinet/in_systm.h>
#include <netinet/in_var.h>
#include <netinet/if_ether.h>
#include <netinet/ip.h>
#endif
#include <net80211/ieee80211_var.h>
#include <net80211/ieee80211_regdomain.h>
#include <net80211/ieee80211_radiotap.h>
#include <net80211/ieee80211_amrr.h>
#include <dev/usb/usb.h>
#include <dev/usb/usb_error.h>
#include <dev/usb/usb_core.h>
#include <dev/usb/usb_lookup.h>
#include <dev/usb/usb_process.h>
#include <dev/usb/usb_debug.h>
#include <dev/usb/usb_request.h>
#include <dev/usb/usb_busdma.h>
#include <dev/usb/usb_util.h>
#include "usbdevs.h"
#include <dev/usb/wlan/usb_wlan.h>
#include <dev/usb/wlan/if_zydreg.h>
#include <dev/usb/wlan/if_zydfw.h>
@ -74,7 +110,7 @@ enum {
#endif
#define zyd_do_request(sc,req,data) \
usb2_do_request_proc((sc)->sc_udev, &(sc)->sc_tq, req, data, 0, NULL, 5000)
usb2_do_request_flags((sc)->sc_udev, &(sc)->sc_mtx, req, data, 0, NULL, 5000)
static device_probe_t zyd_match;
static device_attach_t zyd_attach;
@ -85,14 +121,6 @@ static usb2_callback_t zyd_intr_write_callback;
static usb2_callback_t zyd_bulk_read_callback;
static usb2_callback_t zyd_bulk_write_callback;
static usb2_proc_callback_t zyd_attach_post;
static usb2_proc_callback_t zyd_task;
static usb2_proc_callback_t zyd_scantask;
static usb2_proc_callback_t zyd_multitask;
static usb2_proc_callback_t zyd_init_task;
static usb2_proc_callback_t zyd_stop_task;
static usb2_proc_callback_t zyd_flush_task;
static struct ieee80211vap *zyd_vap_create(struct ieee80211com *,
const char name[IFNAMSIZ], int unit, int opmode,
int flags, const uint8_t bssid[IEEE80211_ADDR_LEN],
@ -137,7 +165,9 @@ static void zyd_start(struct ifnet *);
static int zyd_raw_xmit(struct ieee80211_node *, struct mbuf *,
const struct ieee80211_bpf_params *);
static int zyd_ioctl(struct ifnet *, u_long, caddr_t);
static void zyd_init_locked(struct zyd_softc *);
static void zyd_init(void *);
static void zyd_stop(struct zyd_softc *);
static int zyd_loadfirmware(struct zyd_softc *);
static void zyd_newassoc(struct ieee80211_node *, int);
static void zyd_scan_start(struct ieee80211com *);
@ -166,8 +196,6 @@ static int zyd_maxim_set_channel(struct zyd_rf *, uint8_t);
static int zyd_maxim2_init(struct zyd_rf *);
static int zyd_maxim2_switch_radio(struct zyd_rf *, int);
static int zyd_maxim2_set_channel(struct zyd_rf *, uint8_t);
static void zyd_queue_command(struct zyd_softc *, usb2_proc_callback_t *,
struct usb2_proc_msg *, struct usb2_proc_msg *);
static const struct zyd_phy_pair zyd_def_phy[] = ZYD_DEF_PHY;
static const struct zyd_phy_pair zyd_def_phyB[] = ZYD_DEF_PHYB;
@ -307,8 +335,10 @@ zyd_attach(device_t dev)
{
struct usb2_attach_arg *uaa = device_get_ivars(dev);
struct zyd_softc *sc = device_get_softc(dev);
struct ifnet *ifp;
struct ieee80211com *ic;
uint8_t iface_index, bands;
int error;
uint8_t iface_index;
if (uaa->info.bcdDevice < 0x4330) {
device_printf(dev, "device version mismatch: 0x%X "
@ -324,7 +354,6 @@ zyd_attach(device_t dev)
mtx_init(&sc->sc_mtx, device_get_nameunit(sc->sc_dev),
MTX_NETWORK_LOCK, MTX_DEF);
cv_init(&sc->sc_cmd_cv, "wtxdone");
STAILQ_INIT(&sc->sc_rqh);
iface_index = ZYD_IFACE_INDEX;
@ -336,52 +365,19 @@ zyd_attach(device_t dev)
"err=%s\n", usb2_errstr(error));
goto detach;
}
error = usb2_proc_create(&sc->sc_tq, &sc->sc_mtx,
device_get_nameunit(dev), USB_PRI_MED);
if (error) {
device_printf(dev, "could not setup config thread!\n");
goto detach;
}
/* fork rest of the attach code */
ZYD_LOCK(sc);
zyd_queue_command(sc, zyd_attach_post,
&sc->sc_synctask[0].hdr,
&sc->sc_synctask[1].hdr);
ZYD_UNLOCK(sc);
return (0);
detach:
zyd_detach(dev);
return (ENXIO); /* failure */
}
static void
zyd_attach_post(struct usb2_proc_msg *pm)
{
struct zyd_task *task = (struct zyd_task *)pm;
struct zyd_softc *sc = task->sc;
struct ifnet *ifp;
struct ieee80211com *ic;
int error;
uint8_t bands;
if ((error = zyd_get_macaddr(sc)) != 0) {
device_printf(sc->sc_dev, "could not read EEPROM\n");
return;
ZYD_UNLOCK(sc);
goto detach;
}
/* XXX Async attach race */
if (usb2_proc_is_gone(&sc->sc_tq))
return;
ZYD_UNLOCK(sc);
ifp = sc->sc_ifp = if_alloc(IFT_IEEE80211);
if (ifp == NULL) {
device_printf(sc->sc_dev, "can not if_alloc()\n");
ZYD_LOCK(sc);
return;
goto detach;
}
ifp->if_softc = sc;
if_initname(ifp, "zyd", device_get_unit(sc->sc_dev));
@ -437,7 +433,11 @@ zyd_attach_post(struct usb2_proc_msg *pm)
if (bootverbose)
ieee80211_announce(ic);
ZYD_LOCK(sc);
return (0);
detach:
zyd_detach(dev);
return (ENXIO); /* failure */
}
static int
@ -447,12 +447,8 @@ zyd_detach(device_t dev)
struct ifnet *ifp = sc->sc_ifp;
struct ieee80211com *ic;
/* wait for any post attach or other command to complete */
usb2_proc_drain(&sc->sc_tq);
/* stop all USB transfers */
usb2_transfer_unsetup(sc->sc_xfer, ZYD_N_TRANSFER);
usb2_proc_free(&sc->sc_tq);
/* free TX list, if any */
zyd_unsetup_tx_list(sc);
@ -463,7 +459,6 @@ zyd_detach(device_t dev)
ieee80211_ifdetach(ic);
if_free(ifp);
}
cv_destroy(&sc->sc_cmd_cv);
mtx_destroy(&sc->sc_mtx);
return (0);
@ -475,7 +470,6 @@ zyd_vap_create(struct ieee80211com *ic,
const uint8_t bssid[IEEE80211_ADDR_LEN],
const uint8_t mac[IEEE80211_ADDR_LEN])
{
struct zyd_softc *sc = ic->ic_ifp->if_softc;
struct zyd_vap *zvp;
struct ieee80211vap *vap;
@ -494,7 +488,6 @@ zyd_vap_create(struct ieee80211com *ic,
zvp->newstate = vap->iv_newstate;
vap->iv_newstate = zyd_newstate;
zvp->sc = sc;
ieee80211_amrr_init(&zvp->amrr, vap,
IEEE80211_AMRR_MIN_SUCCESS_THRESHOLD,
IEEE80211_AMRR_MAX_SUCCESS_THRESHOLD,
@ -507,24 +500,10 @@ zyd_vap_create(struct ieee80211com *ic,
return (vap);
}
static void
zyd_flush_task(struct usb2_proc_msg *_pm)
{
/* nothing to do */
}
static void
zyd_vap_delete(struct ieee80211vap *vap)
{
struct zyd_vap *zvp = ZYD_VAP(vap);
struct zyd_softc *sc = zvp->sc;
ZYD_LOCK(sc);
/* wait for any pending tasks to complete */
zyd_queue_command(sc, zyd_flush_task,
&sc->sc_synctask[0].hdr,
&sc->sc_synctask[1].hdr);
ZYD_UNLOCK(sc);
ieee80211_amrr_cleanup(&zvp->amrr);
ieee80211_vap_detach(vap);
@ -606,34 +585,38 @@ zyd_node_alloc(struct ieee80211vap *vap __unused,
return (zn != NULL) ? (&zn->ni) : (NULL);
}
static void
zyd_task(struct usb2_proc_msg *pm)
static int
zyd_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
{
struct zyd_task *task = (struct zyd_task *)pm;
struct zyd_softc *sc = task->sc;
struct ifnet *ifp = sc->sc_ifp;
struct ieee80211com *ic = ifp->if_l2com;
struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
struct ieee80211_node *ni = vap->iv_bss;
struct zyd_vap *zvp = ZYD_VAP(vap);
struct ieee80211com *ic = vap->iv_ic;
struct zyd_softc *sc = ic->ic_ifp->if_softc;
struct ieee80211_node *ni;
int error;
switch (sc->sc_state) {
DPRINTF(sc, ZYD_DEBUG_STATE, "%s: %s -> %s\n", __func__,
ieee80211_state_name[vap->iv_state],
ieee80211_state_name[nstate]);
IEEE80211_UNLOCK(ic);
ZYD_LOCK(sc);
switch (nstate) {
case IEEE80211_S_AUTH:
zyd_set_chan(sc, ic->ic_curchan);
break;
case IEEE80211_S_RUN:
ni = vap->iv_bss;
if (vap->iv_opmode == IEEE80211_M_MONITOR)
break;
/* turn link LED on */
error = zyd_set_led(sc, ZYD_LED1, 1);
if (error != 0)
goto fail;
break;
/* make data LED blink upon Tx */
zyd_write32_m(sc, sc->sc_fwbase + ZYD_FW_LINK_STATUS, 1);
IEEE80211_ADDR_COPY(sc->sc_bssid, ni->ni_bssid);
zyd_set_bssid(sc, sc->sc_bssid);
break;
@ -643,40 +626,7 @@ zyd_task(struct usb2_proc_msg *pm)
fail:
ZYD_UNLOCK(sc);
IEEE80211_LOCK(ic);
zvp->newstate(vap, sc->sc_state, sc->sc_arg);
if (vap->iv_newstate_cb != NULL)
vap->iv_newstate_cb(vap, sc->sc_state, sc->sc_arg);
IEEE80211_UNLOCK(ic);
ZYD_LOCK(sc);
}
static int
zyd_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
{
struct zyd_vap *zvp = ZYD_VAP(vap);
struct ieee80211com *ic = vap->iv_ic;
struct zyd_softc *sc = ic->ic_ifp->if_softc;
DPRINTF(sc, ZYD_DEBUG_STATE, "%s: %s -> %s\n", __func__,
ieee80211_state_name[vap->iv_state],
ieee80211_state_name[nstate]);
ZYD_LOCK(sc);
/* do it in a process context */
sc->sc_state = nstate;
sc->sc_arg = arg;
ZYD_UNLOCK(sc);
if (nstate == IEEE80211_S_INIT) {
zvp->newstate(vap, nstate, arg);
return (0);
} else {
ZYD_LOCK(sc);
zyd_queue_command(sc, zyd_task, &sc->sc_task[0].hdr,
&sc->sc_task[1].hdr);
ZYD_UNLOCK(sc);
return (EINPROGRESS);
}
return (zvp->newstate(vap, nstate, arg));
}
/*
@ -845,9 +795,6 @@ zyd_cmd(struct zyd_softc *sc, uint16_t code, const void *idata, int ilen,
if (ilen > sizeof(cmd.data))
return (EINVAL);
if (usb2_proc_is_gone(&sc->sc_tq))
return (ENXIO);
cmd.code = htole16(code);
bcopy(idata, cmd.data, ilen);
DPRINTF(sc, ZYD_DEBUG_CMD, "sending cmd %p = %*D\n",
@ -2011,15 +1958,6 @@ zyd_set_led(struct zyd_softc *sc, int which, int on)
return (error);
}
static void
zyd_multitask(struct usb2_proc_msg *pm)
{
struct zyd_task *task = (struct zyd_task *)pm;
struct zyd_softc *sc = task->sc;
zyd_set_multi(sc);
}
static void
zyd_set_multi(struct zyd_softc *sc)
{
@ -2073,8 +2011,7 @@ zyd_update_mcast(struct ifnet *ifp)
return;
ZYD_LOCK(sc);
zyd_queue_command(sc, zyd_multitask,
&sc->sc_mcasttask[0].hdr, &sc->sc_mcasttask[1].hdr);
zyd_set_multi(sc);
ZYD_UNLOCK(sc);
}
@ -2502,10 +2439,6 @@ zyd_bulk_write_callback(struct usb2_xfer *xfer)
struct zyd_tx_data *data;
struct mbuf *m;
/* wakeup any waiting command, if any */
if (sc->sc_last_task != NULL)
cv_signal(&sc->sc_cmd_cv);
switch (USB_GET_STATE(xfer)) {
case USB_ST_TRANSFERRED:
DPRINTF(sc, ZYD_DEBUG_ANY, "transfer complete, %u bytes\n",
@ -2522,10 +2455,6 @@ zyd_bulk_write_callback(struct usb2_xfer *xfer)
/* FALLTHROUGH */
case USB_ST_SETUP:
tr_setup:
/* wait for command to complete, if any */
if (sc->sc_last_task != NULL)
break;
data = STAILQ_FIRST(&sc->tx_q);
if (data) {
STAILQ_REMOVE_HEAD(&sc->tx_q, next);
@ -2757,22 +2686,14 @@ zyd_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
case SIOCSIFFLAGS:
ZYD_LOCK(sc);
if (ifp->if_flags & IFF_UP) {
if (ifp->if_drv_flags & IFF_DRV_RUNNING) {
zyd_queue_command(sc, zyd_multitask,
&sc->sc_mcasttask[0].hdr,
&sc->sc_mcasttask[1].hdr);
} else {
zyd_queue_command(sc, zyd_init_task,
&sc->sc_synctask[0].hdr,
&sc->sc_synctask[1].hdr);
if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) {
zyd_init_locked(sc);
startall = 1;
}
} else
zyd_set_multi(sc);
} else {
if (ifp->if_drv_flags & IFF_DRV_RUNNING) {
zyd_queue_command(sc, zyd_stop_task,
&sc->sc_synctask[0].hdr,
&sc->sc_synctask[1].hdr);
}
if (ifp->if_drv_flags & IFF_DRV_RUNNING)
zyd_stop(sc);
}
ZYD_UNLOCK(sc);
if (startall)
@ -2792,10 +2713,8 @@ zyd_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
}
static void
zyd_init_task(struct usb2_proc_msg *pm)
zyd_init_locked(struct zyd_softc *sc)
{
struct zyd_task *task = (struct zyd_task *)pm;
struct zyd_softc *sc = task->sc;
struct ifnet *ifp = sc->sc_ifp;
struct ieee80211com *ic = ifp->if_l2com;
struct usb2_config_descriptor *cd;
@ -2850,7 +2769,7 @@ zyd_init_task(struct usb2_proc_msg *pm)
}
if (ifp->if_drv_flags & IFF_DRV_RUNNING)
zyd_stop_task(pm);
zyd_stop(sc);
DPRINTF(sc, ZYD_DEBUG_INIT, "setting MAC address to %6D\n",
IF_LLADDR(ifp), ":");
@ -2898,7 +2817,7 @@ zyd_init_task(struct usb2_proc_msg *pm)
return;
fail: zyd_stop_task(pm);
fail: zyd_stop(sc);
return;
}
@ -2910,9 +2829,7 @@ zyd_init(void *priv)
struct ieee80211com *ic = ifp->if_l2com;
ZYD_LOCK(sc);
zyd_queue_command(sc, zyd_init_task,
&sc->sc_synctask[0].hdr,
&sc->sc_synctask[1].hdr);
zyd_init_locked(sc);
ZYD_UNLOCK(sc);
if (ifp->if_drv_flags & IFF_DRV_RUNNING)
@ -2920,10 +2837,8 @@ zyd_init(void *priv)
}
static void
zyd_stop_task(struct usb2_proc_msg *pm)
zyd_stop(struct zyd_softc *sc)
{
struct zyd_task *task = (struct zyd_task *)pm;
struct zyd_softc *sc = task->sc;
struct ifnet *ifp = sc->sc_ifp;
int error;
@ -3029,13 +2944,12 @@ zyd_newassoc(struct ieee80211_node *ni, int isnew)
static void
zyd_scan_start(struct ieee80211com *ic)
{
struct zyd_softc *sc = ic->ic_ifp->if_softc;
struct ifnet *ifp = ic->ic_ifp;
struct zyd_softc *sc = ifp->if_softc;
ZYD_LOCK(sc);
/* do it in a process context */
sc->sc_scan_action = ZYD_SCAN_START;
zyd_queue_command(sc, zyd_scantask,
&sc->sc_scantask[0].hdr, &sc->sc_scantask[1].hdr);
/* want broadcast address while scanning */
zyd_set_bssid(sc, ifp->if_broadcastaddr);
ZYD_UNLOCK(sc);
}
@ -3045,10 +2959,8 @@ zyd_scan_end(struct ieee80211com *ic)
struct zyd_softc *sc = ic->ic_ifp->if_softc;
ZYD_LOCK(sc);
/* do it in a process context */
sc->sc_scan_action = ZYD_SCAN_END;
zyd_queue_command(sc, zyd_scantask,
&sc->sc_scantask[0].hdr, &sc->sc_scantask[1].hdr);
/* restore previous bssid */
zyd_set_bssid(sc, sc->sc_bssid);
ZYD_UNLOCK(sc);
}
@ -3058,105 +2970,16 @@ zyd_set_channel(struct ieee80211com *ic)
struct zyd_softc *sc = ic->ic_ifp->if_softc;
ZYD_LOCK(sc);
/* do it in a process context */
sc->sc_scan_action = ZYD_SET_CHANNEL;
zyd_queue_command(sc, zyd_scantask,
&sc->sc_scantask[0].hdr, &sc->sc_scantask[1].hdr);
zyd_set_chan(sc, ic->ic_curchan);
ZYD_UNLOCK(sc);
}
static void
zyd_scantask(struct usb2_proc_msg *pm)
{
struct zyd_task *task = (struct zyd_task *)pm;
struct zyd_softc *sc = task->sc;
struct ifnet *ifp = sc->sc_ifp;
struct ieee80211com *ic = ifp->if_l2com;
ZYD_LOCK_ASSERT(sc, MA_OWNED);
switch (sc->sc_scan_action) {
case ZYD_SCAN_START:
/* want broadcast address while scanning */
zyd_set_bssid(sc, ifp->if_broadcastaddr);
break;
case ZYD_SET_CHANNEL:
zyd_set_chan(sc, ic->ic_curchan);
break;
default: /* ZYD_SCAN_END */
/* restore previous bssid */
zyd_set_bssid(sc, sc->sc_bssid);
break;
}
}
static void
zyd_command_wrapper(struct usb2_proc_msg *pm)
{
struct zyd_task *task = (struct zyd_task *)pm;
struct zyd_softc *sc = task->sc;
struct ifnet *ifp;
/* wait for pending transfer, if any */
while (usb2_transfer_pending(sc->sc_xfer[ZYD_BULK_WR]))
cv_wait(&sc->sc_cmd_cv, &sc->sc_mtx);
/* make sure any hardware FIFOs are emptied */
usb2_pause_mtx(&sc->sc_mtx, hz / 1000);
/* execute task */
task->func(pm);
/* check if this is the last task executed */
if (sc->sc_last_task == task) {
sc->sc_last_task = NULL;
ifp = sc->sc_ifp;
/* re-start TX, if any */
if ((ifp != NULL) && (ifp->if_drv_flags & IFF_DRV_RUNNING))
usb2_transfer_start(sc->sc_xfer[ZYD_BULK_WR]);
}
}
static void
zyd_queue_command(struct zyd_softc *sc, usb2_proc_callback_t *fn,
struct usb2_proc_msg *t0, struct usb2_proc_msg *t1)
{
struct zyd_task *task;
ZYD_LOCK_ASSERT(sc, MA_OWNED);
/*
* NOTE: The task cannot get executed before we drop the
* "sc_mtx" mutex. It is safe to update fields in the message
* structure after that the message got queued.
*/
task = (struct zyd_task *)
usb2_proc_msignal(&sc->sc_tq, t0, t1);
/* Setup callback and softc pointers */
task->hdr.pm_callback = zyd_command_wrapper;
task->func = fn;
task->sc = sc;
/* Make sure that any TX operation will stop */
sc->sc_last_task = task;
/*
* Init and stop must be synchronous!
*/
if ((fn == zyd_init_task) || (fn == zyd_stop_task) ||
(fn == zyd_flush_task))
usb2_proc_mwait(&sc->sc_tq, t0, t1);
}
static device_method_t zyd_methods[] = {
/* Device interface */
DEVMETHOD(device_probe, zyd_match),
DEVMETHOD(device_attach, zyd_attach),
DEVMETHOD(device_detach, zyd_detach),
{ 0, 0 }
};

View File

@ -1163,12 +1163,6 @@ struct zyd_mac_pair {
uint32_t val;
};
struct zyd_task {
struct usb2_proc_msg hdr;
usb2_proc_callback_t *func;
struct zyd_softc *sc;
};
struct zyd_tx_data {
STAILQ_ENTRY(zyd_tx_data) next;
struct zyd_softc *sc;
@ -1248,7 +1242,6 @@ struct zyd_vap {
struct ieee80211vap vap;
int (*newstate)(struct ieee80211vap *,
enum ieee80211_state, int);
struct zyd_softc *sc;
struct ieee80211_amrr amrr;
};
#define ZYD_VAP(vap) ((struct zyd_vap *)(vap))
@ -1265,27 +1258,14 @@ struct zyd_softc {
struct ifnet *sc_ifp;
device_t sc_dev;
struct usb2_device *sc_udev;
struct usb2_process sc_tq;
struct usb2_xfer *sc_xfer[ZYD_N_TRANSFER];
struct zyd_task *sc_last_task;
enum ieee80211_state sc_state;
int sc_arg;
int sc_flags;
#define ZYD_FLAG_FWLOADED (1 << 0)
#define ZYD_FLAG_INITONCE (1 << 1)
#define ZYD_FLAG_INITDONE (1 << 2)
struct zyd_task sc_synctask[2];
struct zyd_task sc_mcasttask[2];
struct zyd_task sc_scantask[2];
int sc_scan_action;
#define ZYD_SCAN_START 0
#define ZYD_SCAN_END 1
#define ZYD_SET_CHANNEL 2
struct zyd_task sc_task[2];
struct zyd_rf sc_rf;
STAILQ_HEAD(, zyd_rq) sc_rtx;
@ -1317,7 +1297,6 @@ struct zyd_softc {
uint8_t sc_ofdm54_cal[14];
struct mtx sc_mtx;
struct cv sc_cmd_cv;
struct zyd_tx_data tx_data[ZYD_TX_LIST_CNT];
zyd_txdhead tx_q;
zyd_txdhead tx_free;

View File

@ -1,56 +0,0 @@
/* $FreeBSD$ */
/*-
* Copyright (c) 2008 Hans Petter Selasky. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#ifndef _USB2_WLAN_H_
#define _USB2_WLAN_H_
#include <sys/param.h>
#include <sys/sockio.h>
#include <sys/mbuf.h>
#include <sys/kernel.h>
#include <sys/socket.h>
#include <net/bpf.h>
#include <net/if.h>
#include <net/if_arp.h>
#include <net/ethernet.h>
#include <net/if_dl.h>
#include <net/if_media.h>
#include <net/if_types.h>
#include <net/if_clone.h>
#include <net80211/ieee80211_var.h>
#include <net80211/ieee80211_radiotap.h>
#include <net80211/ieee80211_amrr.h>
#include <net80211/ieee80211_regdomain.h>
#include <netinet/in.h>
#include <netinet/in_systm.h>
#include <netinet/in_var.h>
#include <netinet/ip.h>
#include <netinet/if_ether.h>
#endif /* _USB2_WLAN_H_ */

View File

@ -78,7 +78,6 @@ __FBSDID("$FreeBSD$");
#include <sys/random.h>
#include <sys/syslog.h>
#include <sys/sysctl.h>
#include <sys/taskqueue.h>
#include <machine/bus.h>
#include <machine/resource.h>
@ -134,10 +133,6 @@ static void wi_rx_intr(struct wi_softc *);
static void wi_tx_intr(struct wi_softc *);
static void wi_tx_ex_intr(struct wi_softc *);
static void wi_status_connected(void *, int);
static void wi_status_disconnected(void *, int);
static void wi_status_oor(void *, int);
static void wi_status_assoc_failed(void *, int);
static void wi_info_intr(struct wi_softc *);
static int wi_write_txrate(struct wi_softc *, struct ieee80211vap *);
@ -451,7 +446,6 @@ wi_attach(device_t dev)
}
sc->sc_portnum = WI_DEFAULT_PORT;
TASK_INIT(&sc->sc_oor_task, 0, wi_status_oor, ic);
ieee80211_ifattach(ic, macaddr);
ic->ic_raw_xmit = wi_raw_xmit;
@ -574,10 +568,6 @@ wi_vap_create(struct ieee80211com *ic,
break;
}
TASK_INIT(&wvp->wv_connected_task, 0, wi_status_connected, vap);
TASK_INIT(&wvp->wv_disconnected_task, 0, wi_status_disconnected, vap);
TASK_INIT(&wvp->wv_assoc_failed_task, 0, wi_status_assoc_failed, vap);
/* complete setup */
ieee80211_vap_attach(vap, ieee80211_media_change, wi_media_status);
ic->ic_opmode = opmode;
@ -902,7 +892,7 @@ wi_newstate_sta(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
* notification we get on association.
*/
vap->iv_state = nstate;
return EINPROGRESS;
return (0);
}
return WI_VAP(vap)->wv_newstate(vap, nstate, arg);
}
@ -1512,55 +1502,12 @@ wi_tx_intr(struct wi_softc *sc)
}
}
static void
wi_status_connected(void *arg, int pending)
{
struct ieee80211vap *vap = arg;
struct ieee80211com *ic = vap->iv_ic;
IEEE80211_LOCK(ic);
vap->iv_bss->ni_associd = 1 | 0xc000; /* NB: anything will do */
WI_VAP(vap)->wv_newstate(vap, IEEE80211_S_RUN, 0);
if (vap->iv_newstate_cb != NULL)
vap->iv_newstate_cb(vap, IEEE80211_S_RUN, 0);
IEEE80211_UNLOCK(ic);
}
static void
wi_status_disconnected(void *arg, int pending)
{
struct ieee80211vap *vap = arg;
if (vap->iv_state == IEEE80211_S_RUN) {
vap->iv_bss->ni_associd = 0;
vap->iv_stats.is_rx_deauth++;
ieee80211_new_state(vap, IEEE80211_S_SCAN, 0);
}
}
static void
wi_status_oor(void *arg, int pending)
{
struct ieee80211com *ic = arg;
ieee80211_beacon_miss(ic);
}
static void
wi_status_assoc_failed(void *arg, int pending)
{
struct ieee80211vap *vap = arg;
ieee80211_new_state(vap, IEEE80211_S_SCAN, IEEE80211_SCAN_FAIL_TIMEOUT);
}
static __noinline void
wi_info_intr(struct wi_softc *sc)
{
struct ifnet *ifp = sc->sc_ifp;
struct ieee80211com *ic = ifp->if_l2com;
struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
struct wi_vap *wvp = WI_VAP(vap);
int i, fid, len, off;
u_int16_t ltbuf[2];
u_int16_t stat;
@ -1580,22 +1527,29 @@ wi_info_intr(struct wi_softc *sc)
break;
/* fall thru... */
case WI_INFO_LINK_STAT_AP_CHG:
taskqueue_enqueue(taskqueue_swi, &wvp->wv_connected_task);
IEEE80211_LOCK(ic);
vap->iv_bss->ni_associd = 1 | 0xc000; /* NB: anything will do */
ieee80211_new_state(vap, IEEE80211_S_RUN, 0);
IEEE80211_UNLOCK(ic);
break;
case WI_INFO_LINK_STAT_AP_INR:
break;
case WI_INFO_LINK_STAT_DISCONNECTED:
/* we dropped off the net; e.g. due to deauth/disassoc */
taskqueue_enqueue(taskqueue_swi, &wvp->wv_disconnected_task);
IEEE80211_LOCK(ic);
vap->iv_bss->ni_associd = 0;
vap->iv_stats.is_rx_deauth++;
ieee80211_new_state(vap, IEEE80211_S_SCAN, 0);
IEEE80211_UNLOCK(ic);
break;
case WI_INFO_LINK_STAT_AP_OOR:
/* XXX does this need to be per-vap? */
taskqueue_enqueue(taskqueue_swi, &sc->sc_oor_task);
ieee80211_beacon_miss(ic);
break;
case WI_INFO_LINK_STAT_ASSOC_FAILED:
if (vap->iv_opmode == IEEE80211_M_STA)
taskqueue_enqueue(taskqueue_swi,
&wvp->wv_assoc_failed_task);
ieee80211_new_state(vap, IEEE80211_S_SCAN,
IEEE80211_SCAN_FAIL_TIMEOUT);
break;
}
break;

View File

@ -59,9 +59,6 @@
struct wi_vap {
struct ieee80211vap wv_vap;
struct ieee80211_beacon_offsets wv_bo;
struct task wv_connected_task;
struct task wv_disconnected_task;
struct task wv_assoc_failed_task;
void (*wv_recv_mgmt)(struct ieee80211_node *,
struct mbuf *, int, int, int, u_int32_t);
@ -75,7 +72,6 @@ struct wi_softc {
device_t sc_dev;
struct mtx sc_mtx;
struct callout sc_watchdog;
struct task sc_oor_task;
int sc_unit;
int wi_gone;
int sc_enabled;

View File

@ -192,12 +192,9 @@ static void wpi_rx_intr(struct wpi_softc *, struct wpi_rx_desc *,
struct wpi_rx_data *);
static void wpi_tx_intr(struct wpi_softc *, struct wpi_rx_desc *);
static void wpi_cmd_intr(struct wpi_softc *, struct wpi_rx_desc *);
static void wpi_bmiss(void *, int);
static void wpi_notif_intr(struct wpi_softc *);
static void wpi_intr(void *);
static void wpi_ops(void *, int);
static uint8_t wpi_plcp_signal(int);
static int wpi_queue_cmd(struct wpi_softc *, int, int, int);
static void wpi_watchdog(void *);
static int wpi_tx_data(struct wpi_softc *, struct mbuf *,
struct ieee80211_node *, int);
@ -230,6 +227,8 @@ static int wpi_config(struct wpi_softc *);
static void wpi_stop_master(struct wpi_softc *);
static int wpi_power_up(struct wpi_softc *);
static int wpi_reset(struct wpi_softc *);
static void wpi_hwreset(void *, int);
static void wpi_rfreset(void *, int);
static void wpi_hw_config(struct wpi_softc *);
static void wpi_init(void *);
static void wpi_init_locked(struct wpi_softc *, int);
@ -514,21 +513,11 @@ wpi_attach(device_t dev)
}
}
/*
* Create the taskqueues used by the driver. Primarily
* sc_tq handles most the task
*/
sc->sc_tq = taskqueue_create("wpi_taskq", M_NOWAIT | M_ZERO,
taskqueue_thread_enqueue, &sc->sc_tq);
taskqueue_start_threads(&sc->sc_tq, 1, PI_NET, "%s taskq",
device_get_nameunit(dev));
/* Create the tasks that can be queued */
TASK_INIT(&sc->sc_opstask, 0, wpi_ops, sc);
TASK_INIT(&sc->sc_bmiss_task, 0, wpi_bmiss, sc);
TASK_INIT(&sc->sc_restarttask, 0, wpi_hwreset, sc);
TASK_INIT(&sc->sc_radiotask, 0, wpi_rfreset, sc);
WPI_LOCK_INIT(sc);
WPI_CMD_LOCK_INIT(sc);
callout_init_mtx(&sc->calib_to, &sc->sc_mtx, 0);
callout_init_mtx(&sc->watchdog_to, &sc->sc_mtx, 0);
@ -732,6 +721,9 @@ wpi_detach(device_t dev)
struct ieee80211com *ic = ifp->if_l2com;
int ac;
ieee80211_draintask(ic, &sc->sc_restarttask);
ieee80211_draintask(ic, &sc->sc_radiotask);
if (ifp != NULL) {
wpi_stop(sc);
callout_drain(&sc->watchdog_to);
@ -769,10 +761,7 @@ wpi_detach(device_t dev)
if (ifp != NULL)
if_free(ifp);
taskqueue_free(sc->sc_tq);
WPI_LOCK_DESTROY(sc);
WPI_CMD_LOCK_DESTROY(sc);
return 0;
}
@ -1280,21 +1269,32 @@ wpi_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
ieee80211_state_name[vap->iv_state],
ieee80211_state_name[nstate], sc->flags));
IEEE80211_UNLOCK(ic);
WPI_LOCK(sc);
if (nstate == IEEE80211_S_AUTH) {
/* Delay the auth transition until we can update the firmware */
error = wpi_queue_cmd(sc, WPI_AUTH, arg, WPI_QUEUE_NORMAL);
return (error != 0 ? error : EINPROGRESS);
/* The node must be registered in the firmware before auth */
error = wpi_auth(sc, vap);
if (error != 0) {
device_printf(sc->sc_dev,
"%s: could not move to auth state, error %d\n",
__func__, error);
}
}
if (nstate == IEEE80211_S_RUN && vap->iv_state != IEEE80211_S_RUN) {
/* set the association id first */
error = wpi_queue_cmd(sc, WPI_RUN, arg, WPI_QUEUE_NORMAL);
return (error != 0 ? error : EINPROGRESS);
error = wpi_run(sc, vap);
if (error != 0) {
device_printf(sc->sc_dev,
"%s: could not move to run state, error %d\n",
__func__, error);
}
}
if (nstate == IEEE80211_S_RUN) {
/* RUN -> RUN transition; just restart the timers */
wpi_calib_timeout(sc);
/* XXX split out rate control timer */
}
WPI_UNLOCK(sc);
IEEE80211_LOCK(ic);
return wvp->newstate(vap, nstate, arg);
}
@ -1641,15 +1641,6 @@ wpi_cmd_intr(struct wpi_softc *sc, struct wpi_rx_desc *desc)
wakeup(&ring->cmd[desc->idx]);
}
static void
wpi_bmiss(void *arg, int npending)
{
struct wpi_softc *sc = arg;
struct ieee80211com *ic = sc->sc_ifp->if_l2com;
ieee80211_beacon_miss(ic);
}
static void
wpi_notif_intr(struct wpi_softc *sc)
{
@ -1759,8 +1750,7 @@ wpi_notif_intr(struct wpi_softc *sc)
DPRINTF(("Beacon miss: %u >= %u\n",
le32toh(beacon->consecutive),
vap->iv_bmissthreshold));
taskqueue_enqueue(taskqueue_swi,
&sc->sc_bmiss_task);
ieee80211_beacon_miss(ic);
}
break;
}
@ -1794,10 +1784,13 @@ wpi_intr(void *arg)
WPI_WRITE(sc, WPI_INTR, r);
if (r & (WPI_SW_ERROR | WPI_HW_ERROR)) {
struct ifnet *ifp = sc->sc_ifp;
struct ieee80211com *ic = ifp->if_l2com;
device_printf(sc->sc_dev, "fatal firmware error\n");
DPRINTFN(6,("(%s)\n", (r & WPI_SW_ERROR) ? "(Software Error)" :
"(Hardware Error)"));
wpi_queue_cmd(sc, WPI_RESTART, 0, WPI_QUEUE_CLEAR);
ieee80211_runtask(ic, &sc->sc_restarttask);
sc->flags &= ~WPI_FLAG_BUSY;
WPI_UNLOCK(sc);
return;
@ -3030,8 +3023,7 @@ wpi_rfkill_resume(struct wpi_softc *sc)
if (vap != NULL) {
if ((ic->ic_flags & IEEE80211_F_SCAN) == 0) {
if (vap->iv_opmode != IEEE80211_M_MONITOR) {
taskqueue_enqueue(taskqueue_swi,
&sc->sc_bmiss_task);
ieee80211_beacon_miss(ic);
wpi_set_led(sc, WPI_LED_LINK, 0, 1);
} else
wpi_set_led(sc, WPI_LED_LINK, 5, 5);
@ -3189,12 +3181,6 @@ wpi_stop_locked(struct wpi_softc *sc)
WPI_WRITE(sc, WPI_INTR_STATUS, 0xff);
WPI_WRITE(sc, WPI_INTR_STATUS, 0x00070000);
/* Clear any commands left in the command buffer */
memset(sc->sc_cmd, 0, sizeof(sc->sc_cmd));
memset(sc->sc_cmd_arg, 0, sizeof(sc->sc_cmd_arg));
sc->sc_cmd_cur = 0;
sc->sc_cmd_next = 0;
wpi_mem_lock(sc);
wpi_mem_write(sc, WPI_MEM_MODE, 0);
wpi_mem_unlock(sc);
@ -3538,7 +3524,9 @@ wpi_scan_start(struct ieee80211com *ic)
struct ifnet *ifp = ic->ic_ifp;
struct wpi_softc *sc = ifp->if_softc;
wpi_queue_cmd(sc, WPI_SCAN_START, 0, WPI_QUEUE_NORMAL);
WPI_LOCK(sc);
wpi_set_led(sc, WPI_LED_LINK, 20, 2);
WPI_UNLOCK(sc);
}
/**
@ -3549,10 +3537,7 @@ wpi_scan_start(struct ieee80211com *ic)
static void
wpi_scan_end(struct ieee80211com *ic)
{
struct ifnet *ifp = ic->ic_ifp;
struct wpi_softc *sc = ifp->if_softc;
wpi_queue_cmd(sc, WPI_SCAN_STOP, 0, WPI_QUEUE_NORMAL);
/* XXX ignore */
}
/**
@ -3564,13 +3549,18 @@ wpi_set_channel(struct ieee80211com *ic)
{
struct ifnet *ifp = ic->ic_ifp;
struct wpi_softc *sc = ifp->if_softc;
int error;
/*
* Only need to set the channel in Monitor mode. AP scanning and auth
* are already taken care of by their respective firmware commands.
*/
if (ic->ic_opmode == IEEE80211_M_MONITOR)
wpi_queue_cmd(sc, WPI_SET_CHAN, 0, WPI_QUEUE_NORMAL);
if (ic->ic_opmode == IEEE80211_M_MONITOR) {
error = wpi_config(sc);
if (error != 0)
device_printf(sc->sc_dev,
"error %d settting channel\n", error);
}
}
/**
@ -3585,7 +3575,10 @@ wpi_scan_curchan(struct ieee80211_scan_state *ss, unsigned long maxdwell)
struct ifnet *ifp = vap->iv_ic->ic_ifp;
struct wpi_softc *sc = ifp->if_softc;
wpi_queue_cmd(sc, WPI_SCAN_CURCHAN, 0, WPI_QUEUE_NORMAL);
WPI_LOCK(sc);
if (wpi_scan(sc))
ieee80211_cancel_scan(vap);
WPI_UNLOCK(sc);
}
/**
@ -3600,152 +3593,24 @@ wpi_scan_mindwell(struct ieee80211_scan_state *ss)
/* NB: don't try to abort scan; wait for firmware to finish */
}
/**
* The ops function is called to perform some actual work.
* because we can't sleep from any of the ic callbacks, we queue an
* op task with wpi_queue_cmd and have the taskqueue process that task.
* The task that gets cued is a op task, which ends up calling this function.
*/
static void
wpi_ops(void *arg0, int pending)
wpi_hwreset(void *arg, int pending)
{
struct wpi_softc *sc = arg0;
struct ifnet *ifp = sc->sc_ifp;
struct ieee80211com *ic = ifp->if_l2com;
int cmd, arg, error;
struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
struct wpi_softc *sc = arg;
again:
WPI_CMD_LOCK(sc);
cmd = sc->sc_cmd[sc->sc_cmd_cur];
arg = sc->sc_cmd_arg[sc->sc_cmd_cur];
if (cmd == 0) {
/* No more commands to process */
WPI_CMD_UNLOCK(sc);
return;
}
sc->sc_cmd[sc->sc_cmd_cur] = 0; /* free the slot */
sc->sc_cmd_arg[sc->sc_cmd_cur] = 0; /* free the slot */
sc->sc_cmd_cur = (sc->sc_cmd_cur + 1) % WPI_CMD_MAXOPS;
WPI_CMD_UNLOCK(sc);
WPI_LOCK(sc);
DPRINTFN(WPI_DEBUG_OPS,("wpi_ops: command: %d\n", cmd));
switch (cmd) {
case WPI_RESTART:
wpi_init_locked(sc, 0);
WPI_UNLOCK(sc);
return;
case WPI_RF_RESTART:
wpi_rfkill_resume(sc);
WPI_UNLOCK(sc);
return;
}
if (!(sc->sc_ifp->if_drv_flags & IFF_DRV_RUNNING)) {
WPI_UNLOCK(sc);
return;
}
switch (cmd) {
case WPI_SCAN_START:
/* make the link LED blink while we're scanning */
wpi_set_led(sc, WPI_LED_LINK, 20, 2);
sc->flags |= WPI_FLAG_SCANNING;
break;
case WPI_SCAN_STOP:
sc->flags &= ~WPI_FLAG_SCANNING;
break;
case WPI_SCAN_CURCHAN:
if (wpi_scan(sc))
ieee80211_cancel_scan(vap);
break;
case WPI_SET_CHAN:
error = wpi_config(sc);
if (error != 0)
device_printf(sc->sc_dev,
"error %d settting channel\n", error);
break;
case WPI_AUTH:
/* The node must be registered in the firmware before auth */
error = wpi_auth(sc, vap);
WPI_UNLOCK(sc);
if (error != 0) {
device_printf(sc->sc_dev,
"%s: could not move to auth state, error %d\n",
__func__, error);
return;
}
IEEE80211_LOCK(ic);
WPI_VAP(vap)->newstate(vap, IEEE80211_S_AUTH, arg);
if (vap->iv_newstate_cb != NULL)
vap->iv_newstate_cb(vap, IEEE80211_S_AUTH, arg);
IEEE80211_UNLOCK(ic);
goto again;
case WPI_RUN:
error = wpi_run(sc, vap);
WPI_UNLOCK(sc);
if (error != 0) {
device_printf(sc->sc_dev,
"%s: could not move to run state, error %d\n",
__func__, error);
return;
}
IEEE80211_LOCK(ic);
WPI_VAP(vap)->newstate(vap, IEEE80211_S_RUN, arg);
if (vap->iv_newstate_cb != NULL)
vap->iv_newstate_cb(vap, IEEE80211_S_RUN, arg);
IEEE80211_UNLOCK(ic);
goto again;
}
wpi_init_locked(sc, 0);
WPI_UNLOCK(sc);
/* Take another pass */
goto again;
}
/**
* queue a command for later execution in a different thread.
* This is needed as the net80211 callbacks do not allow
* sleeping, since we need to sleep to confirm commands have
* been processed by the firmware, we must defer execution to
* a sleep enabled thread.
*/
static int
wpi_queue_cmd(struct wpi_softc *sc, int cmd, int arg, int flush)
static void
wpi_rfreset(void *arg, int pending)
{
WPI_CMD_LOCK(sc);
struct wpi_softc *sc = arg;
if (flush) {
memset(sc->sc_cmd, 0, sizeof (sc->sc_cmd));
memset(sc->sc_cmd_arg, 0, sizeof (sc->sc_cmd_arg));
sc->sc_cmd_cur = 0;
sc->sc_cmd_next = 0;
}
if (sc->sc_cmd[sc->sc_cmd_next] != 0) {
WPI_CMD_UNLOCK(sc);
DPRINTF(("%s: command %d dropped\n", __func__, cmd));
return (EBUSY);
}
sc->sc_cmd[sc->sc_cmd_next] = cmd;
sc->sc_cmd_arg[sc->sc_cmd_next] = arg;
sc->sc_cmd_next = (sc->sc_cmd_next + 1) % WPI_CMD_MAXOPS;
taskqueue_enqueue(sc->sc_tq, &sc->sc_opstask);
WPI_CMD_UNLOCK(sc);
return 0;
WPI_LOCK(sc);
wpi_rfkill_resume(sc);
WPI_UNLOCK(sc);
}
/*
@ -3775,6 +3640,7 @@ wpi_watchdog(void *arg)
{
struct wpi_softc *sc = arg;
struct ifnet *ifp = sc->sc_ifp;
struct ieee80211com *ic = ifp->if_l2com;
uint32_t tmp;
DPRINTFN(WPI_DEBUG_WATCHDOG,("Watchdog: tick\n"));
@ -3790,7 +3656,7 @@ wpi_watchdog(void *arg)
}
device_printf(sc->sc_dev, "Hardware Switch Enabled\n");
wpi_queue_cmd(sc, WPI_RF_RESTART, 0, WPI_QUEUE_CLEAR);
ieee80211_runtask(ic, &sc->sc_radiotask);
return;
}
@ -3798,17 +3664,15 @@ wpi_watchdog(void *arg)
if (--sc->sc_tx_timer == 0) {
device_printf(sc->sc_dev,"device timeout\n");
ifp->if_oerrors++;
wpi_queue_cmd(sc, WPI_RESTART, 0, WPI_QUEUE_CLEAR);
ieee80211_runtask(ic, &sc->sc_restarttask);
}
}
if (sc->sc_scan_timer > 0) {
struct ifnet *ifp = sc->sc_ifp;
struct ieee80211com *ic = ifp->if_l2com;
struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
if (--sc->sc_scan_timer == 0 && vap != NULL) {
device_printf(sc->sc_dev,"scan timeout\n");
ieee80211_cancel_scan(vap);
wpi_queue_cmd(sc, WPI_RESTART, 0, WPI_QUEUE_CLEAR);
ieee80211_runtask(ic, &sc->sc_restarttask);
}
}

View File

@ -144,9 +144,8 @@ struct wpi_softc {
*/
uint32_t flags;
#define WPI_FLAG_HW_RADIO_OFF (1 << 0)
#define WPI_FLAG_SCANNING (1 << 1)
#define WPI_FLAG_BUSY (1 << 2)
#define WPI_FLAG_AUTH (1 << 3)
#define WPI_FLAG_BUSY (1 << 1)
#define WPI_FLAG_AUTH (1 << 2)
/* shared area */
struct wpi_dma_info shared_dma;
@ -193,36 +192,9 @@ struct wpi_softc {
/* firmware DMA transfer */
struct wpi_dma_info fw_dma;
/* command queue related variables */
#define WPI_SCAN_START (1<<0)
#define WPI_SCAN_CURCHAN (1<<1)
#define WPI_SCAN_STOP (1<<2)
#define WPI_SET_CHAN (1<<3)
#define WPI_AUTH (1<<4)
#define WPI_RUN (1<<5)
#define WPI_SCAN_NEXT (1<<6)
#define WPI_RESTART (1<<7)
#define WPI_RF_RESTART (1<<8)
#define WPI_CMD_MAXOPS 10
/* command queuing request type */
#define WPI_QUEUE_NORMAL 0
#define WPI_QUEUE_CLEAR 1
int sc_cmd[WPI_CMD_MAXOPS];
int sc_cmd_arg[WPI_CMD_MAXOPS];
int sc_cmd_cur; /* current queued scan task */
int sc_cmd_next; /* last queued scan task */
struct mtx sc_cmdlock;
/* Task queues used to control the driver */
struct taskqueue *sc_tq; /* Main command task queue */
struct taskqueue *sc_tq2; /* firmware reset task queue */
/* Tasks used by the driver */
struct task sc_radioontask; /* enable rf transmitter task*/
struct task sc_radioofftask;/* disable rf transmitter task*/
struct task sc_opstask; /* operation handling task */
struct task sc_restarttask; /* reset firmware task */
struct task sc_bmiss_task; /* beacon miss */
struct task sc_radiotask; /* reset rf task */
/* Eeprom info */
uint8_t cap;
@ -239,10 +211,3 @@ struct wpi_softc {
#define WPI_UNLOCK(_sc) mtx_unlock(&(_sc)->sc_mtx)
#define WPI_LOCK_ASSERT(sc) mtx_assert(&(sc)->sc_mtx, MA_OWNED)
#define WPI_LOCK_DESTROY(_sc) mtx_destroy(&(_sc)->sc_mtx)
#define WPI_CMD_LOCK_INIT(_sc) \
mtx_init(&(_sc)->sc_cmdlock, device_get_nameunit((_sc)->sc_dev), \
NULL, MTX_DEF)
#define WPI_CMD_LOCK_DESTROY(_sc) mtx_destroy(&(_sc)->sc_cmdlock)
#define WPI_CMD_LOCK(_sc) mtx_lock(&(_sc)->sc_cmdlock)
#define WPI_CMD_UNLOCK(_sc) mtx_unlock(&(_sc)->sc_cmdlock)

View File

@ -33,7 +33,7 @@ __FBSDID("$FreeBSD$");
#include "opt_wlan.h"
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/socket.h>
@ -251,6 +251,12 @@ ieee80211_ifattach(struct ieee80211com *ic,
IEEE80211_LOCK_INIT(ic, ifp->if_xname);
TAILQ_INIT(&ic->ic_vaps);
/* Create a taskqueue for all state changes */
ic->ic_tq = taskqueue_create("ic_taskq", M_WAITOK | M_ZERO,
taskqueue_thread_enqueue, &ic->ic_tq);
taskqueue_start_threads(&ic->ic_tq, 1, PI_NET, "%s taskq",
ifp->if_xname);
/*
* Fill in 802.11 available channel set, mark all
* available channels as active, and pick a default
@ -325,6 +331,7 @@ ieee80211_ifdetach(struct ieee80211com *ic)
ieee80211_node_detach(ic);
ifmedia_removeall(&ic->ic_media);
taskqueue_free(ic->ic_tq);
IEEE80211_LOCK_DESTROY(ic);
if_detach(ifp);
}
@ -554,7 +561,17 @@ ieee80211_vap_detach(struct ieee80211vap *vap)
* while we cleanup internal state but that is hard.
*/
ieee80211_stop_locked(vap);
IEEE80211_UNLOCK(ic);
/*
* Flush any deferred vap tasks.
* NB: must be before ether_ifdetach() and removal from ic_vaps list
*/
ieee80211_draintask(ic, &vap->iv_nstate_task);
ieee80211_draintask(ic, &vap->iv_swbmiss_task);
IEEE80211_LOCK(ic);
KASSERT(vap->iv_state == IEEE80211_S_INIT , ("vap still running"));
TAILQ_REMOVE(&ic->ic_vaps, vap, iv_next);
ieee80211_syncflag_locked(ic, IEEE80211_F_WME);
#ifdef IEEE80211_SUPPORT_SUPERG
@ -627,9 +644,9 @@ ieee80211_syncifflag_locked(struct ieee80211com *ic, int flag)
/* XXX should we return 1/0 and let caller do this? */
if (ifp->if_drv_flags & IFF_DRV_RUNNING) {
if (flag == IFF_PROMISC)
ic->ic_update_promisc(ifp);
ieee80211_runtask(ic, &ic->ic_promisc_task);
else if (flag == IFF_ALLMULTI)
ic->ic_update_mcast(ifp);
ieee80211_runtask(ic, &ic->ic_mcast_task);
}
}
}

View File

@ -32,6 +32,7 @@
#include <sys/lock.h>
#include <sys/mutex.h>
#include <sys/rwlock.h>
#include <sys/taskqueue.h>
/*
* Common state locking definitions.

View File

@ -42,7 +42,6 @@ __FBSDID("$FreeBSD$");
#include <sys/socket.h>
#include <sys/sockio.h>
#include <sys/systm.h>
#include <sys/taskqueue.h>
#include <net/if.h>
#include <net/if_dl.h>
@ -3196,8 +3195,7 @@ ieee80211_ioctl_updatemulti(struct ieee80211com *ic)
(void) if_addmulti(parent, ifma->ifma_addr, NULL);
}
parent->if_ioctl = ioctl;
ic->ic_update_mcast(ic->ic_ifp);
ieee80211_runtask(ic, &ic->ic_mcast_task);
IEEE80211_UNLOCK(ic);
}

View File

@ -629,16 +629,18 @@ ieee80211_sync_curchan(struct ieee80211com *ic)
ic->ic_curchan = c;
ic->ic_curmode = ieee80211_chan2mode(ic->ic_curchan);
ic->ic_rt = ieee80211_get_ratetable(ic->ic_curchan);
IEEE80211_UNLOCK(ic);
ic->ic_set_channel(ic);
IEEE80211_LOCK(ic);
}
}
/*
* Change the current channel. The request channel may be
* Setup the current channel. The request channel may be
* promoted if other vap's are operating with HT20/HT40.
*/
void
ieee80211_setcurchan(struct ieee80211com *ic, struct ieee80211_channel *c)
ieee80211_setupcurchan(struct ieee80211com *ic, struct ieee80211_channel *c)
{
if (ic->ic_htcaps & IEEE80211_HTC_HT) {
int flags = gethtadjustflags(ic);
@ -654,7 +656,17 @@ ieee80211_setcurchan(struct ieee80211com *ic, struct ieee80211_channel *c)
ic->ic_bsschan = ic->ic_curchan = c;
ic->ic_curmode = ieee80211_chan2mode(ic->ic_curchan);
ic->ic_rt = ieee80211_get_ratetable(ic->ic_curchan);
ic->ic_set_channel(ic);
}
/*
* Change the current channel. The channel change is guaranteed to have
* happened before the next state change.
*/
void
ieee80211_setcurchan(struct ieee80211com *ic, struct ieee80211_channel *c)
{
ieee80211_setupcurchan(ic, c);
ieee80211_runtask(ic, &ic->ic_chan_task);
}
/*

View File

@ -286,6 +286,8 @@ void ieee80211_node_set_chan(struct ieee80211_node *,
void ieee80211_create_ibss(struct ieee80211vap*, struct ieee80211_channel *);
void ieee80211_reset_bss(struct ieee80211vap *);
void ieee80211_sync_curchan(struct ieee80211com *);
void ieee80211_setupcurchan(struct ieee80211com *,
struct ieee80211_channel *);
void ieee80211_setcurchan(struct ieee80211com *, struct ieee80211_channel *);
int ieee80211_ibss_merge(struct ieee80211_node *);
struct ieee80211_scan_entry;

View File

@ -37,7 +37,6 @@ __FBSDID("$FreeBSD$");
#include <sys/param.h>
#include <sys/kernel.h>
#include <sys/systm.h>
#include <sys/taskqueue.h>
#include <sys/socket.h>
#include <sys/sockio.h>
@ -96,7 +95,13 @@ const char *ieee80211_wme_acnames[] = {
"WME_UPSD",
};
static void beacon_miss(void *, int);
static void beacon_swmiss(void *, int);
static void parent_updown(void *, int);
static void update_mcast(void *, int);
static void update_promisc(void *, int);
static void update_channel(void *, int);
static void ieee80211_newstate_cb(void *, int);
static int ieee80211_new_state_locked(struct ieee80211vap *,
enum ieee80211_state, int);
@ -131,6 +136,10 @@ ieee80211_proto_attach(struct ieee80211com *ic)
ic->ic_protmode = IEEE80211_PROT_CTSONLY;
TASK_INIT(&ic->ic_parent_task, 0, parent_updown, ifp);
TASK_INIT(&ic->ic_mcast_task, 0, update_mcast, ic);
TASK_INIT(&ic->ic_promisc_task, 0, update_promisc, ic);
TASK_INIT(&ic->ic_chan_task, 0, update_channel, ic);
TASK_INIT(&ic->ic_bmiss_task, 0, beacon_miss, ic);
ic->ic_wme.wme_hipri_switch_hysteresis =
AGGRESSIVE_MODE_SWITCH_HYSTERESIS;
@ -176,6 +185,8 @@ ieee80211_proto_vattach(struct ieee80211vap *vap)
vap->iv_bmiss_max = IEEE80211_BMISS_MAX;
callout_init(&vap->iv_swbmiss, CALLOUT_MPSAFE);
callout_init(&vap->iv_mgtsend, CALLOUT_MPSAFE);
TASK_INIT(&vap->iv_nstate_task, 0, ieee80211_newstate_cb, vap);
TASK_INIT(&vap->iv_swbmiss_task, 0, beacon_swmiss, vap);
/*
* Install default tx rate handling: no fixed rate, lowest
* supported rate for mgmt and multicast frames. Default
@ -1072,6 +1083,32 @@ parent_updown(void *arg, int npending)
parent->if_ioctl(parent, SIOCSIFFLAGS, NULL);
}
static void
update_mcast(void *arg, int npending)
{
struct ieee80211com *ic = arg;
struct ifnet *parent = ic->ic_ifp;
ic->ic_update_mcast(parent);
}
static void
update_promisc(void *arg, int npending)
{
struct ieee80211com *ic = arg;
struct ifnet *parent = ic->ic_ifp;
ic->ic_update_promisc(parent);
}
static void
update_channel(void *arg, int npending)
{
struct ieee80211com *ic = arg;
ic->ic_set_channel(ic);
}
/*
* Block until the parent is in a known state. This is
* used after any operations that dispatch a task (e.g.
@ -1080,7 +1117,13 @@ parent_updown(void *arg, int npending)
void
ieee80211_waitfor_parent(struct ieee80211com *ic)
{
taskqueue_drain(taskqueue_thread, &ic->ic_parent_task);
taskqueue_block(ic->ic_tq);
ieee80211_draintask(ic, &ic->ic_parent_task);
ieee80211_draintask(ic, &ic->ic_mcast_task);
ieee80211_draintask(ic, &ic->ic_promisc_task);
ieee80211_draintask(ic, &ic->ic_chan_task);
ieee80211_draintask(ic, &ic->ic_bmiss_task);
taskqueue_unblock(ic->ic_tq);
}
/*
@ -1121,7 +1164,7 @@ ieee80211_start_locked(struct ieee80211vap *vap)
IEEE80211_MSG_STATE | IEEE80211_MSG_DEBUG,
"%s: up parent %s\n", __func__, parent->if_xname);
parent->if_flags |= IFF_UP;
taskqueue_enqueue(taskqueue_thread, &ic->ic_parent_task);
ieee80211_runtask(ic, &ic->ic_parent_task);
return;
}
}
@ -1156,8 +1199,7 @@ ieee80211_start_locked(struct ieee80211vap *vap)
* preempted if the station is locked to a particular
* channel.
*/
/* XXX needed? */
ieee80211_new_state_locked(vap, IEEE80211_S_INIT, 0);
vap->iv_flags_ext |= IEEE80211_FEXT_REINIT;
if (vap->iv_opmode == IEEE80211_M_MONITOR ||
vap->iv_opmode == IEEE80211_M_WDS)
ieee80211_new_state_locked(vap,
@ -1240,7 +1282,7 @@ ieee80211_stop_locked(struct ieee80211vap *vap)
IEEE80211_MSG_STATE | IEEE80211_MSG_DEBUG,
"down parent %s\n", parent->if_xname);
parent->if_flags &= ~IFF_UP;
taskqueue_enqueue(taskqueue_thread, &ic->ic_parent_task);
ieee80211_runtask(ic, &ic->ic_parent_task);
}
}
}
@ -1319,10 +1361,20 @@ ieee80211_resume_all(struct ieee80211com *ic)
void
ieee80211_beacon_miss(struct ieee80211com *ic)
{
IEEE80211_LOCK(ic);
if ((ic->ic_flags & IEEE80211_F_SCAN) == 0) {
/* Process in a taskq, the handler may reenter the driver */
ieee80211_runtask(ic, &ic->ic_bmiss_task);
}
IEEE80211_UNLOCK(ic);
}
static void
beacon_miss(void *arg, int npending)
{
struct ieee80211com *ic = arg;
struct ieee80211vap *vap;
if (ic->ic_flags & IEEE80211_F_SCAN)
return;
/* XXX locking */
TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) {
/*
@ -1337,6 +1389,18 @@ ieee80211_beacon_miss(struct ieee80211com *ic)
}
}
static void
beacon_swmiss(void *arg, int npending)
{
struct ieee80211vap *vap = arg;
if (vap->iv_state != IEEE80211_S_RUN)
return;
/* XXX Call multiple times if npending > zero? */
vap->iv_bmiss(vap);
}
/*
* Software beacon miss handling. Check if any beacons
* were received in the last period. If not post a
@ -1366,7 +1430,7 @@ ieee80211_swbmiss(void *arg)
vap->iv_swbmiss_count = 0;
} else if (vap->iv_swbmiss_count == 0) {
if (vap->iv_bmiss != NULL)
vap->iv_bmiss(vap);
ieee80211_runtask(ic, &vap->iv_swbmiss_task);
if (vap->iv_bmiss_count == 0) /* don't re-arm timer */
return;
} else
@ -1463,7 +1527,6 @@ ieee80211_cac_completeswitch(struct ieee80211vap *vap0)
* and mark them as waiting for a scan to complete. These vaps
* will be brought up when the scan completes and the scanning vap
* reaches RUN state by wakeupwaiting.
* XXX if we do this in threads we can use sleep/wakeup.
*/
static void
markwaiting(struct ieee80211vap *vap0)
@ -1473,10 +1536,16 @@ markwaiting(struct ieee80211vap *vap0)
IEEE80211_LOCK_ASSERT(ic);
/*
* A vap list entry can not disappear since we are running on the
* taskqueue and a vap destroy will queue and drain another state
* change task.
*/
TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) {
if (vap == vap0)
continue;
if (vap->iv_state != IEEE80211_S_INIT) {
/* NB: iv_newstate may drop the lock */
vap->iv_newstate(vap, IEEE80211_S_INIT, 0);
vap->iv_flags_ext |= IEEE80211_FEXT_SCANWAIT;
}
@ -1487,6 +1556,7 @@ markwaiting(struct ieee80211vap *vap0)
* Wakeup all vap's waiting for a scan to complete. This is the
* companion to markwaiting (above) and is used to coordinate
* multiple vaps scanning.
* This is called from the state taskqueue.
*/
static void
wakeupwaiting(struct ieee80211vap *vap0)
@ -1496,12 +1566,18 @@ wakeupwaiting(struct ieee80211vap *vap0)
IEEE80211_LOCK_ASSERT(ic);
/*
* A vap list entry can not disappear since we are running on the
* taskqueue and a vap destroy will queue and drain another state
* change task.
*/
TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) {
if (vap == vap0)
continue;
if (vap->iv_flags_ext & IEEE80211_FEXT_SCANWAIT) {
vap->iv_flags_ext &= ~IEEE80211_FEXT_SCANWAIT;
/* NB: sta's cannot go INIT->RUN */
/* NB: iv_newstate may drop the lock */
vap->iv_newstate(vap,
vap->iv_opmode == IEEE80211_M_STA ?
IEEE80211_S_SCAN : IEEE80211_S_RUN, 0);
@ -1513,15 +1589,63 @@ wakeupwaiting(struct ieee80211vap *vap0)
* Handle post state change work common to all operating modes.
*/
static void
ieee80211_newstate_cb(struct ieee80211vap *vap,
enum ieee80211_state nstate, int arg)
ieee80211_newstate_cb(void *xvap, int npending)
{
struct ieee80211vap *vap = xvap;
struct ieee80211com *ic = vap->iv_ic;
enum ieee80211_state nstate, ostate;
int arg, rc;
IEEE80211_LOCK_ASSERT(ic);
IEEE80211_LOCK(ic);
nstate = vap->iv_nstate;
arg = vap->iv_nstate_arg;
if (vap->iv_flags_ext & IEEE80211_FEXT_REINIT) {
/*
* We have been requested to drop back to the INIT before
* proceeding to the new state.
*/
IEEE80211_DPRINTF(vap, IEEE80211_MSG_STATE,
"%s: %s -> %s arg %d\n", __func__,
ieee80211_state_name[vap->iv_state],
ieee80211_state_name[IEEE80211_S_INIT], arg);
vap->iv_newstate(vap, IEEE80211_S_INIT, arg);
vap->iv_flags_ext &= ~IEEE80211_FEXT_REINIT;
}
ostate = vap->iv_state;
if (nstate == IEEE80211_S_SCAN && ostate != IEEE80211_S_INIT) {
/*
* SCAN was forced; e.g. on beacon miss. Force other running
* vap's to INIT state and mark them as waiting for the scan to
* complete. This insures they don't interfere with our
* scanning. Since we are single threaded the vaps can not
* transition again while we are executing.
*
* XXX not always right, assumes ap follows sta
*/
markwaiting(vap);
}
IEEE80211_DPRINTF(vap, IEEE80211_MSG_STATE,
"%s: %s arg %d\n", __func__, ieee80211_state_name[nstate], arg);
"%s: %s -> %s arg %d\n", __func__,
ieee80211_state_name[ostate], ieee80211_state_name[nstate], arg);
rc = vap->iv_newstate(vap, nstate, arg);
vap->iv_flags_ext &= ~IEEE80211_FEXT_STATEWAIT;
if (rc != 0) {
/* State transition failed */
KASSERT(rc != EINPROGRESS, ("iv_newstate was deferred"));
KASSERT(nstate != IEEE80211_S_INIT,
("INIT state change failed"));
IEEE80211_DPRINTF(vap, IEEE80211_MSG_STATE,
"%s: %s returned error %d\n", __func__,
ieee80211_state_name[nstate], rc);
goto done;
}
/* No actual transition, skip post processing */
if (ostate == nstate)
goto done;
if (nstate == IEEE80211_S_RUN) {
/*
@ -1549,7 +1673,8 @@ ieee80211_newstate_cb(struct ieee80211vap *vap,
/* XXX NB: cast for altq */
ieee80211_flush_ifq((struct ifqueue *)&ic->ic_ifp->if_snd, vap);
}
vap->iv_newstate_cb = NULL;
done:
IEEE80211_UNLOCK(ic);
}
/*
@ -1586,10 +1711,32 @@ ieee80211_new_state_locked(struct ieee80211vap *vap,
struct ieee80211com *ic = vap->iv_ic;
struct ieee80211vap *vp;
enum ieee80211_state ostate;
int nrunning, nscanning, rc;
int nrunning, nscanning;
IEEE80211_LOCK_ASSERT(ic);
if (vap->iv_flags_ext & IEEE80211_FEXT_STATEWAIT) {
if (vap->iv_nstate == IEEE80211_S_INIT) {
/*
* XXX The vap is being stopped, do no allow any other
* state changes until this is completed.
*/
return -1;
}
#if 0
/* Warn if the previous state hasn't completed. */
IEEE80211_DPRINTF(vap, IEEE80211_MSG_STATE,
"%s: pending %s -> %s transition lost\n", __func__,
ieee80211_state_name[vap->iv_state],
ieee80211_state_name[vap->iv_nstate]);
#else
/* XXX temporarily enable to identify issues */
if_printf(vap->iv_ifp, "%s: pending %s -> %s transition lost\n",
__func__, ieee80211_state_name[vap->iv_state],
ieee80211_state_name[vap->iv_nstate]);
#endif
}
nrunning = nscanning = 0;
/* XXX can track this state instead of calculating */
TAILQ_FOREACH(vp, &ic->ic_vaps, iv_next) {
@ -1625,8 +1772,7 @@ ieee80211_new_state_locked(struct ieee80211vap *vap,
__func__, ieee80211_state_name[ostate],
ieee80211_state_name[nstate]);
vap->iv_flags_ext |= IEEE80211_FEXT_SCANWAIT;
rc = 0;
goto done;
return 0;
}
if (nrunning) {
/*
@ -1650,16 +1796,6 @@ ieee80211_new_state_locked(struct ieee80211vap *vap,
}
#endif
}
} else {
/*
* SCAN was forced; e.g. on beacon miss. Force
* other running vap's to INIT state and mark
* them as waiting for the scan to complete. This
* insures they don't interfere with our scanning.
*
* XXX not always right, assumes ap follows sta
*/
markwaiting(vap);
}
break;
case IEEE80211_S_RUN:
@ -1676,8 +1812,7 @@ ieee80211_new_state_locked(struct ieee80211vap *vap,
ieee80211_state_name[ostate],
ieee80211_state_name[nstate]);
vap->iv_flags_ext |= IEEE80211_FEXT_SCANWAIT;
rc = 0;
goto done;
return 0;
}
if (vap->iv_opmode == IEEE80211_M_HOSTAP &&
IEEE80211_IS_CHAN_DFS(ic->ic_bsschan) &&
@ -1706,20 +1841,12 @@ ieee80211_new_state_locked(struct ieee80211vap *vap,
default:
break;
}
/* XXX on transition RUN->CAC do we need to set nstate = iv_state? */
if (ostate != nstate) {
/*
* Arrange for work to happen after state change completes.
* If this happens asynchronously the caller must arrange
* for the com lock to be held.
*/
vap->iv_newstate_cb = ieee80211_newstate_cb;
}
rc = vap->iv_newstate(vap, nstate, arg);
if (rc == 0 && vap->iv_newstate_cb != NULL)
vap->iv_newstate_cb(vap, nstate, arg);
done:
return rc;
/* defer the state change to a thread */
vap->iv_nstate = nstate;
vap->iv_nstate_arg = arg;
vap->iv_flags_ext |= IEEE80211_FEXT_STATEWAIT;
ieee80211_runtask(ic, &vap->iv_nstate_task);
return EINPROGRESS;
}
int

View File

@ -33,7 +33,9 @@ __FBSDID("$FreeBSD$");
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/proc.h>
#include <sys/kernel.h>
#include <sys/condvar.h>
#include <sys/socket.h>
@ -52,10 +54,12 @@ struct scan_state {
#define ISCAN_MINDWELL 0x0001 /* min dwell time reached */
#define ISCAN_DISCARD 0x0002 /* discard rx'd frames */
#define ISCAN_CANCEL 0x0004 /* cancel current scan */
#define ISCAN_START 0x0008 /* 1st time through next_scan */
#define ISCAN_ABORT 0x0008 /* end the scan immediately */
unsigned long ss_chanmindwell; /* min dwell on curchan */
unsigned long ss_scanend; /* time scan must stop */
u_int ss_duration; /* duration for next scan */
struct task ss_scan_task; /* scan execution */
struct cv ss_scan_cv; /* scan signal */
struct callout ss_scan_timer; /* scan timer */
};
#define SCAN_PRIVATE(ss) ((struct scan_state *) ss)
@ -88,10 +92,10 @@ struct scan_state {
#define ROAM_RATE_QUARTER_DEFAULT 2*3 /* quarter-width 11a/g bss */
#define ROAM_MCS_11N_DEFAULT (1 | IEEE80211_RATE_MCS) /* 11n bss */
static void scan_restart_pwrsav(void *);
static void scan_curchan(struct ieee80211_scan_state *, unsigned long);
static void scan_mindwell(struct ieee80211_scan_state *);
static void scan_next(void *);
static void scan_signal(void *);
static void scan_task(void *, int);
MALLOC_DEFINE(M_80211_SCAN, "80211scan", "802.11 scan state");
@ -107,7 +111,10 @@ ieee80211_scan_attach(struct ieee80211com *ic)
return;
}
callout_init_mtx(&ss->ss_scan_timer, IEEE80211_LOCK_OBJ(ic), 0);
cv_init(&ss->ss_scan_cv, "scan");
TASK_INIT(&ss->ss_scan_task, 0, scan_task, ss);
ic->ic_scan = &ss->base;
ss->base.ss_ic = ic;
ic->ic_scan_curchan = scan_curchan;
ic->ic_scan_mindwell = scan_mindwell;
@ -119,12 +126,17 @@ ieee80211_scan_detach(struct ieee80211com *ic)
struct ieee80211_scan_state *ss = ic->ic_scan;
if (ss != NULL) {
callout_drain(&SCAN_PRIVATE(ss)->ss_scan_timer);
IEEE80211_LOCK(ic);
SCAN_PRIVATE(ss)->ss_iflags |= ISCAN_ABORT;
scan_signal(ss);
IEEE80211_UNLOCK(ic);
ieee80211_draintask(ic, &SCAN_PRIVATE(ss)->ss_scan_task);
KASSERT((ic->ic_flags & IEEE80211_F_SCAN) == 0,
("scan still running"));
if (ss->ss_ops != NULL) {
ss->ss_ops->scan_detach(ss);
ss->ss_ops = NULL;
}
ic->ic_flags &= ~IEEE80211_F_SCAN;
ic->ic_scan = NULL;
free(SCAN_PRIVATE(ss), M_80211_SCAN);
}
@ -174,9 +186,8 @@ ieee80211_scan_vdetach(struct ieee80211vap *vap)
ss = ic->ic_scan;
if (ss != NULL && ss->ss_vap == vap) {
if (ic->ic_flags & IEEE80211_F_SCAN) {
/* XXX callout_drain */
callout_stop(&SCAN_PRIVATE(ss)->ss_scan_timer);
ic->ic_flags &= ~IEEE80211_F_SCAN;
SCAN_PRIVATE(ss)->ss_iflags |= ISCAN_ABORT;
scan_signal(ss);
}
if (ss->ss_ops != NULL) {
ss->ss_ops->scan_detach(ss);
@ -293,15 +304,6 @@ scan_update_locked(struct ieee80211vap *vap,
}
}
static void
change_channel(struct ieee80211com *ic,
struct ieee80211_channel *chan)
{
ic->ic_curchan = chan;
ic->ic_rt = ieee80211_get_ratetable(chan);
ic->ic_set_channel(ic);
}
static char
channel_type(const struct ieee80211_channel *c)
{
@ -325,7 +327,7 @@ channel_type(const struct ieee80211_channel *c)
void
ieee80211_scan_dump_channels(const struct ieee80211_scan_state *ss)
{
struct ieee80211com *ic = ss->ss_vap->iv_ic;
struct ieee80211com *ic = ss->ss_ic;
const char *sep;
int i;
@ -352,76 +354,6 @@ scan_dump(struct ieee80211_scan_state *ss)
}
#endif /* IEEE80211_DEBUG */
/*
* Enable station power save mode and start/restart the scanning thread.
*/
static void
scan_restart_pwrsav(void *arg)
{
struct scan_state *ss = (struct scan_state *) arg;
struct ieee80211vap *vap = ss->base.ss_vap;
struct ieee80211com *ic = vap->iv_ic;
int ticksdelay;
ieee80211_sta_pwrsave(vap, 1);
/*
* Use an initial 1ms delay so the null
* data frame has a chance to go out.
* XXX 1ms is a lot, better to trigger scan
* on tx complete.
*/
ticksdelay = msecs_to_ticks(1);
if (ticksdelay < 1)
ticksdelay = 1;
ic->ic_scan_start(ic); /* notify driver */
ss->ss_scanend = ticks + ticksdelay + ss->ss_duration;
ss->ss_iflags |= ISCAN_START;
callout_reset(&ss->ss_scan_timer, ticksdelay, scan_next, ss);
}
/*
* Start/restart scanning. If we're operating in station mode
* and associated notify the ap we're going into power save mode
* and schedule a callback to initiate the work (where there's a
* better context for doing the work). Otherwise, start the scan
* directly.
*/
static int
scan_restart(struct scan_state *ss, u_int duration)
{
struct ieee80211vap *vap = ss->base.ss_vap;
struct ieee80211com *ic = vap->iv_ic;
int defer = 0;
if (ss->base.ss_next == ss->base.ss_last) {
IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN,
"%s: no channels to scan\n", __func__);
return 0;
}
if (vap->iv_opmode == IEEE80211_M_STA &&
vap->iv_state == IEEE80211_S_RUN) {
if ((vap->iv_bss->ni_flags & IEEE80211_NODE_PWR_MGT) == 0) {
/*
* Initiate power save before going off-channel.
* Note that we cannot do this directly because
* of locking issues; instead we defer it to a
* tasklet.
*/
ss->ss_duration = duration;
defer = 1;
}
}
if (!defer) {
ic->ic_scan_start(ic); /* notify driver */
ss->ss_scanend = ticks + duration;
ss->ss_iflags |= ISCAN_START;
callout_reset(&ss->ss_scan_timer, 0, scan_next, ss);
} else
scan_restart_pwrsav(ss);
return 1;
}
static void
copy_ssid(struct ieee80211vap *vap, struct ieee80211_scan_state *ss,
int nssid, const struct ieee80211_scan_ssid ssids[])
@ -485,16 +417,18 @@ start_scan_locked(const struct ieee80211_scanner *scan,
/* NB: flush frames rx'd before 1st channel change */
SCAN_PRIVATE(ss)->ss_iflags |= ISCAN_DISCARD;
SCAN_PRIVATE(ss)->ss_duration = 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))
scan_dump(ss);
#endif /* IEEE80211_DEBUG */
if (scan_restart(SCAN_PRIVATE(ss), duration))
ic->ic_flags |= IEEE80211_F_SCAN;
ic->ic_flags |= IEEE80211_F_SCAN;
ieee80211_runtask(ic, &SCAN_PRIVATE(ss)->ss_scan_task);
}
} else {
IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN,
@ -716,11 +650,11 @@ ieee80211_bg_scan(struct ieee80211vap *vap, int flags)
}
/* NB: flush frames rx'd before 1st channel change */
SCAN_PRIVATE(ss)->ss_iflags |= ISCAN_DISCARD;
SCAN_PRIVATE(ss)->ss_duration = duration;
ss->ss_maxdwell = duration;
if (scan_restart(SCAN_PRIVATE(ss), duration)) {
ic->ic_flags |= IEEE80211_F_SCAN;
ic->ic_flags_ext |= IEEE80211_FEXT_BGSCAN;
}
ic->ic_flags |= IEEE80211_F_SCAN;
ic->ic_flags_ext |= IEEE80211_FEXT_BGSCAN;
ieee80211_runtask(ic, &SCAN_PRIVATE(ss)->ss_scan_task);
} else {
/* XXX msg+stat */
}
@ -756,9 +690,8 @@ ieee80211_cancel_scan(struct ieee80211vap *vap)
/* clear bg scan NOPICK and mark cancel request */
ss->ss_flags &= ~IEEE80211_SCAN_NOPICK;
SCAN_PRIVATE(ss)->ss_iflags |= ISCAN_CANCEL;
/* force it to fire asap */
callout_reset(&SCAN_PRIVATE(ss)->ss_scan_timer,
0, scan_next, ss);
/* wake up the scan task */
scan_signal(ss);
}
IEEE80211_UNLOCK(ic);
}
@ -783,9 +716,8 @@ ieee80211_cancel_anyscan(struct ieee80211vap *vap)
/* clear bg scan NOPICK and mark cancel request */
ss->ss_flags &= ~IEEE80211_SCAN_NOPICK;
SCAN_PRIVATE(ss)->ss_iflags |= ISCAN_CANCEL;
/* force it to fire asap */
callout_reset(&SCAN_PRIVATE(ss)->ss_scan_timer,
0, scan_next, ss);
/* wake up the scan task */
scan_signal(ss);
}
IEEE80211_UNLOCK(ic);
}
@ -800,7 +732,10 @@ ieee80211_scan_next(struct ieee80211vap *vap)
struct ieee80211com *ic = vap->iv_ic;
struct ieee80211_scan_state *ss = ic->ic_scan;
callout_reset(&SCAN_PRIVATE(ss)->ss_scan_timer, 0, scan_next, ss);
/* wake up the scan task */
IEEE80211_LOCK(ic);
scan_signal(ss);
IEEE80211_UNLOCK(ic);
}
/*
@ -816,7 +751,7 @@ ieee80211_scan_done(struct ieee80211vap *vap)
IEEE80211_LOCK(ic);
ss = ic->ic_scan;
ss->ss_next = ss->ss_last; /* all channels are complete */
scan_next(ss);
scan_signal(ss);
IEEE80211_UNLOCK(ic);
}
@ -866,12 +801,22 @@ scan_curchan(struct ieee80211_scan_state *ss, unsigned long maxdwell)
{
struct ieee80211vap *vap = ss->ss_vap;
IEEE80211_LOCK_ASSERT(vap->iv_ic);
IEEE80211_LOCK(vap->iv_ic);
if (ss->ss_flags & IEEE80211_SCAN_ACTIVE)
ieee80211_probe_curchan(vap, 0);
callout_reset(&SCAN_PRIVATE(ss)->ss_scan_timer,
maxdwell, scan_next, ss);
maxdwell, scan_signal, ss);
IEEE80211_UNLOCK(vap->iv_ic);
}
static void
scan_signal(void *arg)
{
struct ieee80211_scan_state *ss = (struct ieee80211_scan_state *) arg;
IEEE80211_LOCK_ASSERT(ss->ss_ic);
cv_signal(&SCAN_PRIVATE(ss)->ss_scan_cv);
}
/*
@ -881,35 +826,67 @@ scan_curchan(struct ieee80211_scan_state *ss, unsigned long maxdwell)
static void
scan_mindwell(struct ieee80211_scan_state *ss)
{
callout_reset(&SCAN_PRIVATE(ss)->ss_scan_timer, 0, scan_next, ss);
struct ieee80211com *ic = ss->ss_ic;
IEEE80211_LOCK(ic);
scan_signal(ss);
IEEE80211_UNLOCK(ic);
}
/*
* Switch to the next channel marked for scanning.
*/
static void
scan_next(void *arg)
scan_task(void *arg, int pending)
{
#define ISCAN_REP (ISCAN_MINDWELL | ISCAN_START | ISCAN_DISCARD)
#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 = vap->iv_ic;
struct ieee80211com *ic = ss->ss_ic;
struct ieee80211_channel *chan;
unsigned long maxdwell, scanend;
int scandone;
int scandone = 0;
IEEE80211_LOCK_ASSERT(ic);
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;
if ((ic->ic_flags & IEEE80211_F_SCAN) == 0)
return;
again:
scandone = (ss->ss_next >= ss->ss_last) ||
(SCAN_PRIVATE(ss)->ss_iflags & ISCAN_CANCEL) != 0;
scanend = SCAN_PRIVATE(ss)->ss_scanend;
if (!scandone &&
(ss->ss_flags & IEEE80211_SCAN_GOTPICK) == 0 &&
((SCAN_PRIVATE(ss)->ss_iflags & ISCAN_START) ||
time_before(ticks + ss->ss_mindwell, scanend))) {
chan = ss->ss_chans[ss->ss_next++];
/*
@ -934,7 +911,15 @@ scan_next(void *arg)
/*
* Potentially change channel and phy mode.
*/
change_channel(ic, chan);
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);
/*
* Scan curchan. Drivers for "intelligent hardware"
@ -942,93 +927,115 @@ scan_next(void *arg)
* 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;
} else {
ic->ic_scan_end(ic); /* notify driver */
/*
* 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_setcurchan(ic, ic->ic_bsschan);
/* 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++;
if ((SCAN_PRIVATE(ss)->ss_iflags & (ISCAN_CANCEL|ISCAN_ABORT)))
continue;
ss->ss_ops->scan_restart(ss, vap); /* XXX? */
ic->ic_scan_start(ic); /* notify driver */
goto again;
} else {
/* past here, scandone is ``true'' if not in bg mode */
if ((ss->ss_flags & IEEE80211_SCAN_BGSCAN) == 0)
scandone = 1;
/* 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_DPRINTF(vap, IEEE80211_MSG_SCAN,
"%s: %s, "
"[ticks %u, dwell min %lu scanend %lu]\n",
__func__, scandone ? "done" : "stopped",
ticks, ss->ss_mindwell, scanend);
IEEE80211_UNLOCK(ic);
ic->ic_scan_end(ic); /* notify driver */
IEEE80211_LOCK(ic);
/*
* 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.
*/
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;
ss->ss_flags &=
~(IEEE80211_SCAN_ONCE | IEEE80211_SCAN_PICK1ST);
/*
* 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_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
}

View File

@ -90,6 +90,7 @@ struct ieee80211_scan_ssid {
*/
struct ieee80211_scan_state {
struct ieee80211vap *ss_vap;
struct ieee80211com *ss_ic;
const struct ieee80211_scanner *ss_ops; /* policy hookup, see below */
void *ss_priv; /* scanner private state */
uint16_t ss_flags;

View File

@ -32,10 +32,10 @@ __FBSDID("$FreeBSD$");
#include "opt_wlan.h"
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/module.h>
#include <sys/socket.h>
#include <net/if.h>
@ -1640,7 +1640,7 @@ ap_force_promisc(struct ieee80211com *ic)
IEEE80211_LOCK(ic);
/* set interface into promiscuous mode */
ifp->if_flags |= IFF_PROMISC;
ic->ic_update_promisc(ifp);
ieee80211_runtask(ic, &ic->ic_promisc_task);
IEEE80211_UNLOCK(ic);
}

View File

@ -126,7 +126,12 @@ struct ieee80211com {
enum ieee80211_opmode ic_opmode; /* operation mode */
struct ifmedia ic_media; /* interface media config */
struct callout ic_inact; /* inactivity processing */
struct taskqueue *ic_tq; /* deferred state thread */
struct task ic_parent_task; /* deferred parent processing */
struct task ic_promisc_task;/* deferred promisc update */
struct task ic_mcast_task; /* deferred mcast update */
struct task ic_chan_task; /* deferred channel change */
struct task ic_bmiss_task; /* deferred beacon miss hndlr */
uint32_t ic_flags; /* state flags */
uint32_t ic_flags_ext; /* extended state flags */
@ -325,8 +330,10 @@ struct ieee80211vap {
uint32_t iv_htcaps; /* HT capabilities */
enum ieee80211_opmode iv_opmode; /* operation mode */
enum ieee80211_state iv_state; /* state machine state */
void (*iv_newstate_cb)(struct ieee80211vap *,
enum ieee80211_state, int);
enum ieee80211_state iv_nstate; /* pending state */
int iv_nstate_arg; /* pending state arg */
struct task iv_nstate_task; /* deferred state processing */
struct task iv_swbmiss_task;/* deferred iv_bmiss call */
struct callout iv_mgtsend; /* mgmt frame response timer */
/* inactivity timer settings */
int iv_inact_init; /* setting for new station */
@ -519,6 +526,8 @@ MALLOC_DECLARE(M_80211_VAP);
#define IEEE80211_FEXT_SWBMISS 0x00000400 /* CONF: do bmiss in s/w */
#define IEEE80211_FEXT_DFS 0x00000800 /* CONF: DFS enabled */
#define IEEE80211_FEXT_DOTD 0x00001000 /* CONF: 11d enabled */
#define IEEE80211_FEXT_STATEWAIT 0x00002000 /* STATUS: awaiting state chg */
#define IEEE80211_FEXT_REINIT 0x00004000 /* STATUS: INIT state first */
/* NB: immutable: should be set only when creating a vap */
#define IEEE80211_FEXT_WDSLEGACY 0x00010000 /* CONF: legacy WDS operation */
#define IEEE80211_FEXT_PROBECHAN 0x00020000 /* CONF: probe passive channel*/
@ -536,9 +545,10 @@ MALLOC_DECLARE(M_80211_VAP);
#define IEEE80211_FEXT_BITS \
"\20\1NONHT_PR\2INACT\3SCANWAIT\4BGSCAN\5WPS\6TSN\7SCANREQ\10RESUME" \
"\0114ADDR\12NONEPR_PR\13SWBMISS\14DFS\15DOTD\22WDSLEGACY\23PROBECHAN" \
"\24HT\25AMDPU_TX\26AMPDU_TX\27AMSDU_TX\30AMSDU_RX\31USEHT40\32PUREN" \
"\33SHORTGI20\34SHORTGI40\35HTCOMPAT\36RIFS"
"\0114ADDR\12NONEPR_PR\13SWBMISS\14DFS\15DOTD\16STATEWAIT\17REINIT" \
"\22WDSLEGACY\23PROBECHAN\24HT\25AMDPU_TX\26AMPDU_TX\27AMSDU_TX" \
"\30AMSDU_RX\31USEHT40\32PUREN\33SHORTGI20\34SHORTGI40\35HTCOMPAT" \
"\36RIFS"
#define IEEE80211_FVEN_BITS "\20"
@ -633,6 +643,24 @@ struct ieee80211_channel *ieee80211_find_channel_byieee(struct ieee80211com *,
int ieee80211_setmode(struct ieee80211com *, enum ieee80211_phymode);
enum ieee80211_phymode ieee80211_chan2mode(const struct ieee80211_channel *);
/*
* Enqueue a task on the state thread.
*/
static __inline void
ieee80211_runtask(struct ieee80211com *ic, struct task *task)
{
taskqueue_enqueue(ic->ic_tq, task);
}
/*
* Wait for a queued task to complete.
*/
static __inline void
ieee80211_draintask(struct ieee80211com *ic, struct task *task)
{
taskqueue_drain(ic->ic_tq, task);
}
/*
* Key update synchronization methods. XXX should not be visible.
*/