rsu: various scanning fixes.

- Set IEEE80211_FEXT_SCAN_OFFLOAD flag; firmware can send null data
frames when associated.
- Check IEEE80211_SCAN_ACTIVE scan flag instead of IEEE80211_F_ASCAN
ic flag; the last is never set since r170530.
- Eliminate software scan (net80211) <-> site_survey (driver) race:
 * override ic_scan_curchan and ic_scan_mindwell pointers so net80211
will not try to finish scanning automatically;
 * inform net80211 about current status via ieee80211_cancel_scan()
and ieee80211_scan_done();
 * remove corresponding workaround from rsu_join_bss().

Now the driver can associate to an AP with hidden SSID.

Tested with Asus USB-N10.
This commit is contained in:
Andriy Voskoboinyk 2016-11-27 12:03:34 +00:00
parent abc1515601
commit 5dbbb84e42
2 changed files with 72 additions and 60 deletions

View file

@ -173,6 +173,8 @@ static void rsu_scan_end(struct ieee80211com *);
static void rsu_getradiocaps(struct ieee80211com *, int, int *,
struct ieee80211_channel[]);
static void rsu_set_channel(struct ieee80211com *);
static void rsu_scan_curchan(struct ieee80211_scan_state *, unsigned long);
static void rsu_scan_mindwell(struct ieee80211_scan_state *);
static void rsu_update_mcast(struct ieee80211com *);
static int rsu_alloc_rx_list(struct rsu_softc *);
static void rsu_free_rx_list(struct rsu_softc *);
@ -203,7 +205,8 @@ static int rsu_newstate(struct ieee80211vap *, enum ieee80211_state, int);
static void rsu_set_key(struct rsu_softc *, const struct ieee80211_key *);
static void rsu_delete_key(struct rsu_softc *, const struct ieee80211_key *);
#endif
static int rsu_site_survey(struct rsu_softc *, struct ieee80211vap *);
static int rsu_site_survey(struct rsu_softc *,
struct ieee80211_scan_ssid *);
static int rsu_join_bss(struct rsu_softc *, struct ieee80211_node *);
static int rsu_disconnect(struct rsu_softc *);
static int rsu_hwrssi_to_rssi(struct rsu_softc *, int hw_rssi);
@ -537,6 +540,7 @@ rsu_attach(device_t self)
ic->ic_txstream = sc->sc_ntxstream;
ic->ic_rxstream = sc->sc_nrxstream;
}
ic->ic_flags_ext |= IEEE80211_FEXT_SCAN_OFFLOAD;
rsu_getradiocaps(ic, IEEE80211_CHAN_MAX, &ic->ic_nchans,
ic->ic_channels);
@ -547,6 +551,8 @@ rsu_attach(device_t self)
ic->ic_scan_end = rsu_scan_end;
ic->ic_getradiocaps = rsu_getradiocaps;
ic->ic_set_channel = rsu_set_channel;
ic->ic_scan_curchan = rsu_scan_curchan;
ic->ic_scan_mindwell = rsu_scan_mindwell;
ic->ic_vap_create = rsu_vap_create;
ic->ic_vap_delete = rsu_vap_delete;
ic->ic_update_mcast = rsu_update_mcast;
@ -680,16 +686,21 @@ static void
rsu_scan_start(struct ieee80211com *ic)
{
struct rsu_softc *sc = ic->ic_softc;
struct ieee80211_scan_state *ss = ic->ic_scan;
struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
int error;
/* Scanning is done by the firmware. */
RSU_LOCK(sc);
/* XXX TODO: force awake if in in network-sleep? */
error = rsu_site_survey(sc, TAILQ_FIRST(&ic->ic_vaps));
sc->sc_active_scan = !!(ss->ss_flags & IEEE80211_SCAN_ACTIVE);
/* XXX TODO: force awake if in network-sleep? */
error = rsu_site_survey(sc, ss->ss_nssid > 0 ? &ss->ss_ssid[0] : NULL);
RSU_UNLOCK(sc);
if (error != 0)
if (error != 0) {
device_printf(sc->sc_dev,
"could not send site survey command\n");
ieee80211_cancel_scan(vap);
}
}
static void
@ -721,6 +732,24 @@ rsu_set_channel(struct ieee80211com *ic __unused)
/* We are unable to switch channels, yet. */
}
static void
rsu_scan_curchan(struct ieee80211_scan_state *ss, unsigned long maxdwell)
{
/* Scan is done in rsu_scan_start(). */
}
/**
* Called by the net80211 framework to indicate
* the minimum dwell time has been met, terminate the scan.
* We don't actually terminate the scan as the firmware will notify
* us when it's finished and we have no way to interrupt it.
*/
static void
rsu_scan_mindwell(struct ieee80211_scan_state *ss)
{
/* NB: don't try to abort scan; wait for firmware to finish */
}
static void
rsu_update_mcast(struct ieee80211com *ic)
{
@ -1323,31 +1352,36 @@ rsu_delete_key(struct rsu_softc *sc, const struct ieee80211_key *k)
#endif
static int
rsu_site_survey(struct rsu_softc *sc, struct ieee80211vap *vap)
rsu_site_survey(struct rsu_softc *sc, struct ieee80211_scan_ssid *ssid)
{
struct r92s_fw_cmd_sitesurvey cmd;
struct ieee80211com *ic = &sc->sc_ic;
int r;
RSU_ASSERT_LOCKED(sc);
memset(&cmd, 0, sizeof(cmd));
if ((ic->ic_flags & IEEE80211_F_ASCAN) || sc->sc_scan_pass == 1)
/* TODO: passive channels? */
if (sc->sc_active_scan)
cmd.active = htole32(1);
cmd.limit = htole32(48);
if (sc->sc_scan_pass == 1 && vap->iv_des_nssid > 0) {
/* Do a directed scan for second pass. */
cmd.ssidlen = htole32(vap->iv_des_ssid[0].len);
memcpy(cmd.ssid, vap->iv_des_ssid[0].ssid,
vap->iv_des_ssid[0].len);
if (ssid != NULL) {
sc->sc_extra_scan = 1;
cmd.ssidlen = htole32(ssid->len);
memcpy(cmd.ssid, ssid->ssid, ssid->len);
}
DPRINTF("sending site survey command, pass=%d\n", sc->sc_scan_pass);
r = rsu_fw_cmd(sc, R92S_CMD_SITE_SURVEY, &cmd, sizeof(cmd));
if (r == 0) {
sc->sc_scanning = 1;
#ifdef USB_DEBUG
if (rsu_debug & (RSU_DEBUG_SCAN | RSU_DEBUG_FWCMD)) {
device_printf(sc->sc_dev,
"sending site survey command, active %d",
le32toh(cmd.active));
if (ssid != NULL) {
printf(", ssid: ");
ieee80211_print_essid(cmd.ssid, le32toh(cmd.ssidlen));
}
printf("\n");
}
return (r);
#endif
return (rsu_fw_cmd(sc, R92S_CMD_SITE_SURVEY, &cmd, sizeof(cmd)));
}
static int
@ -1362,28 +1396,9 @@ rsu_join_bss(struct rsu_softc *sc, struct ieee80211_node *ni)
uint8_t *frm;
uint8_t opmode;
int error;
int cnt;
char *msg = "rsujoin";
RSU_ASSERT_LOCKED(sc);
/*
* Until net80211 scanning doesn't automatically finish
* before we tell it to, let's just wait until any pending
* scan is done.
*
* XXX TODO: yes, this releases and re-acquires the lock.
* We should re-verify the state whenever we re-attempt this!
*/
cnt = 0;
while (sc->sc_scanning && cnt < 10) {
device_printf(sc->sc_dev,
"%s: still scanning! (attempt %d)\n",
__func__, cnt);
msleep(msg, &sc->sc_mtx, 0, msg, hz / 2);
cnt++;
}
/* Let the FW decide the opmode based on the capinfo field. */
opmode = NDIS802_11AUTOUNKNOWN;
RSU_DPRINTF(sc, RSU_DEBUG_RESET,
@ -1634,26 +1649,24 @@ rsu_rx_event(struct rsu_softc *sc, uint8_t code, uint8_t *buf, int len)
break;
case R92S_EVT_SURVEY_DONE:
RSU_DPRINTF(sc, RSU_DEBUG_SCAN,
"%s: site survey pass %d done, found %d BSS\n",
__func__, sc->sc_scan_pass, le32toh(*(uint32_t *)buf));
sc->sc_scanning = 0;
if (vap->iv_state != IEEE80211_S_SCAN)
break; /* Ignore if not scanning. */
/*
* XXX TODO: This needs to be done without a transition to
* the SCAN state again. Grr.
*/
if (sc->sc_scan_pass == 0 && vap->iv_des_nssid != 0) {
/* Schedule a directed scan for hidden APs. */
/* XXX bad! */
sc->sc_scan_pass = 1;
RSU_UNLOCK(sc);
ieee80211_new_state(vap, IEEE80211_S_SCAN, -1);
RSU_LOCK(sc);
"%s: %s scan done, found %d BSS\n",
__func__, sc->sc_extra_scan ? "direct" : "broadcast",
le32toh(*(uint32_t *)buf));
if (sc->sc_extra_scan == 1) {
/* Send broadcast probe request. */
sc->sc_extra_scan = 0;
if (vap != NULL && rsu_site_survey(sc, NULL) != 0) {
RSU_UNLOCK(sc);
ieee80211_cancel_scan(vap);
RSU_LOCK(sc);
}
break;
}
sc->sc_scan_pass = 0;
if (vap != NULL) {
RSU_UNLOCK(sc);
ieee80211_scan_done(vap);
RSU_LOCK(sc);
}
break;
case R92S_EVT_JOIN_BSS:
if (vap->iv_state == IEEE80211_S_AUTH)
@ -2920,12 +2933,11 @@ rsu_init(struct rsu_softc *sc)
goto fail;
}
sc->sc_scan_pass = 0;
sc->sc_extra_scan = 0;
usbd_transfer_start(sc->sc_xfer[RSU_BULK_RX]);
/* We're ready to go. */
sc->sc_running = 1;
sc->sc_scanning = 0;
return;
fail:
/* Need to stop all failed transfers, if any */

View file

@ -768,8 +768,8 @@ struct rsu_softc {
u_int sc_running:1,
sc_calibrating:1,
sc_scanning:1,
sc_scan_pass:1;
sc_active_scan:1,
sc_extra_scan:1;
u_int cut;
uint8_t sc_rftype;
int8_t sc_nrxstream;