summaryrefslogtreecommitdiff
path: root/sys/dev/pci/if_iwi.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/dev/pci/if_iwi.c')
-rw-r--r--sys/dev/pci/if_iwi.c211
1 files changed, 124 insertions, 87 deletions
diff --git a/sys/dev/pci/if_iwi.c b/sys/dev/pci/if_iwi.c
index 09ff28a340d..0e6ca56bd0f 100644
--- a/sys/dev/pci/if_iwi.c
+++ b/sys/dev/pci/if_iwi.c
@@ -1,30 +1,20 @@
-/* $OpenBSD: if_iwi.c,v 1.89 2008/08/27 09:28:38 damien Exp $ */
+/* $OpenBSD: if_iwi.c,v 1.90 2008/08/28 15:52:20 damien Exp $ */
/*-
- * Copyright (c) 2004-2006
+ * Copyright (c) 2004-2008
* Damien Bergamini <damien.bergamini@free.fr>. 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 unmodified, 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.
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
*
- * 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.
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
/*
@@ -119,6 +109,7 @@ int iwi_reset(struct iwi_softc *);
int iwi_load_ucode(struct iwi_softc *, const char *, int);
int iwi_load_firmware(struct iwi_softc *, const char *, int);
int iwi_config(struct iwi_softc *);
+void iwi_update_edca(struct ieee80211com *);
int iwi_set_chan(struct iwi_softc *, struct ieee80211_channel *);
int iwi_scan(struct iwi_softc *);
int iwi_auth_and_assoc(struct iwi_softc *);
@@ -275,7 +266,8 @@ iwi_attach(struct device *parent, struct device *self, void *aux)
IEEE80211_C_TXPMGT | /* tx power management */
IEEE80211_C_SHPREAMBLE | /* short preamble supported */
IEEE80211_C_SHSLOT | /* short slot time supported */
- IEEE80211_C_WEP | /* h/w WEP supported */
+ IEEE80211_C_WEP | /* s/w WEP */
+ IEEE80211_C_RSN | /* WPA/RSN supported */
IEEE80211_C_SCANALL; /* h/w scanning */
/* read MAC address from EEPROM */
@@ -602,7 +594,6 @@ iwi_alloc_rx_ring(struct iwi_softc *sc, struct iwi_rx_ring *ring)
error = ENOMEM;
goto fail;
}
-
MCLGET(data->m, M_DONTWAIT);
if (!(data->m->m_flags & M_EXT)) {
m_freem(data->m);
@@ -939,25 +930,6 @@ iwi_frame_intr(struct iwi_softc *sc, struct iwi_rx_data *data,
sizeof (struct iwi_frame) + letoh16(frame->len);
m_adj(m, sizeof (struct iwi_hdr) + sizeof (struct iwi_frame));
- wh = mtod(m, struct ieee80211_frame *);
-
- rxi.rxi_flags = 0;
- if (wh->i_fc[1] & IEEE80211_FC1_WEP) {
- /*
- * Hardware decrypts the frame itself but leaves the WEP bit
- * set in the 802.11 header and doesn't remove the IV and CRC
- * fields.
- */
- wh->i_fc[1] &= ~IEEE80211_FC1_WEP;
- ovbcopy(wh, (char *)wh + IEEE80211_WEP_IVLEN +
- IEEE80211_WEP_KIDLEN, sizeof (struct ieee80211_frame));
- m_adj(m, IEEE80211_WEP_IVLEN + IEEE80211_WEP_KIDLEN);
- m_adj(m, -IEEE80211_WEP_CRCLEN);
- wh = mtod(m, struct ieee80211_frame *);
-
- rxi.rxi_flags |= IEEE80211_RXI_HWDEC;
- }
-
#if NBPFILTER > 0
if (sc->sc_drvbpf != NULL) {
struct mbuf mb;
@@ -984,9 +956,11 @@ iwi_frame_intr(struct iwi_softc *sc, struct iwi_rx_data *data,
}
#endif
+ wh = mtod(m, struct ieee80211_frame *);
ni = ieee80211_find_rxnode(ic, wh);
/* send the frame to the upper layer */
+ rxi.rxi_flags = 0;
rxi.rxi_rssi = frame->rssi_dbm;
rxi.rxi_tstamp = 0; /* unused */
ieee80211_input(ifp, m, ni, &rxi);
@@ -1271,11 +1245,25 @@ iwi_tx_start(struct ifnet *ifp, struct mbuf *m0, struct ieee80211_node *ni)
{
struct iwi_softc *sc = ifp->if_softc;
struct ieee80211com *ic = &sc->sc_ic;
+ struct ieee80211_frame *wh;
+ struct ieee80211_key *k;
struct iwi_tx_data *data;
struct iwi_tx_desc *desc;
struct iwi_tx_ring *txq = &sc->txq[0];
struct mbuf *mnew;
- int error, i, station = 0;
+ int hdrlen, error, i, station = 0;
+
+ wh = mtod(m0, struct ieee80211_frame *);
+
+ if (wh->i_fc[1] & IEEE80211_FC1_PROTECTED) {
+ k = ieee80211_get_txkey(ic, wh, ni);
+
+ if ((m0 = ieee80211_encrypt(ic, m0, k)) == NULL)
+ return ENOBUFS;
+
+ /* packet header may have moved, reset our local pointer */
+ wh = mtod(m0, struct ieee80211_frame *);
+ }
#if NBPFILTER > 0
if (sc->sc_drvbpf != NULL) {
@@ -1299,9 +1287,10 @@ iwi_tx_start(struct ifnet *ifp, struct mbuf *m0, struct ieee80211_node *ni)
data = &txq->data[txq->cur];
desc = &txq->desc[txq->cur];
- /* save and trim IEEE802.11 header */
- m_copydata(m0, 0, sizeof (struct ieee80211_frame), (caddr_t)&desc->wh);
- m_adj(m0, sizeof (struct ieee80211_frame));
+ /* copy and trim IEEE802.11 header */
+ hdrlen = ieee80211_get_hdrlen(wh);
+ bcopy(wh, &desc->wh, hdrlen);
+ m_adj(m0, hdrlen);
#ifndef IEEE80211_STA_ONLY
if (ic->ic_opmode == IEEE80211_M_IBSS) {
@@ -1331,7 +1320,6 @@ iwi_tx_start(struct ifnet *ifp, struct mbuf *m0, struct ieee80211_node *ni)
m_freem(m0);
return ENOMEM;
}
-
M_DUP_PKTHDR(mnew, m0);
if (m0->m_pkthdr.len > MHLEN) {
MCLGET(mnew, M_DONTWAIT);
@@ -1365,24 +1353,20 @@ iwi_tx_start(struct ifnet *ifp, struct mbuf *m0, struct ieee80211_node *ni)
desc->cmd = IWI_DATA_CMD_TX;
desc->len = htole16(m0->m_pkthdr.len);
desc->station = station;
- desc->flags = 0;
+ desc->flags = IWI_DATA_FLAG_NO_WEP;
desc->xflags = 0;
if (!IEEE80211_IS_MULTICAST(desc->wh.i_addr1))
desc->flags |= IWI_DATA_FLAG_NEED_ACK;
- if (desc->wh.i_fc[1] & IEEE80211_FC1_WEP) {
- desc->wep_txkey = ic->ic_wep_txkey |
- ((ic->ic_nw_keys[ic->ic_wep_txkey].k_cipher ==
- IEEE80211_CIPHER_WEP40) ? IWI_DATA_KEY_WEP40 :
- IWI_DATA_KEY_WEP104);
- } else {
- desc->flags |= IWI_DATA_FLAG_NO_WEP;
- desc->wep_txkey = 0;
- }
if (ic->ic_flags & IEEE80211_F_SHPREAMBLE)
desc->flags |= IWI_DATA_FLAG_SHPREAMBLE;
+ if ((desc->wh.i_fc[0] &
+ (IEEE80211_FC0_TYPE_MASK | IEEE80211_FC0_SUBTYPE_QOS)) ==
+ (IEEE80211_FC0_TYPE_DATA | IEEE80211_FC0_SUBTYPE_QOS))
+ desc->xflags |= IWI_DATA_XFLAG_QOS;
+
if (ic->ic_curmode == IEEE80211_MODE_11B)
desc->xflags |= IWI_DATA_XFLAG_CCK;
@@ -1820,8 +1804,6 @@ iwi_config(struct iwi_softc *sc)
struct iwi_configuration config;
struct iwi_rateset rs;
struct iwi_txpower power;
- struct ieee80211_key *k;
- struct iwi_wep_key wepkey;
uint32_t data;
int error, nchan, i;
@@ -1944,34 +1926,62 @@ iwi_config(struct iwi_softc *sc)
return error;
}
- data = htole32(arc4random());
- DPRINTF(("Setting initialization vector to %u\n", letoh32(data)));
- error = iwi_cmd(sc, IWI_CMD_SET_IV, &data, sizeof data, 0);
+ data = arc4random();
+ DPRINTF(("Setting random seed to %u\n", data));
+ error = iwi_cmd(sc, IWI_CMD_SET_RANDOM_SEED, &data, sizeof data, 0);
if (error != 0)
return error;
- if (ic->ic_flags & IEEE80211_F_WEPON) {
- k = ic->ic_nw_keys;
- for (i = 0; i < IEEE80211_WEP_NKID; i++, k++) {
- wepkey.cmd = IWI_WEP_KEY_CMD_SETKEY;
- wepkey.idx = i;
- wepkey.len = k->k_len;
- bzero(wepkey.key, sizeof wepkey.key);
- bcopy(k->k_key, wepkey.key, k->k_len);
- DPRINTF(("Setting wep key index %u len %u\n",
- wepkey.idx, wepkey.len));
- error = iwi_cmd(sc, IWI_CMD_SET_WEP_KEY, &wepkey,
- sizeof wepkey, 0);
- if (error != 0)
- return error;
- }
- }
-
/* enable adapter */
DPRINTF(("Enabling adapter\n"));
return iwi_cmd(sc, IWI_CMD_ENABLE, NULL, 0, 0);
}
+void
+iwi_update_edca(struct ieee80211com *ic)
+{
+#define IWI_EXP2(v) htole16((1 << (v)) - 1)
+#define IWI_TXOP(v) IEEE80211_TXOP_TO_US(v)
+ struct iwi_softc *sc = ic->ic_softc;
+ struct iwi_qos_cmd cmd;
+ struct iwi_qos_params *qos;
+ struct ieee80211_edca_ac_params *edca = ic->ic_edca_ac;
+ int aci;
+
+ /* set default QoS parameters for CCK */
+ qos = &cmd.cck;
+ for (aci = 0; aci < EDCA_NUM_AC; aci++) {
+ qos->cwmin[aci] = IWI_EXP2(iwi_cck[aci].ac_ecwmin);
+ qos->cwmax[aci] = IWI_EXP2(iwi_cck[aci].ac_ecwmax);
+ qos->txop [aci] = IWI_TXOP(iwi_cck[aci].ac_txoplimit);
+ qos->aifsn[aci] = iwi_cck[aci].ac_aifsn;
+ qos->acm [aci] = 0;
+ }
+ /* set default QoS parameters for OFDM */
+ qos = &cmd.ofdm;
+ for (aci = 0; aci < EDCA_NUM_AC; aci++) {
+ qos->cwmin[aci] = IWI_EXP2(iwi_ofdm[aci].ac_ecwmin);
+ qos->cwmax[aci] = IWI_EXP2(iwi_ofdm[aci].ac_ecwmax);
+ qos->txop [aci] = IWI_TXOP(iwi_ofdm[aci].ac_txoplimit);
+ qos->aifsn[aci] = iwi_ofdm[aci].ac_aifsn;
+ qos->acm [aci] = 0;
+ }
+ /* set current QoS parameters */
+ qos = &cmd.current;
+ for (aci = 0; aci < EDCA_NUM_AC; aci++) {
+ qos->cwmin[aci] = IWI_EXP2(edca[aci].ac_ecwmin);
+ qos->cwmax[aci] = IWI_EXP2(edca[aci].ac_ecwmax);
+ qos->txop [aci] = IWI_TXOP(edca[aci].ac_txoplimit);
+ qos->aifsn[aci] = edca[aci].ac_aifsn;
+ qos->acm [aci] = 0;
+ }
+
+ DPRINTF(("Setting QoS parameters\n"));
+ (void)iwi_cmd(sc, IWI_CMD_SET_QOS_PARAMS, &cmd, sizeof cmd, 1);
+#undef IWI_EXP2
+#undef IWI_TXOP
+}
+
int
iwi_set_chan(struct iwi_softc *sc, struct ieee80211_channel *chan)
{
@@ -2039,13 +2049,17 @@ iwi_auth_and_assoc(struct iwi_softc *sc)
struct iwi_configuration config;
struct iwi_associate assoc;
struct iwi_rateset rs;
- uint16_t capinfo;
+ uint8_t *frm;
uint32_t data;
+ uint16_t capinfo;
+ uint8_t buf[64]; /* XXX max WPA/RSN/WMM IE length */
int error;
/* update adapter configuration */
bzero(&config, sizeof config);
config.multicast_enabled = 1;
+ config.disable_unicast_decryption = 1;
+ config.disable_multicast_decryption = 1;
config.silence_threshold = 30;
config.report_noise = 1;
config.answer_pbreq =
@@ -2098,6 +2112,27 @@ iwi_auth_and_assoc(struct iwi_softc *sc)
if (error != 0)
return error;
+ if (ic->ic_flags & IEEE80211_F_QOS) {
+ iwi_update_edca(ic);
+
+ frm = ieee80211_add_edca_params(buf, ic);
+ DPRINTF(("Setting EDCA IE length %d\n", frm - buf));
+ error = iwi_cmd(sc, IWI_CMD_SET_EDCAIE, buf, frm - buf, 1);
+ if (error != 0)
+ return error;
+ }
+ if (ic->ic_flags & IEEE80211_F_RSNON) {
+ /* tell firmware to add WPA/RSN IE to (re)assoc request */
+ if (ni->ni_rsnprotos == IEEE80211_PROTO_RSN)
+ frm = ieee80211_add_rsn(buf, ic, ni);
+ else
+ frm = ieee80211_add_wpa(buf, ic, ni);
+ DPRINTF(("Setting RSN IE length %d\n", frm - buf));
+ error = iwi_cmd(sc, IWI_CMD_SET_OPTIE, buf, frm - buf, 1);
+ if (error != 0)
+ return error;
+ }
+
bzero(&assoc, sizeof assoc);
#ifndef IEEE80211_STA_ONLY
if (ic->ic_flags & IEEE80211_F_SIBSS)
@@ -2105,6 +2140,11 @@ iwi_auth_and_assoc(struct iwi_softc *sc)
else
#endif
assoc.type = IWI_ASSOC_ASSOCIATE;
+ assoc.policy = 0;
+ if (ic->ic_flags & IEEE80211_F_RSNON)
+ assoc.policy |= htole16(IWI_ASSOC_POLICY_RSN);
+ if (ic->ic_flags & IEEE80211_F_QOS)
+ assoc.policy |= htole16(IWI_ASSOC_POLICY_QOS);
if (ic->ic_curmode == IEEE80211_MODE_11A)
assoc.mode = IWI_MODE_11A;
else if (ic->ic_curmode == IEEE80211_MODE_11B)
@@ -2112,15 +2152,12 @@ iwi_auth_and_assoc(struct iwi_softc *sc)
else /* assume 802.11b/g */
assoc.mode = IWI_MODE_11G;
assoc.chan = ieee80211_chan2ieee(ic, ni->ni_chan);
-#if 0
- if (ni->ni_challenge != NULL) /* XXX */
- assoc.auth = (ic->ic_wep_txkey << 4) | IWI_AUTH_SHARED;
-#endif
- if (ic->ic_flags & IEEE80211_F_SHPREAMBLE)
+ if ((ic->ic_flags & IEEE80211_F_SHPREAMBLE) &&
+ IEEE80211_IS_CHAN_2GHZ(ni->ni_chan))
assoc.plen = IWI_ASSOC_SHPREAMBLE;
bcopy(ni->ni_tstamp, assoc.tstamp, 8);
capinfo = IEEE80211_CAPINFO_ESS;
- if (ic->ic_flags & IEEE80211_F_WEPON)
+ if (ic->ic_flags & (IEEE80211_F_WEPON | IEEE80211_F_RSNON))
capinfo |= IEEE80211_CAPINFO_PRIVACY;
if ((ic->ic_flags & IEEE80211_F_SHPREAMBLE) &&
IEEE80211_IS_CHAN_2GHZ(ni->ni_chan))