aboutsummaryrefslogtreecommitdiff
path: root/sys/dev/iwx/if_iwx.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/dev/iwx/if_iwx.c')
-rw-r--r--sys/dev/iwx/if_iwx.c274
1 files changed, 172 insertions, 102 deletions
diff --git a/sys/dev/iwx/if_iwx.c b/sys/dev/iwx/if_iwx.c
index 04ed09f04604..3c953e522973 100644
--- a/sys/dev/iwx/if_iwx.c
+++ b/sys/dev/iwx/if_iwx.c
@@ -3429,6 +3429,14 @@ iwx_sta_rx_agg(struct iwx_softc *sc, struct ieee80211_node *ni, uint8_t tid,
sc->sc_rx_ba_sessions--;
}
+/**
+ * @brief Allocate an A-MPDU / aggregation session for the given node and TID.
+ *
+ * This allocates a TX queue specifically for that TID.
+ *
+ * Note that this routine currently doesn't return any status/errors,
+ * so the caller can't know if the aggregation session was setup or not.
+ */
static void
iwx_sta_tx_agg_start(struct iwx_softc *sc, struct ieee80211_node *ni,
uint8_t tid)
@@ -3502,6 +3510,14 @@ iwx_ba_rx_task(void *arg, int npending __unused)
IWX_UNLOCK(sc);
}
+/**
+ * @brief Task called to setup a deferred block-ack session.
+ *
+ * This sets up any/all pending blockack sessions as defined
+ * in sc->ba_tx.start_tidmask.
+ *
+ * Note: the call to iwx_sta_tx_agg_start() isn't being error checked.
+ */
static void
iwx_ba_tx_task(void *arg, int npending __unused)
{
@@ -3509,22 +3525,38 @@ iwx_ba_tx_task(void *arg, int npending __unused)
struct ieee80211com *ic = &sc->sc_ic;
struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
struct ieee80211_node *ni = vap->iv_bss;
+ uint32_t started_mask = 0;
int tid;
IWX_LOCK(sc);
for (tid = 0; tid < IWX_MAX_TID_COUNT; tid++) {
+ const struct ieee80211_tx_ampdu *tap;
+
if (sc->sc_flags & IWX_FLAG_SHUTDOWN)
break;
+ tap = &ni->ni_tx_ampdu[tid];
+ if (IEEE80211_AMPDU_RUNNING(tap))
+ break;
if (sc->ba_tx.start_tidmask & (1 << tid)) {
- DPRINTF(("%s: ampdu tx start for tid %i\n", __func__,
- tid));
+ IWX_DPRINTF(sc, IWX_DEBUG_AMPDU_MGMT,
+ "%s: ampdu tx start for tid %i\n", __func__, tid);
iwx_sta_tx_agg_start(sc, ni, tid);
sc->ba_tx.start_tidmask &= ~(1 << tid);
- sc->sc_flags |= IWX_FLAG_AMPDUTX;
+ started_mask |= (1 << tid);
}
}
IWX_UNLOCK(sc);
+
+ /* Iterate over the sessions we started; mark them as active */
+ for (tid = 0; tid < IWX_MAX_TID_COUNT; tid++) {
+ if (started_mask & (1 << tid)) {
+ IWX_DPRINTF(sc, IWX_DEBUG_AMPDU_MGMT,
+ "%s: informing net80211 to start ampdu on tid %i\n",
+ __func__, tid);
+ ieee80211_ampdu_tx_request_active_ext(ni, tid, 1);
+ }
+ }
}
static void
@@ -4575,37 +4607,39 @@ iwx_rx_mpdu_mq(struct iwx_softc *sc, struct mbuf *m, void *pktdata,
pad = 1;
}
-// /*
-// * Hardware de-aggregates A-MSDUs and copies the same MAC header
-// * in place for each subframe. But it leaves the 'A-MSDU present'
-// * bit set in the frame header. We need to clear this bit ourselves.
-// * (XXX This workaround is not required on AX200/AX201 devices that
-// * have been tested by me, but it's unclear when this problem was
-// * fixed in the hardware. It definitely affects the 9k generation.
-// * Leaving this in place for now since some 9k/AX200 hybrids seem
-// * to exist that we may eventually add support for.)
-// *
-// * And we must allow the same CCMP PN for subframes following the
-// * first subframe. Otherwise they would be discarded as replays.
-// */
+ /* If it's a HT node then perform re-order processing */
+ if (ni->ni_flags & IEEE80211_NODE_HT)
+ m->m_flags |= M_AMPDU;
+
+ /*
+ * Hardware de-aggregates A-MSDUs and copies the same MAC header
+ * in place for each subframe. But it leaves the 'A-MSDU present'
+ * bit set in the frame header. We need to clear this bit ourselves.
+ * (XXX This workaround is not required on AX200/AX201 devices that
+ * have been tested by me, but it's unclear when this problem was
+ * fixed in the hardware. It definitely affects the 9k generation.
+ * Leaving this in place for now since some 9k/AX200 hybrids seem
+ * to exist that we may eventually add support for.)
+ *
+ * And we must allow the same CCMP PN for subframes following the
+ * first subframe. Otherwise they would be discarded as replays.
+ */
if (desc->mac_flags2 & IWX_RX_MPDU_MFLG2_AMSDU) {
- DPRINTF(("%s: === IWX_RX_MPDU_MFLG2_AMSDU\n", __func__));
-// struct ieee80211_frame *wh = mtod(m, struct ieee80211_frame *);
-// uint8_t subframe_idx = (desc->amsdu_info &
-// IWX_RX_MPDU_AMSDU_SUBFRAME_IDX_MASK);
-// if (subframe_idx > 0)
-// rxi.rxi_flags |= IEEE80211_RXI_HWDEC_SAME_PN;
-// if (ieee80211_has_qos(wh) && ieee80211_has_addr4(wh) &&
-// m->m_len >= sizeof(struct ieee80211_qosframe_addr4)) {
-// struct ieee80211_qosframe_addr4 *qwh4 = mtod(m,
-// struct ieee80211_qosframe_addr4 *);
-// qwh4->i_qos[0] &= htole16(~IEEE80211_QOS_AMSDU);
-// } else if (ieee80211_has_qos(wh) &&
-// m->m_len >= sizeof(struct ieee80211_qosframe)) {
-// struct ieee80211_qosframe *qwh = mtod(m,
-// struct ieee80211_qosframe *);
-// qwh->i_qos[0] &= htole16(~IEEE80211_QOS_AMSDU);
-// }
+ struct ieee80211_frame *wh = mtod(m, struct ieee80211_frame *);
+ uint8_t subframe_idx = (desc->amsdu_info &
+ IWX_RX_MPDU_AMSDU_SUBFRAME_IDX_MASK);
+ uint8_t *qos;
+
+ rxs.c_pktflags |= IEEE80211_RX_F_AMSDU;
+ if (subframe_idx > 0)
+ rxs.c_pktflags |= IEEE80211_RX_F_AMSDU_MORE;
+
+ /* XXX should keep driver statistics about this */
+ IWX_DPRINTF(sc, IWX_DEBUG_AMPDU_MGMT,
+ "%s: === IWX_RX_MPDU_MFLG2_AMSDU\n", __func__);
+
+ qos = ieee80211_getqos(wh);
+ qos[0] &= ~IEEE80211_QOS_AMSDU;
}
/*
@@ -5627,7 +5661,6 @@ iwx_tx(struct iwx_softc *sc, struct mbuf *m, struct ieee80211_node *ni)
u_int hdrlen;
uint32_t rate_n_flags;
uint16_t num_tbs, flags, offload_assist = 0;
- uint8_t type, subtype;
int i, totlen, err, pad, qid;
#define IWM_MAX_SCATTER 20
bus_dma_segment_t *seg, segs[IWM_MAX_SCATTER];
@@ -5638,38 +5671,32 @@ iwx_tx(struct iwx_softc *sc, struct mbuf *m, struct ieee80211_node *ni)
IWX_ASSERT_LOCKED(sc);
wh = mtod(m, struct ieee80211_frame *);
- type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK;
- subtype = wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK;
hdrlen = ieee80211_anyhdrsize(wh);
qid = sc->first_data_qid;
/* Put QoS frames on the data queue which maps to their TID. */
- if (IEEE80211_QOS_HAS_SEQ(wh) && (sc->sc_flags & IWX_FLAG_AMPDUTX)) {
+ if (IEEE80211_QOS_HAS_SEQ(wh)) {
uint16_t qos = ieee80211_gettid(wh);
uint8_t tid = qos & IEEE80211_QOS_TID;
-#if 0
+ struct ieee80211_tx_ampdu *tap = &ni->ni_tx_ampdu[tid];
+
/*
- * XXX-THJ: TODO when we enable ba we need to manage the
- * mappings
+ * Note: we're currently putting all frames into one queue
+ * except for A-MPDU queues. We should be able to choose
+ * other WME queues but first we need to verify they've been
+ * correctly setup for data.
*/
- struct ieee80211_tx_ba *ba;
- ba = &ni->ni_tx_ba[tid];
- if (!IEEE80211_IS_MULTICAST(wh->i_addr1) &&
- type == IEEE80211_FC0_TYPE_DATA &&
- subtype != IEEE80211_FC0_SUBTYPE_NODATA &&
- subtype != IEEE80211_FC0_SUBTYPE_BAR &&
- sc->aggqid[tid] != 0 /*&&
- ba->ba_state == IEEE80211_BA_AGREED*/) {
- qid = sc->aggqid[tid];
-#else
- if (!IEEE80211_IS_MULTICAST(wh->i_addr1) &&
- type == IEEE80211_FC0_TYPE_DATA &&
- subtype != IEEE80211_FC0_SUBTYPE_NODATA &&
+ /*
+ * Only QoS data goes into an A-MPDU queue;
+ * don't add QoS null, the other data types, etc.
+ */
+ if (IEEE80211_AMPDU_RUNNING(tap) &&
+ IEEE80211_IS_QOSDATA(wh) &&
+ !IEEE80211_IS_MULTICAST(wh->i_addr1) &&
sc->aggqid[tid] != 0) {
qid = sc->aggqid[tid];
-#endif
}
}
@@ -10711,9 +10738,13 @@ iwx_suspend(device_t dev)
struct iwx_softc *sc = device_get_softc(dev);
struct ieee80211com *ic = &sc->sc_ic;
- if (sc->sc_flags & IWX_FLAG_HW_INITED) {
- ieee80211_suspend_all(ic);
+ /*
+ * Suspend everything first, then shutdown hardware if it's
+ * still up.
+ */
+ ieee80211_suspend_all(ic);
+ if (sc->sc_flags & IWX_FLAG_HW_INITED) {
iwx_stop(sc);
sc->sc_flags &= ~IWX_FLAG_HW_INITED;
}
@@ -10725,7 +10756,6 @@ iwx_resume(device_t dev)
{
struct iwx_softc *sc = device_get_softc(dev);
struct ieee80211com *ic = &sc->sc_ic;
- int err;
/*
* We disable the RETRY_TIMEOUT register (0x41) to keep
@@ -10735,15 +10765,15 @@ iwx_resume(device_t dev)
IWX_LOCK(sc);
- err = iwx_init(sc);
- if (err) {
- iwx_stop_device(sc);
- IWX_UNLOCK(sc);
- return err;
+ /* Stop the hardware here if it's still thought of as "up" */
+ if (sc->sc_flags & IWX_FLAG_HW_INITED) {
+ iwx_stop(sc);
+ sc->sc_flags &= ~IWX_FLAG_HW_INITED;
}
IWX_UNLOCK(sc);
+ /* Start the VAPs, which will bring the hardware back up again */
ieee80211_resume_all(ic);
return (0);
}
@@ -10900,6 +10930,26 @@ iwx_ampdu_rx_stop(struct ieee80211_node *ni, struct ieee80211_rx_ampdu *rap)
return;
}
+/**
+ * @brief Called by net80211 to request an A-MPDU session be established.
+ *
+ * This is called by net80211 to see if an A-MPDU session can be established.
+ * However, the iwx(4) firmware will take care of establishing the BA
+ * session for us. net80211 doesn't have to send any action frames here;
+ * it just needs to plumb up the ampdu session once the BA has been sent.
+ *
+ * If we return 0 here then the firmware will set up the state but net80211
+ * will not; so it's on us to actually complete it via a call to
+ * ieee80211_ampdu_tx_request_active_ext() .
+ *
+ * @param ni ieee80211_node to establish A-MPDU session for
+ * @param tap pointer to the per-TID state struct
+ * @param dialogtoken dialogtoken field from the BA request
+ * @param baparamset baparamset field from the BA request
+ * @param batimeout batimeout field from the BA request
+ *
+ * @returns 0 so net80211 doesn't send the BA action frame to establish A-MPDU.
+ */
static int
iwx_addba_request(struct ieee80211_node *ni, struct ieee80211_tx_ampdu *tap,
int dialogtoken, int baparamset, int batimeout)
@@ -10908,10 +10958,22 @@ iwx_addba_request(struct ieee80211_node *ni, struct ieee80211_tx_ampdu *tap,
int tid;
tid = _IEEE80211_MASKSHIFT(le16toh(baparamset), IEEE80211_BAPS_TID);
- DPRINTF(("%s: tid=%i\n", __func__, tid));
+ IWX_DPRINTF(sc, IWX_DEBUG_AMPDU_MGMT,
+ "%s: queuing AMPDU start on tid %i\n", __func__, tid);
+
+ /* There's no nice way right now to tell net80211 that we're in the
+ * middle of an asynchronous ADDBA setup session. So, bump the timeout
+ * to hz ticks, hopefully we'll get a response by then.
+ */
+ tap->txa_nextrequest = ticks + hz;
+
+ IWX_LOCK(sc);
sc->ba_tx.start_tidmask |= (1 << tid);
+ IWX_UNLOCK(sc);
+
taskqueue_enqueue(sc->sc_tq, &sc->ba_tx_task);
- return 0;
+
+ return (0);
}
@@ -10940,28 +11002,20 @@ iwx_key_alloc(struct ieee80211vap *vap, struct ieee80211_key *k,
{
if (k->wk_cipher->ic_cipher == IEEE80211_CIPHER_AES_CCM) {
- return 1;
+ return (1);
}
- if (!(&vap->iv_nw_keys[0] <= k &&
- k < &vap->iv_nw_keys[IEEE80211_WEP_NKID])) {
- /*
- * Not in the global key table, the driver should handle this
- * by allocating a slot in the h/w key table/cache. In
- * lieu of that return key slot 0 for any unicast key
- * request. We disallow the request if this is a group key.
- * This default policy does the right thing for legacy hardware
- * with a 4 key table. It also handles devices that pass
- * packets through untouched when marked with the WEP bit
- * and key index 0.
- */
- if (k->wk_flags & IEEE80211_KEY_GROUP)
- return 0;
+
+ if (ieee80211_is_key_unicast(vap, k)) {
*keyix = 0; /* NB: use key index 0 for ucast key */
- } else {
+ } else if (ieee80211_is_key_global(vap, k)) {
*keyix = ieee80211_crypto_get_key_wepidx(vap, k);
+ } else {
+ net80211_vap_printf(vap, "%s: invalid crypto key type\n",
+ __func__);
+ return (0);
}
*rxkeyix = IEEE80211_KEYIX_NONE; /* XXX maybe *keyix? */
- return 1;
+ return (1);
}
static int
@@ -10978,7 +11032,6 @@ iwx_key_set(struct ieee80211vap *vap, const struct ieee80211_key *k)
return 1;
}
- IWX_LOCK(sc);
/*
* Keys are stored in 'ni' so 'k' is valid if 'ni' is valid.
* Currently we only implement station mode where 'ni' is always
@@ -10987,37 +11040,45 @@ iwx_key_set(struct ieee80211vap *vap, const struct ieee80211_key *k)
memset(&cmd, 0, sizeof(cmd));
- if (k->wk_flags & IEEE80211_KEY_GROUP) {
- DPRINTF(("%s: adding group key\n", __func__));
+ if (ieee80211_is_key_global(vap, k)) {
+ id = ieee80211_crypto_get_key_wepidx(vap, k);
+ IWX_DPRINTF(sc, IWX_DEBUG_KEYMGMT, "%s: adding group key\n",
+ __func__);
+ } else if (ieee80211_is_key_unicast(vap, k)) {
+ IWX_DPRINTF(sc, IWX_DEBUG_KEYMGMT, "%s: adding key\n",
+ __func__);
+ id = 0; /* net80211 currently only supports unicast key 0 */
} else {
- DPRINTF(("%s: adding key\n", __func__));
+ net80211_vap_printf(vap, "%s: unknown key type\n", __func__);
+ return (ENXIO);
}
- if (k >= &vap->iv_nw_keys[0] &&
- k < &vap->iv_nw_keys[IEEE80211_WEP_NKID])
- id = (k - vap->iv_nw_keys);
- else
- id = (0);
- DPRINTF(("%s: setting keyid=%i\n", __func__, id));
+
+ IWX_LOCK(sc);
+
cmd.common.key_flags = htole16(IWX_STA_KEY_FLG_CCM |
IWX_STA_KEY_FLG_WEP_KEY_MAP |
((id << IWX_STA_KEY_FLG_KEYID_POS) &
IWX_STA_KEY_FLG_KEYID_MSK));
- if (k->wk_flags & IEEE80211_KEY_GROUP) {
+ if (ieee80211_is_key_global(vap, k)) {
cmd.common.key_offset = 1;
cmd.common.key_flags |= htole16(IWX_STA_KEY_MULTICAST);
- } else {
+ } else if (ieee80211_is_key_unicast(vap, k)) {
cmd.common.key_offset = 0;
+ } else {
+ net80211_vap_printf(vap, "%s: unknown key type\n", __func__);
+ IWX_UNLOCK(sc);
+ return (ENXIO);
}
memcpy(cmd.common.key, k->wk_key, MIN(sizeof(cmd.common.key),
k->wk_keylen));
- DPRINTF(("%s: wk_keylen=%i\n", __func__, k->wk_keylen));
- for (int i=0; i<k->wk_keylen; i++) {
- DPRINTF(("%s: key[%d]=%x\n", __func__, i, k->wk_key[i]));
- }
+ IWX_DPRINTF(sc, IWX_DEBUG_KEYMGMT, "%s: key: id=%d, len=%i, key=%*D\n",
+ __func__, id, k->wk_keylen, k->wk_keylen,
+ (const unsigned char *) k->wk_key, "");
cmd.common.sta_id = IWX_STATION_ID;
cmd.transmit_seq_cnt = htole64(k->wk_keytsc);
- DPRINTF(("%s: k->wk_keytsc=%lu\n", __func__, k->wk_keytsc));
+ IWX_DPRINTF(sc, IWX_DEBUG_KEYMGMT, "%s: k->wk_keytsc=%lu\n", __func__,
+ k->wk_keytsc);
status = IWX_ADD_STA_SUCCESS;
err = iwx_send_cmd_pdu_status(sc, IWX_ADD_STA_KEY, sizeof(cmd), &cmd,
@@ -11025,19 +11086,28 @@ iwx_key_set(struct ieee80211vap *vap, const struct ieee80211_key *k)
if (!err && (status & IWX_ADD_STA_STATUS_MASK) != IWX_ADD_STA_SUCCESS)
err = EIO;
if (err) {
- printf("%s: can't set wpa2 keys (error %d)\n", __func__, err);
+ net80211_vap_printf(vap,
+ "%s: can't set wpa2 keys (error %d)\n", __func__, err);
IWX_UNLOCK(sc);
return err;
} else
- DPRINTF(("%s: key added successfully\n", __func__));
+ IWX_DPRINTF(sc, IWX_DEBUG_KEYMGMT,
+ "%s: key added successfully\n", __func__);
IWX_UNLOCK(sc);
- return 1;
+ return (1);
}
static int
iwx_key_delete(struct ieee80211vap *vap, const struct ieee80211_key *k)
{
- return 1;
+ /*
+ * Note: since there's no key allocations to track - it's either
+ * the 4 static WEP keys or the single unicast key - there's nothing
+ * else to do here.
+ *
+ * This would need some further work to support IBSS/mesh/AP modes.
+ */
+ return (1);
}
static device_method_t iwx_pci_methods[] = {