--- if_iwn.c.orig 2008-03-08 11:24:44.000000000 -0500 +++ if_iwn.c.old 2009-05-24 12:45:32.000000000 -0400 @@ -152,321 +165,509 @@ -void -iwn_attach(struct device *parent, struct device *self, void *aux) +static int +iwn_attach(device_t dev) { /* * Allocate shared area (communication area). */ - if ((error = iwn_alloc_shared(sc)) != 0) { - printf(": could not allocate shared area\n"); - goto fail2; + error = iwn_alloc_shared(sc); + if (error != 0) { + device_printf(dev, + "could not allocate shared area, error %d\n", error); + goto fail; } [...] @@ -1665,102 +1878,84 @@ int iwn_tx_data(struct iwn_softc *sc, struct mbuf *m0, struct ieee80211_node *ni, - int ac) + struct iwn_tx_ring *ring) { - /* pickup a rate */ - if (IEEE80211_IS_MULTICAST(wh->i_addr1) || - type != IEEE80211_FC0_TYPE_DATA) { - /* mgmt/multicast frames are sent at the lowest avail. rate */ - rate = ni->ni_rates.rs_rates[0]; - } else if (ic->ic_fixed_rate != -1) { - rate = ic->ic_sup_rates[ic->ic_curmode]. - rs_rates[ic->ic_fixed_rate]; + /* pick a tx rate */ + /* XXX ni_chan */ + tp = &vap->iv_txparms[ieee80211_chan2mode(ic->ic_curchan)]; + if (type == IEEE80211_FC0_TYPE_MGT) + rate = tp->mgmtrate; + else if (ismcast) + rate = tp->mcastrate; + else if (tp->ucastrate != IEEE80211_FIXED_RATE_NONE) + rate = tp->ucastrate; + else { + (void) ieee80211_amrr_choose(ni, &IWN_NODE(ni)->amn); + rate = ni->ni_txrate; + } + + if (wh->i_fc[1] & IEEE80211_FC1_WEP) { + k = ieee80211_crypto_encap(ni, m0); + if (k == NULL) { + m_freem(m0); + return ENOBUFS; + } + /* packet header may have moved, reset our local pointer */ + wh = mtod(m0, struct ieee80211_frame *); } else - rate = ni->ni_rates.rs_rates[ni->ni_txrate]; - rate &= IEEE80211_RATE_VAL; + k = NULL; -#if NBPFILTER > 0 - if (sc->sc_drvbpf != NULL) { - struct mbuf mb; + if (ieee80211_radiotap_active_vap(vap)) { struct iwn_tx_radiotap_header *tap = &sc->sc_txtap; tap->wt_flags = 0; - tap->wt_chan_freq = htole16(ni->ni_chan->ic_freq); - tap->wt_chan_flags = htole16(ni->ni_chan->ic_flags); tap->wt_rate = rate; - tap->wt_hwqueue = ac; - if (wh->i_fc[1] & IEEE80211_FC1_WEP) + if (k != NULL) tap->wt_flags |= IEEE80211_RADIOTAP_F_WEP; - mb.m_data = (caddr_t)tap; - mb.m_len = sc->sc_txtap_len; - mb.m_next = m0; - mb.m_nextpkt = NULL; - mb.m_type = 0; - mb.m_flags = 0; - bpf_mtap(sc->sc_drvbpf, &mb, BPF_DIRECTION_OUT); + ieee80211_radiotap_tx(vap, m0); } -#endif - - cmd = &ring->cmd[ring->cur]; - cmd->code = IWN_CMD_TX_DATA; - cmd->flags = 0; - cmd->qid = ring->qid; - cmd->idx = ring->cur; - - tx = (struct iwn_cmd_data *)cmd->data; - /* no need to bzero tx, all fields are reinitialized here */ - - if (wh->i_fc[1] & IEEE80211_FC1_PROTECTED) { - const struct ieee80211_key *key = - &ic->ic_nw_keys[ic->ic_wep_txkey]; - if (key->k_cipher == IEEE80211_CIPHER_WEP40) - tx->security = IWN_CIPHER_WEP40; - else - tx->security = IWN_CIPHER_WEP104; - tx->security |= ic->ic_wep_txkey << 6; - memcpy(&tx->key[3], key->k_key, key->k_len); - /* compute crypto overhead */ - ovhd = IEEE80211_WEP_TOTLEN; - } else - tx->security = 0; flags = IWN_TX_AUTO_SEQ; - if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) + /* XXX honor ACM */ + if (!ismcast) flags |= IWN_TX_NEED_ACK; - if (IEEE80211_IS_MULTICAST(wh->i_addr1) || - type != IEEE80211_FC0_TYPE_DATA) - tx->id = IWN_ID_BROADCAST; + if (ismcast || type != IEEE80211_FC0_TYPE_DATA) + id = IWN_ID_BROADCAST; else - tx->id = IWN_ID_BSS; + id = IWN_ID_BSS; /* check if RTS/CTS or CTS-to-self protection must be used */ - if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) { + if (!ismcast) { /* multicast frames are not sent at OFDM rates in 802.11b/g */ - if (m0->m_pkthdr.len + ovhd + IEEE80211_CRC_LEN > - ic->ic_rtsthreshold) { + if (m0->m_pkthdr.len+IEEE80211_CRC_LEN > vap->iv_rtsthreshold) { flags |= IWN_TX_NEED_RTS | IWN_TX_FULL_TXOP; } else if ((ic->ic_flags & IEEE80211_F_USEPROT) && IWN_RATE_IS_OFDM(rate)) { @@ -1780,11 +1975,11 @@ if (subtype == IEEE80211_FC0_SUBTYPE_ASSOC_REQ || subtype == IEEE80211_FC0_SUBTYPE_REASSOC_REQ) - tx->timeout = htole16(3); + timeout = htole16(3); else - tx->timeout = htole16(2); + timeout = htole16(2); } else - tx->timeout = htole16(0); + timeout = htole16(0); if (hdrlen & 3) { /* first segment's length must be a multiple of 4 */ @@ -1793,12 +1988,30 @@ } else pad = 0; + desc = &ring->desc[ring->cur]; + data = &ring->data[ring->cur]; + + cmd = &ring->cmd[ring->cur]; + cmd->code = IWN_CMD_TX_DATA; + cmd->flags = 0; + cmd->qid = ring->qid; + cmd->idx = ring->cur; + + tx = (struct iwn_cmd_data *)cmd->data; + /* NB: no need to bzero tx, all fields are reinitialized here */ + tx->id = id; tx->flags = htole32(flags); tx->len = htole16(m0->m_pkthdr.len); tx->rate = iwn_plcp_signal(rate); - tx->rts_ntries = 60; - tx->data_ntries = 15; + tx->rts_ntries = 60; /* XXX? */ + tx->data_ntries = 15; /* XXX? */ tx->lifetime = htole32(IWN_LIFETIME_INFINITE); + tx->timeout = timeout; + + if (k != NULL) { + /* XXX fill in */; + } else + tx->security = 0; /* XXX alternate between Ant A and Ant B ? */ tx->rflags = IWN_RFLAG_ANT_B; @@ -1816,42 +2029,28 @@ memcpy((uint8_t *)(tx + 1), wh, hdrlen); m_adj(m0, hdrlen); - error = bus_dmamap_load_mbuf(sc->sc_dmat, data->map, m0, - BUS_DMA_NOWAIT); - if (error != 0 && error != EFBIG) { - printf("%s: could not map mbuf (error %d)\n", - sc->sc_dev.dv_xname, error); - m_freem(m0); - return error; - } + error = bus_dmamap_load_mbuf_sg(ring->data_dmat, data->map, m0, segs, + &nsegs, BUS_DMA_NOWAIT); if (error != 0) { - /* too many fragments, linearize */ - - MGETHDR(mnew, M_DONTWAIT, MT_DATA); - if (mnew == NULL) { - m_freem(m0); - return ENOMEM; - } - M_DUP_PKTHDR(mnew, m0); - if (m0->m_pkthdr.len > MHLEN) { - MCLGET(mnew, M_DONTWAIT); - if (!(mnew->m_flags & M_EXT)) { + if (error == EFBIG) { + /* too many fragments, linearize */ + mnew = m_collapse(m0, M_DONTWAIT, IWN_MAX_SCATTER); + if (mnew == NULL) { + IWN_UNLOCK(sc); + device_printf(sc->sc_dev, + "%s: could not defrag mbuf\n", __func__); m_freem(m0); - m_freem(mnew); - return ENOMEM; + return ENOBUFS; } + m0 = mnew; + error = bus_dmamap_load_mbuf_sg(ring->data_dmat, + data->map, m0, segs, &nsegs, BUS_DMA_NOWAIT); } - - m_copydata(m0, 0, m0->m_pkthdr.len, mtod(mnew, caddr_t)); - m_freem(m0); - mnew->m_len = mnew->m_pkthdr.len; - m0 = mnew; - - error = bus_dmamap_load_mbuf(sc->sc_dmat, data->map, m0, - BUS_DMA_NOWAIT); if (error != 0) { - printf("%s: could not map mbuf (error %d)\n", - sc->sc_dev.dv_xname, error); + IWN_UNLOCK(sc); + device_printf(sc->sc_dev, + "%s: bus_dmamap_load_mbuf_sg failed, error %d\n", + __func__, error); m_freem(m0); return error; } @@ -1860,8 +2059,8 @@ data->m = m0; data->ni = ni; - DPRINTFN(4, ("sending data: qid=%d idx=%d len=%d nsegs=%d\n", - ring->qid, ring->cur, m0->m_pkthdr.len, data->map->dm_nsegs)); + DPRINTF(sc, IWN_DEBUG_XMIT, "%s: qid %d idx %d len %d nsegs %d\n", + __func__, ring->qid, ring->cur, m0->m_pkthdr.len, nsegs); paddr = ring->cmd_dma.paddr + ring->cur * sizeof (struct iwn_tx_cmd); tx->loaddr = htole32(paddr + 4 + @@ -1869,24 +2068,28 @@ tx->hiaddr = 0; /* limit to 32-bit physical addresses */ /* first scatter/gather segment is used by the tx data command */ - IWN_SET_DESC_NSEGS(desc, 1 + data->map->dm_nsegs); + IWN_SET_DESC_NSEGS(desc, 1 + nsegs); IWN_SET_DESC_SEG(desc, 0, paddr, 4 + sizeof (*tx) + hdrlen + pad); - for (i = 1; i <= data->map->dm_nsegs; i++) { - IWN_SET_DESC_SEG(desc, i, data->map->dm_segs[i - 1].ds_addr, - data->map->dm_segs[i - 1].ds_len); + for (i = 1; i <= nsegs; i++) { + IWN_SET_DESC_SEG(desc, i, segs[i - 1].ds_addr, + segs[i - 1].ds_len); } sc->shared->len[ring->qid][ring->cur] = htole16(hdrlen + m0->m_pkthdr.len + 8); - if (ring->cur < IWN_TX_WINDOW) { + + if (ring->cur < IWN_TX_WINDOW) sc->shared->len[ring->qid][ring->cur + IWN_TX_RING_COUNT] = - htole16(hdrlen + m0->m_pkthdr.len + 8); - } + htole16(hdrlen + m0->m_pkthdr.len + 8); + ring->queued++; /* kick Tx ring */ ring->cur = (ring->cur + 1) % IWN_TX_RING_COUNT; IWN_WRITE(sc, IWN_TX_WIDX, ring->qid << 8 | ring->cur); + ifp->if_opackets++; + sc->sc_tx_timer = 5; + return 0; } @@ -1894,175 +2097,335 @@ iwn_start(struct ifnet *ifp) { struct iwn_softc *sc = ifp->if_softc; - struct ieee80211com *ic = &sc->sc_ic; + + IWN_LOCK(sc); + iwn_start_locked(ifp); + IWN_UNLOCK(sc); +} + +void +iwn_start_locked(struct ifnet *ifp) +{ + struct iwn_softc *sc = ifp->if_softc; struct ieee80211_node *ni; - struct mbuf *m0; + struct iwn_tx_ring *txq; + struct mbuf *m; + int pri; - /* - * net80211 may still try to send management frames even if the - * IFF_RUNNING flag is not set... - */ - if ((ifp->if_flags & (IFF_RUNNING | IFF_OACTIVE)) != IFF_RUNNING) - return; + IWN_LOCK_ASSERT(sc); + + for (;;) { + IFQ_DRV_DEQUEUE(&ifp->if_snd, m); + if (m == NULL) + break; + ni = (struct ieee80211_node *)m->m_pkthdr.rcvif; + pri = M_WME_GETAC(m); + txq = &sc->txq[pri]; + if (txq->queued >= IWN_TX_RING_COUNT - 8) { + /* XXX not right */ + /* ring is nearly full, stop flow */ + ifp->if_drv_flags |= IFF_DRV_OACTIVE; + } + if (iwn_tx_data(sc, m, ni, txq) != 0) { + ifp->if_oerrors++; + ieee80211_free_node(ni); + break; + } + } +} + +static int +iwn_tx_handoff(struct iwn_softc *sc, + struct iwn_tx_ring *ring, + struct iwn_tx_cmd *cmd, + struct iwn_cmd_data *tx, + struct ieee80211_node *ni, + struct mbuf *m0, u_int hdrlen, int pad) +{ + struct ifnet *ifp = sc->sc_ifp; + struct iwn_tx_desc *desc; + struct iwn_tx_data *data; + bus_addr_t paddr; + struct mbuf *mnew; + int error, nsegs, i; + bus_dma_segment_t segs[IWN_MAX_SCATTER]; + + /* copy and trim IEEE802.11 header */ + memcpy((uint8_t *)(tx + 1), mtod(m0, uint8_t *), hdrlen); + m_adj(m0, hdrlen); + + desc = &ring->desc[ring->cur]; + data = &ring->data[ring->cur]; + + error = bus_dmamap_load_mbuf_sg(ring->data_dmat, data->map, m0, segs, + &nsegs, BUS_DMA_NOWAIT); + if (error != 0) { + if (error == EFBIG) { + /* too many fragments, linearize */ + mnew = m_collapse(m0, M_DONTWAIT, IWN_MAX_SCATTER); + if (mnew == NULL) { + IWN_UNLOCK(sc); + device_printf(sc->sc_dev, + "%s: could not defrag mbuf\n", __func__); + m_freem(m0); + return ENOBUFS; + } + m0 = mnew; + error = bus_dmamap_load_mbuf_sg(ring->data_dmat, + data->map, m0, segs, &nsegs, BUS_DMA_NOWAIT); + } + if (error != 0) { + IWN_UNLOCK(sc); + device_printf(sc->sc_dev, + "%s: bus_dmamap_load_mbuf_sg failed, error %d\n", + __func__, error); + m_freem(m0); + return error; + } + } + + data->m = m0; + data->ni = ni; + + DPRINTF(sc, IWN_DEBUG_XMIT, "%s: qid %d idx %d len %d nsegs %d\n", + __func__, ring->qid, ring->cur, m0->m_pkthdr.len, nsegs); + + paddr = ring->cmd_dma.paddr + ring->cur * sizeof (struct iwn_tx_cmd); + tx->loaddr = htole32(paddr + 4 + + offsetof(struct iwn_cmd_data, ntries)); + tx->hiaddr = 0; /* limit to 32-bit physical addresses */ + + /* first scatter/gather segment is used by the tx data command */ + IWN_SET_DESC_NSEGS(desc, 1 + nsegs); + IWN_SET_DESC_SEG(desc, 0, paddr, 4 + sizeof (*tx) + hdrlen + pad); + for (i = 1; i <= nsegs; i++) { + IWN_SET_DESC_SEG(desc, i, segs[i - 1].ds_addr, + segs[i - 1].ds_len); + } + sc->shared->len[ring->qid][ring->cur] = + htole16(hdrlen + m0->m_pkthdr.len + 8); + + if (ring->cur < IWN_TX_WINDOW) + sc->shared->len[ring->qid][ring->cur + IWN_TX_RING_COUNT] = + htole16(hdrlen + m0->m_pkthdr.len + 8); + + ring->queued++; + + /* kick Tx ring */ + ring->cur = (ring->cur + 1) % IWN_TX_RING_COUNT; + IWN_WRITE(sc, IWN_TX_WIDX, ring->qid << 8 | ring->cur); + + ifp->if_opackets++; + sc->sc_tx_timer = 5; + + return 0; +} + +static int +iwn_tx_data_raw(struct iwn_softc *sc, struct mbuf *m0, + struct ieee80211_node *ni, struct iwn_tx_ring *ring, + const struct ieee80211_bpf_params *params) +{ + struct ieee80211vap *vap = ni->ni_vap; + struct iwn_tx_cmd *cmd; + struct iwn_cmd_data *tx; + struct ieee80211_frame *wh; + uint32_t flags; + uint8_t type, subtype; + u_int hdrlen; + int rate, pad; + + IWN_LOCK_ASSERT(sc); + + wh = mtod(m0, 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); + + flags = IWN_TX_AUTO_SEQ; + if ((params->ibp_flags & IEEE80211_BPF_NOACK) == 0) + flags |= IWN_TX_NEED_ACK; + if (params->ibp_flags & IEEE80211_BPF_RTS) + flags |= IWN_TX_NEED_RTS | IWN_TX_FULL_TXOP; + if (params->ibp_flags & IEEE80211_BPF_CTS) + flags |= IWN_TX_NEED_CTS | IWN_TX_FULL_TXOP; + if (type == IEEE80211_FC0_TYPE_MGT && + subtype == IEEE80211_FC0_SUBTYPE_PROBE_RESP) { + /* tell h/w to set timestamp in probe responses */ + flags |= IWN_TX_INSERT_TSTAMP; + } + if (hdrlen & 3) { + /* first segment's length must be a multiple of 4 */ + flags |= IWN_TX_NEED_PADDING; + pad = 4 - (hdrlen & 3); + } else + pad = 0; + + /* pick a tx rate */ + rate = params->ibp_rate0; + + if (ieee80211_radiotap_active_vap(vap)) { + struct iwn_tx_radiotap_header *tap = &sc->sc_txtap; + + tap->wt_flags = 0; + tap->wt_rate = rate; + + ieee80211_radiotap_tx(vap, m0); + } + + cmd = &ring->cmd[ring->cur]; + cmd->code = IWN_CMD_TX_DATA; + cmd->flags = 0; + cmd->qid = ring->qid; + cmd->idx = ring->cur; + + tx = (struct iwn_cmd_data *)cmd->data; + /* NB: no need to bzero tx, all fields are reinitialized here */ + tx->id = IWN_ID_BROADCAST; + tx->flags = htole32(flags); + tx->len = htole16(m0->m_pkthdr.len); + tx->rate = iwn_plcp_signal(rate); + tx->rts_ntries = params->ibp_try1; /* XXX? */ + tx->data_ntries = params->ibp_try0; + tx->lifetime = htole32(IWN_LIFETIME_INFINITE); + /* XXX use try count? */ + if (type == IEEE80211_FC0_TYPE_MGT) { + if (subtype == IEEE80211_FC0_SUBTYPE_ASSOC_REQ || + subtype == IEEE80211_FC0_SUBTYPE_REASSOC_REQ) + tx->timeout = htole16(3); + else + tx->timeout = htole16(2); + } else + tx->timeout = htole16(0); + tx->security = 0; + /* XXX alternate between Ant A and Ant B ? */ + tx->rflags = IWN_RFLAG_ANT_B; /* XXX params->ibp_pri >> 2 */ + tx->ridx = IWN_MAX_TX_RETRIES - 1; + if (!IWN_RATE_IS_OFDM(rate)) + tx->rflags |= IWN_RFLAG_CCK; - for (;;) { - IF_POLL(&ic->ic_mgtq, m0); - if (m0 != NULL) { - /* management frames go into ring 0 */ - if (sc->txq[0].queued >= IWN_TX_RING_COUNT - 8) { - ifp->if_flags |= IFF_OACTIVE; - break; - } - IF_DEQUEUE(&ic->ic_mgtq, m0); + return iwn_tx_handoff(sc, ring, cmd, tx, ni, m0, hdrlen, pad); +} - ni = (struct ieee80211_node *)m0->m_pkthdr.rcvif; - m0->m_pkthdr.rcvif = NULL; -#if NBPFILTER > 0 - if (ic->ic_rawbpf != NULL) - bpf_mtap(ic->ic_rawbpf, m0, BPF_DIRECTION_OUT); -#endif - if (iwn_tx_data(sc, m0, ni, 0) != 0) - break; +static int +iwn_raw_xmit(struct ieee80211_node *ni, struct mbuf *m, + const struct ieee80211_bpf_params *params) +{ + struct ieee80211com *ic = ni->ni_ic; + struct ifnet *ifp = ic->ic_ifp; + struct iwn_softc *sc = ifp->if_softc; + struct iwn_tx_ring *txq; + int error; - } else { - if (ic->ic_state != IEEE80211_S_RUN) - break; - IFQ_POLL(&ifp->if_snd, m0); - if (m0 == NULL) - break; - if (sc->txq[0].queued >= IWN_TX_RING_COUNT - 8) { - /* there is no place left in this ring */ - ifp->if_flags |= IFF_OACTIVE; - break; - } - IFQ_DEQUEUE(&ifp->if_snd, m0); -#if NBPFILTER > 0 - if (ifp->if_bpf != NULL) - bpf_mtap(ifp->if_bpf, m0, BPF_DIRECTION_OUT); -#endif - m0 = ieee80211_encap(ifp, m0, &ni); - if (m0 == NULL) - continue; -#if NBPFILTER > 0 - if (ic->ic_rawbpf != NULL) - bpf_mtap(ic->ic_rawbpf, m0, BPF_DIRECTION_OUT); -#endif - if (iwn_tx_data(sc, m0, ni, 0) != 0) { - if (ni != NULL) - ieee80211_release_node(ic, ni); - ifp->if_oerrors++; - break; - } - } + if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) { + ieee80211_free_node(ni); + m_freem(m); + return ENETDOWN; + } - sc->sc_tx_timer = 5; - ifp->if_timer = 1; + IWN_LOCK(sc); + if (params == NULL) + txq = &sc->txq[M_WME_GETAC(m)]; + else + txq = &sc->txq[params->ibp_pri & 3]; + if (txq->queued >= IWN_TX_RING_COUNT - 8) { + /* XXX not right */ + /* ring is nearly full, stop flow */ + ifp->if_drv_flags |= IFF_DRV_OACTIVE; + } + if (params == NULL) { + /* + * Legacy path; interpret frame contents to decide + * precisely how to send the frame. + */ + error = iwn_tx_data(sc, m, ni, txq); + } else { + /* + * Caller supplied explicit parameters to use in + * sending the frame. + */ + error = iwn_tx_data_raw(sc, m, ni, txq, params); + } + if (error != 0) { + /* NB: m is reclaimed on tx failure */ + ieee80211_free_node(ni); + ifp->if_oerrors++; } + IWN_UNLOCK(sc); + return error; } -void -iwn_watchdog(struct ifnet *ifp) +static void +iwn_watchdog(struct iwn_softc *sc) { - struct iwn_softc *sc = ifp->if_softc; + if (sc->sc_tx_timer > 0 && --sc->sc_tx_timer == 0) { + struct ifnet *ifp = sc->sc_ifp; + struct ieee80211com *ic = ifp->if_l2com; - ifp->if_timer = 0; - - if (sc->sc_tx_timer > 0) { - if (--sc->sc_tx_timer == 0) { - printf("%s: device timeout\n", sc->sc_dev.dv_xname); - ifp->if_flags &= ~IFF_UP; - iwn_stop(ifp, 1); - ifp->if_oerrors++; - return; - } - ifp->if_timer = 1; + if_printf(ifp, "device timeout\n"); + ieee80211_runtask(ic, &sc->sc_reinit_task); } - - ieee80211_watchdog(ifp); } int iwn_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) { struct iwn_softc *sc = ifp->if_softc; - struct ieee80211com *ic = &sc->sc_ic; - struct ifaddr *ifa; - struct ifreq *ifr; - int s, error = 0; - - s = splnet(); + struct ieee80211com *ic = ifp->if_l2com; + struct ifreq *ifr = (struct ifreq *) data; + int error = 0, startall = 0; switch (cmd) { - case SIOCSIFADDR: - ifa = (struct ifaddr *)data; - ifp->if_flags |= IFF_UP; -#ifdef INET - if (ifa->ifa_addr->sa_family == AF_INET) - arp_ifinit(&ic->ic_ac, ifa); -#endif - /* FALLTHROUGH */ case SIOCSIFFLAGS: + IWN_LOCK(sc); if (ifp->if_flags & IFF_UP) { - if (!(ifp->if_flags & IFF_RUNNING)) - iwn_init(ifp); + if (!(ifp->if_drv_flags & IFF_DRV_RUNNING)) { + iwn_init_locked(sc); + startall = 1; + } } else { - if (ifp->if_flags & IFF_RUNNING) - iwn_stop(ifp, 1); + if (ifp->if_drv_flags & IFF_DRV_RUNNING) + iwn_stop_locked(sc); } + IWN_UNLOCK(sc); + if (startall) + ieee80211_start_all(ic); break; - - case SIOCADDMULTI: - case SIOCDELMULTI: - ifr = (struct ifreq *)data; - error = (cmd == SIOCADDMULTI) ? - ether_addmulti(ifr, &ic->ic_ac) : - ether_delmulti(ifr, &ic->ic_ac); - - if (error == ENETRESET) - error = 0; + case SIOCGIFMEDIA: + error = ifmedia_ioctl(ifp, ifr, &ic->ic_media, cmd); + break; + case SIOCGIFADDR: + error = ether_ioctl(ifp, cmd, data); break; - default: - error = ieee80211_ioctl(ifp, cmd, data); - } - - if (error == ENETRESET) { - if ((ifp->if_flags & (IFF_UP | IFF_RUNNING)) == - (IFF_UP | IFF_RUNNING)) { - iwn_stop(ifp, 0); - iwn_init(ifp); - } - error = 0; + error = EINVAL; + break; } - - splx(s); return error; } void -iwn_read_eeprom(struct iwn_softc *sc) +iwn_read_eeprom(struct iwn_softc *sc, uint8_t macaddr[IEEE80211_ADDR_LEN]) { - struct ieee80211com *ic = &sc->sc_ic; char domain[4]; uint16_t val; int i, error; if ((error = iwn_eeprom_lock(sc)) != 0) { - printf("%s: could not lock EEPROM (error=%d)\n", - sc->sc_dev.dv_xname, error); + device_printf(sc->sc_dev, + "%s: could not lock EEPROM, error %d\n", __func__, error); return; } /* read and print regulatory domain */ iwn_read_prom_data(sc, IWN_EEPROM_DOMAIN, domain, 4); - printf(", %.4s", domain); + device_printf(sc->sc_dev,"Reg Domain: %.4s", domain); /* read and print MAC address */ - iwn_read_prom_data(sc, IWN_EEPROM_MAC, ic->ic_myaddr, 6); - printf(", address %s\n", ether_sprintf(ic->ic_myaddr)); + iwn_read_prom_data(sc, IWN_EEPROM_MAC, macaddr, 6); + printf(", address %6D\n", macaddr, ":"); /* read the list of authorized channels */ - for (i = 0; i < IWN_CHAN_BANDS_COUNT; i++) - iwn_read_eeprom_channels(sc, i); + iwn_read_eeprom_channels(sc); /* read maximum allowed Tx power for 2GHz and 5GHz bands */ iwn_read_prom_data(sc, IWN_EEPROM_MAXPOW, &val, 2); @@ -2073,17 +2436,19 @@ sc->maxpwr5GHz = 38; if (sc->maxpwr2GHz < 20 || sc->maxpwr2GHz > 50) sc->maxpwr2GHz = 38; - DPRINTF(("maxpwr 2GHz=%d 5GHz=%d\n", sc->maxpwr2GHz, sc->maxpwr5GHz)); + DPRINTF(sc, IWN_DEBUG_RESET, "maxpwr 2GHz=%d 5GHz=%d\n", + sc->maxpwr2GHz, sc->maxpwr5GHz); /* read voltage at which samples were taken */ iwn_read_prom_data(sc, IWN_EEPROM_VOLTAGE, &val, 2); - sc->eeprom_voltage = (int16_t)letoh16(val); - DPRINTF(("voltage=%d (in 0.3V)\n", sc->eeprom_voltage)); + sc->eeprom_voltage = (int16_t)le16toh(val); + DPRINTF(sc, IWN_DEBUG_RESET, "voltage=%d (in 0.3V)\n", + sc->eeprom_voltage); /* read power groups */ iwn_read_prom_data(sc, IWN_EEPROM_BANDS, sc->bands, sizeof sc->bands); #ifdef IWN_DEBUG - if (iwn_debug > 0) { + if (sc->sc_debug & IWN_DEBUG_ANY) { for (i = 0; i < IWN_NBANDS; i++) iwn_print_power_group(sc, i); } @@ -2091,59 +2456,168 @@ iwn_eeprom_unlock(sc); } -void -iwn_read_eeprom_channels(struct iwn_softc *sc, int n) +struct iwn_chan_band { + uint32_t addr; /* offset in EEPROM */ + uint32_t flags; /* net80211 flags */ + uint8_t nchan; +#define IWN_MAX_CHAN_PER_BAND 14 + uint8_t chan[IWN_MAX_CHAN_PER_BAND]; +}; + +static void +iwn_read_eeprom_band(struct iwn_softc *sc, const struct iwn_chan_band *band) { - struct ieee80211com *ic = &sc->sc_ic; - const struct iwn_chan_band *band = &iwn_bands[n]; + struct ifnet *ifp = sc->sc_ifp; + struct ieee80211com *ic = ifp->if_l2com; struct iwn_eeprom_chan channels[IWN_MAX_CHAN_PER_BAND]; - int chan, i; + struct ieee80211_channel *c; + int i, chan, flags; iwn_read_prom_data(sc, band->addr, channels, band->nchan * sizeof (struct iwn_eeprom_chan)); for (i = 0; i < band->nchan; i++) { - if (!(channels[i].flags & IWN_EEPROM_CHAN_VALID)) + if (!(channels[i].flags & IWN_EEPROM_CHAN_VALID)) { + DPRINTF(sc, IWN_DEBUG_RESET, + "skip chan %d flags 0x%x maxpwr %d\n", + band->chan[i], channels[i].flags, + channels[i].maxpwr); continue; - + } chan = band->chan[i]; - if (n == 0) { /* 2GHz band */ - ic->ic_channels[chan].ic_freq = - ieee80211_ieee2mhz(chan, IEEE80211_CHAN_2GHZ); - ic->ic_channels[chan].ic_flags = - IEEE80211_CHAN_CCK | IEEE80211_CHAN_OFDM | - IEEE80211_CHAN_DYN | IEEE80211_CHAN_2GHZ; - + /* translate EEPROM flags to net80211 */ + flags = 0; + if ((channels[i].flags & IWN_EEPROM_CHAN_ACTIVE) == 0) + flags |= IEEE80211_CHAN_PASSIVE; + if ((channels[i].flags & IWN_EEPROM_CHAN_IBSS) == 0) + flags |= IEEE80211_CHAN_NOADHOC; + if (channels[i].flags & IWN_EEPROM_CHAN_RADAR) { + flags |= IEEE80211_CHAN_DFS; + /* XXX apparently IBSS may still be marked */ + flags |= IEEE80211_CHAN_NOADHOC; + } + + DPRINTF(sc, IWN_DEBUG_RESET, + "add chan %d flags 0x%x maxpwr %d\n", + chan, channels[i].flags, channels[i].maxpwr); + + c = &ic->ic_channels[ic->ic_nchans++]; + c->ic_ieee = chan; + c->ic_freq = ieee80211_ieee2mhz(chan, band->flags); + c->ic_maxregpower = channels[i].maxpwr; + c->ic_maxpower = 2*c->ic_maxregpower; + if (band->flags & IEEE80211_CHAN_2GHZ) { + /* G =>'s B is supported */ + c->ic_flags = IEEE80211_CHAN_B | flags; + + c = &ic->ic_channels[ic->ic_nchans++]; + c[0] = c[-1]; + c->ic_flags = IEEE80211_CHAN_G | flags; } else { /* 5GHz band */ - /* - * Some adapters support channels 7, 8, 11 and 12 - * both in the 2GHz *and* 5GHz bands. - * Because of limitations in our net80211(9) stack, - * we can't support these channels in 5GHz band. - */ - if (chan <= 14) - continue; - - ic->ic_channels[chan].ic_freq = - ieee80211_ieee2mhz(chan, IEEE80211_CHAN_5GHZ); - ic->ic_channels[chan].ic_flags = IEEE80211_CHAN_A; + c->ic_flags = IEEE80211_CHAN_A | flags; } + /* XXX no constraints on using HT20 */ + /* add HT20, HT40 added separately */ + c = &ic->ic_channels[ic->ic_nchans++]; + c[0] = c[-1]; + c->ic_flags |= IEEE80211_CHAN_HT20; + /* XXX NARROW =>'s 1/2 and 1/4 width? */ + } +} - /* is active scan allowed on this channel? */ - if (!(channels[i].flags & IWN_EEPROM_CHAN_ACTIVE)) { - ic->ic_channels[chan].ic_flags |= - IEEE80211_CHAN_PASSIVE; - } +static void +iwn_read_eeprom_ht40(struct iwn_softc *sc, const struct iwn_chan_band *band) +{ + struct ifnet *ifp = sc->sc_ifp; + struct ieee80211com *ic = ifp->if_l2com; + struct iwn_eeprom_chan channels[IWN_MAX_CHAN_PER_BAND]; + struct ieee80211_channel *c, *cent, *extc; + int i; + + iwn_read_prom_data(sc, band->addr, channels, + band->nchan * sizeof (struct iwn_eeprom_chan)); - /* save maximum allowed power for this channel */ - sc->maxpwr[chan] = channels[i].maxpwr; + for (i = 0; i < band->nchan; i++) { + if (!(channels[i].flags & IWN_EEPROM_CHAN_VALID) || + !(channels[i].flags & IWN_EEPROM_CHAN_WIDE)) { + DPRINTF(sc, IWN_DEBUG_RESET, + "skip chan %d flags 0x%x maxpwr %d\n", + band->chan[i], channels[i].flags, + channels[i].maxpwr); + continue; + } + /* + * Each entry defines an HT40 channel pair; find the + * center channel, then the extension channel above. + */ + cent = ieee80211_find_channel_byieee(ic, band->chan[i], + band->flags & ~IEEE80211_CHAN_HT); + if (cent == NULL) { /* XXX shouldn't happen */ + device_printf(sc->sc_dev, + "%s: no entry for channel %d\n", + __func__, band->chan[i]); + continue; + } + extc = ieee80211_find_channel(ic, cent->ic_freq+20, + band->flags & ~IEEE80211_CHAN_HT); + if (extc == NULL) { + DPRINTF(sc, IWN_DEBUG_RESET, + "skip chan %d, extension channel not found\n", + band->chan[i]); + continue; + } - DPRINTF(("adding chan %d flags=0x%x maxpwr=%d\n", - chan, channels[i].flags, sc->maxpwr[chan])); + DPRINTF(sc, IWN_DEBUG_RESET, + "add ht40 chan %d flags 0x%x maxpwr %d\n", + band->chan[i], channels[i].flags, channels[i].maxpwr); + + c = &ic->ic_channels[ic->ic_nchans++]; + c[0] = cent[0]; + c->ic_extieee = extc->ic_ieee; + c->ic_flags &= ~IEEE80211_CHAN_HT; + c->ic_flags |= IEEE80211_CHAN_HT40U; + c = &ic->ic_channels[ic->ic_nchans++]; + c[0] = extc[0]; + c->ic_extieee = cent->ic_ieee; + c->ic_flags &= ~IEEE80211_CHAN_HT; + c->ic_flags |= IEEE80211_CHAN_HT40D; } } +static void +iwn_read_eeprom_channels(struct iwn_softc *sc) +{ +#define N(a) (sizeof(a)/sizeof(a[0])) + static const struct iwn_chan_band iwn_bands[] = { + { IWN_EEPROM_BAND1, IEEE80211_CHAN_G, 14, + { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14 } }, + { IWN_EEPROM_BAND2, IEEE80211_CHAN_A, 13, + { 183, 184, 185, 187, 188, 189, 192, 196, 7, 8, 11, 12, 16 } }, + { IWN_EEPROM_BAND3, IEEE80211_CHAN_A, 12, + { 34, 36, 38, 40, 42, 44, 46, 48, 52, 56, 60, 64 } }, + { IWN_EEPROM_BAND4, IEEE80211_CHAN_A, 11, + { 100, 104, 108, 112, 116, 120, 124, 128, 132, 136, 140 } }, + { IWN_EEPROM_BAND5, IEEE80211_CHAN_A, 6, + { 145, 149, 153, 157, 161, 165 } }, + { IWN_EEPROM_BAND6, IEEE80211_CHAN_G | IEEE80211_CHAN_HT40, 7, + { 1, 2, 3, 4, 5, 6, 7 } }, + { IWN_EEPROM_BAND7, IEEE80211_CHAN_A | IEEE80211_CHAN_HT40, 11, + { 36, 44, 52, 60, 100, 108, 116, 124, 132, 149, 157 } } + }; + struct ifnet *ifp = sc->sc_ifp; + struct ieee80211com *ic = ifp->if_l2com; + int i; + + /* read the list of authorized channels */ + for (i = 0; i < N(iwn_bands)-2; i++) + iwn_read_eeprom_band(sc, &iwn_bands[i]); + for (; i < N(iwn_bands); i++) + iwn_read_eeprom_ht40(sc, &iwn_bands[i]); + ieee80211_sort_channels(ic->ic_channels, ic->ic_nchans); +#undef N +} + #ifdef IWN_DEBUG void iwn_print_power_group(struct iwn_softc *sc, int i) @@ -2190,7 +2664,9 @@ struct iwn_tx_cmd *cmd; bus_addr_t paddr; - KASSERT(size <= sizeof cmd->data); + IWN_LOCK_ASSERT(sc); + + KASSERT(size <= sizeof cmd->data, ("Command too big")); desc = &ring->desc[ring->cur]; cmd = &ring->cmd[ring->cur]; @@ -2207,50 +2683,106 @@ IWN_SET_DESC_SEG(desc, 0, paddr, 4 + size); sc->shared->len[ring->qid][ring->cur] = htole16(8); if (ring->cur < IWN_TX_WINDOW) { - sc->shared->len[ring->qid][ring->cur + IWN_TX_RING_COUNT] = - htole16(8); + sc->shared->len[ring->qid][ring->cur + IWN_TX_RING_COUNT] = + htole16(8); } + DPRINTF(sc, IWN_DEBUG_CMD, "%s: %s (0x%x) flags %d qid %d idx %d\n", + __func__, iwn_intr_str(cmd->code), cmd->code, + cmd->flags, cmd->qid, cmd->idx); + /* kick cmd ring */ ring->cur = (ring->cur + 1) % IWN_TX_RING_COUNT; IWN_WRITE(sc, IWN_TX_WIDX, ring->qid << 8 | ring->cur); - return async ? 0 : tsleep(cmd, PCATCH, "iwncmd", hz); + return async ? 0 : msleep(cmd, &sc->sc_mtx, PCATCH, "iwncmd", hz); } +static const uint8_t iwn_ridx_to_plcp[] = { + 10, 20, 55, 110, /* CCK */ + 0xd, 0xf, 0x5, 0x7, 0x9, 0xb, 0x1, 0x3, 0x3 /* OFDM R1-R4 */ +}; +static const uint8_t iwn_siso_mcs_to_plcp[] = { + 0, 0, 0, 0, /* CCK */ + 0, 0, 1, 2, 3, 4, 5, 6, 7 /* HT */ +}; +static const uint8_t iwn_mimo_mcs_to_plcp[] = { + 0, 0, 0, 0, /* CCK */ + 8, 8, 9, 10, 11, 12, 13, 14, 15 /* HT */ +}; +static const uint8_t iwn_prev_ridx[] = { + /* NB: allow fallback from CCK11 to OFDM9 and from OFDM6 to CCK5 */ + 0, 0, 1, 5, /* CCK */ + 2, 4, 3, 6, 7, 8, 9, 10, 10 /* OFDM */ +}; + /* - * Configure hardware multi-rate retries for one node. + * Configure hardware link parameters for the specified + * node operating on the specified channel. */ int -iwn_setup_node_mrr(struct iwn_softc *sc, uint8_t id, int async) +iwn_set_link_quality(struct iwn_softc *sc, uint8_t id, + const struct ieee80211_channel *c, int async) { - struct ieee80211com *ic = &sc->sc_ic; - struct iwn_cmd_mrr mrr; + struct iwn_cmd_link_quality lq; int i, ridx; - memset(&mrr, 0, sizeof mrr); - mrr.id = id; - mrr.ssmask = 2; - mrr.dsmask = 3; - mrr.ampdu_disable = 3; - mrr.ampdu_limit = 4000; + memset(&lq, 0, sizeof(lq)); + lq.id = id; + if (IEEE80211_IS_CHAN_HT(c)) { + lq.mimo = 1; + lq.ssmask = 0x1; + } else + lq.ssmask = 0x2; if (id == IWN_ID_BSS) - ridx = IWN_OFDM54; - else if (ic->ic_curmode == IEEE80211_MODE_11A) - ridx = IWN_OFDM6; + ridx = IWN_RATE_OFDM54; + else if (IEEE80211_IS_CHAN_A(c)) + ridx = IWN_RATE_OFDM6; else - ridx = IWN_CCK1; + ridx = IWN_RATE_CCK1; for (i = 0; i < IWN_MAX_TX_RETRIES; i++) { - mrr.table[i].rate = iwn_ridx_to_plcp[ridx]; - mrr.table[i].rflags = IWN_RFLAG_ANT_B; - if (ridx <= IWN_CCK11) - mrr.table[i].rflags |= IWN_RFLAG_CCK; + /* XXX toggle antenna for retry patterns */ + if (IEEE80211_IS_CHAN_HT40(c)) { + lq.table[i].rate = iwn_mimo_mcs_to_plcp[ridx] + | IWN_RATE_MCS; + lq.table[i].rflags = IWN_RFLAG_HT + | IWN_RFLAG_HT40 + | IWN_RFLAG_ANT_A; + /* XXX shortGI */ + } else if (IEEE80211_IS_CHAN_HT(c)) { + lq.table[i].rate = iwn_siso_mcs_to_plcp[ridx] + | IWN_RATE_MCS; + lq.table[i].rflags = IWN_RFLAG_HT + | IWN_RFLAG_ANT_A; + /* XXX shortGI */ + } else { + lq.table[i].rate = iwn_ridx_to_plcp[ridx]; + if (ridx <= IWN_RATE_CCK11) + lq.table[i].rflags = IWN_RFLAG_CCK; + lq.table[i].rflags |= IWN_RFLAG_ANT_B; + } ridx = iwn_prev_ridx[ridx]; } - return iwn_cmd(sc, IWN_CMD_NODE_MRR_SETUP, &mrr, sizeof mrr, async); + + lq.dsmask = 0x3; + lq.ampdu_disable = 3; + lq.ampdu_limit = htole16(4000); +#ifdef IWN_DEBUG + if (sc->sc_debug & IWN_DEBUG_STATE) { + printf("%s: set link quality for node %d, mimo %d ssmask %d\n", + __func__, id, lq.mimo, lq.ssmask); + printf("%s:", __func__); + for (i = 0; i < IWN_MAX_TX_RETRIES; i++) + printf(" %d:%x", lq.table[i].rate, lq.table[i].rflags); + printf("\n"); + } +#endif + return iwn_cmd(sc, IWN_CMD_TX_LINK_QUALITY, &lq, sizeof(lq), async); } +#if 0 + /* * Install a pairwise key into the hardware. */ @@ -2282,27 +2814,33 @@ return iwn_cmd(sc, IWN_CMD_ADD_NODE, &node, sizeof node, 1); } +#endif -void -iwn_updateedca(struct ieee80211com *ic) +int +iwn_wme_update(struct ieee80211com *ic) { #define IWN_EXP2(x) ((1 << (x)) - 1) /* CWmin = 2^ECWmin - 1 */ - struct iwn_softc *sc = ic->ic_softc; +#define IWN_TXOP_TO_US(v) (v<<5) + struct iwn_softc *sc = ic->ic_ifp->if_softc; struct iwn_edca_params cmd; - int aci; + int i; memset(&cmd, 0, sizeof cmd); cmd.flags = htole32(IWN_EDCA_UPDATE); - for (aci = 0; aci < EDCA_NUM_AC; aci++) { - const struct ieee80211_edca_ac_params *ac = - &ic->ic_edca_ac[aci]; - cmd.ac[aci].aifsn = ac->ac_aifsn; - cmd.ac[aci].cwmin = htole16(IWN_EXP2(ac->ac_ecwmin)); - cmd.ac[aci].cwmax = htole16(IWN_EXP2(ac->ac_ecwmax)); - cmd.ac[aci].txoplimit = - htole16(IEEE80211_TXOP_TO_US(ac->ac_txoplimit)); - } - (void)iwn_cmd(sc, IWN_CMD_EDCA_PARAMS, &cmd, sizeof cmd, 1); + for (i = 0; i < WME_NUM_AC; i++) { + const struct wmeParams *wmep = + &ic->ic_wme.wme_chanParams.cap_wmeParams[i]; + cmd.ac[i].aifsn = wmep->wmep_aifsn; + cmd.ac[i].cwmin = htole16(IWN_EXP2(wmep->wmep_logcwmin)); + cmd.ac[i].cwmax = htole16(IWN_EXP2(wmep->wmep_logcwmax)); + cmd.ac[i].txoplimit = + htole16(IWN_TXOP_TO_US(wmep->wmep_txopLimit)); + } + IWN_LOCK(sc); + (void) iwn_cmd(sc, IWN_CMD_EDCA_PARAMS, &cmd, sizeof cmd, 1 /*async*/); + IWN_UNLOCK(sc); + return 0; +#undef IWN_TXOP_TO_US #undef IWN_EXP2 } @@ -2316,7 +2854,7 @@ led.off = off; led.on = on; - (void)iwn_cmd(sc, IWN_CMD_SET_LED, &led, sizeof led, 1); + (void) iwn_cmd(sc, IWN_CMD_SET_LED, &led, sizeof led, 1); } /* @@ -2330,9 +2868,9 @@ struct iwn_critical_temp crit; uint32_t r1, r2, r3, temp; - r1 = letoh32(uc->temp[0].chan20MHz); - r2 = letoh32(uc->temp[1].chan20MHz); - r3 = letoh32(uc->temp[2].chan20MHz); + r1 = le32toh(uc->temp[0].chan20MHz); + r2 = le32toh(uc->temp[1].chan20MHz); + r3 = le32toh(uc->temp[2].chan20MHz); /* inverse function of iwn_get_temperature() */ temp = r2 + (IWN_CTOK(110) * (r3 - r1)) / 259; @@ -2340,7 +2878,7 @@ memset(&crit, 0, sizeof crit); crit.tempR = htole32(temp); - DPRINTF(("setting critical temperature to %u\n", temp)); + DPRINTF(sc, IWN_DEBUG_RESET, "setting critical temp to %u\n", temp); return iwn_cmd(sc, IWN_CMD_SET_CRITICAL_TEMP, &crit, sizeof crit, 0); } @@ -2351,28 +2889,38 @@ uint64_t val, mod; memset(&tsf, 0, sizeof tsf); - memcpy(&tsf.tstamp, ni->ni_tstamp, sizeof (uint64_t)); + memcpy(&tsf.tstamp, ni->ni_tstamp.data, sizeof (uint64_t)); tsf.bintval = htole16(ni->ni_intval); tsf.lintval = htole16(10); + /* XXX all wrong */ /* compute remaining time until next beacon */ val = (uint64_t)ni->ni_intval * 1024; /* msecs -> usecs */ - mod = letoh64(tsf.tstamp) % val; + DPRINTF(sc, IWN_DEBUG_ANY, "%s: val = %ju %s\n", __func__, + val, val == 0 ? "correcting" : ""); + if (val == 0) + val = 1; + mod = le64toh(tsf.tstamp) % val; tsf.binitval = htole32((uint32_t)(val - mod)); - DPRINTF(("TSF bintval=%u tstamp=%llu, init=%u\n", - ni->ni_intval, letoh64(tsf.tstamp), (uint32_t)(val - mod))); + DPRINTF(sc, IWN_DEBUG_RESET, "TSF bintval=%u tstamp=%ju, init=%u\n", + ni->ni_intval, le64toh(tsf.tstamp), (uint32_t)(val - mod)); if (iwn_cmd(sc, IWN_CMD_TSF, &tsf, sizeof tsf, 1) != 0) - printf("%s: could not enable TSF\n", sc->sc_dev.dv_xname); + device_printf(sc->sc_dev, + "%s: could not enable TSF\n", __func__); } void iwn_power_calibration(struct iwn_softc *sc, int temp) { - struct ieee80211com *ic = &sc->sc_ic; - - DPRINTF(("temperature %d->%d\n", sc->temp, temp)); + struct ifnet *ifp = sc->sc_ifp; + struct ieee80211com *ic = ifp->if_l2com; +#if 0 + KASSERT(ic->ic_state == IEEE80211_S_RUN, ("not running")); +#endif + DPRINTF(sc, IWN_DEBUG_CALIBRATE, "%s: temperature %d->%d\n", + __func__, sc->temp, temp); /* adjust Tx power if need be (delta >= 3°C) */ if (abs(temp - sc->temp) < 3) @@ -2380,11 +2928,12 @@ sc->temp = temp; - DPRINTF(("setting Tx power for channel %d\n", - ieee80211_chan2ieee(ic, ic->ic_bss->ni_chan))); - if (iwn_set_txpower(sc, ic->ic_bss->ni_chan, 1) != 0) { + DPRINTF(sc, IWN_DEBUG_CALIBRATE, "%s: set Tx power for channel %d\n", + __func__, ieee80211_chan2ieee(ic, ic->ic_bsschan)); + if (iwn_set_txpower(sc, ic->ic_bsschan, 1) != 0) { /* just warn, too bad for the automatic calibration... */ - printf("%s: could not adjust Tx power\n", sc->sc_dev.dv_xname); + device_printf(sc->sc_dev, + "%s: could not adjust Tx power\n", __func__); } } @@ -2404,7 +2953,8 @@ ((y1) + fdivround(((int)(x) - (x1)) * ((y2) - (y1)), (x2) - (x1), n)) static const int tdiv[IWN_NATTEN_GROUPS] = { 9, 8, 8, 8, 6 }; - struct ieee80211com *ic = &sc->sc_ic; + struct ifnet *ifp = sc->sc_ifp; + struct ieee80211com *ic = ifp->if_l2com; struct iwn_ucode_info *uc = &sc->ucode_info; struct iwn_cmd_txpower cmd; struct iwn_eeprom_chan_samples *chans; @@ -2431,13 +2981,14 @@ } /* compute voltage compensation */ - vdiff = ((int32_t)letoh32(uc->volt) - sc->eeprom_voltage) / 7; + vdiff = ((int32_t)le32toh(uc->volt) - sc->eeprom_voltage) / 7; if (vdiff > 0) vdiff *= 2; if (abs(vdiff) > 2) vdiff = 0; - DPRINTF(("voltage compensation=%d (UCODE=%d, EEPROM=%d)\n", - vdiff, letoh32(uc->volt), sc->eeprom_voltage)); + DPRINTF(sc, IWN_DEBUG_CALIBRATE | IWN_DEBUG_TXPOW, + "%s: voltage compensation=%d (UCODE=%d, EEPROM=%d)\n", + __func__, vdiff, le32toh(uc->volt), sc->eeprom_voltage); /* get channel's attenuation group */ if (chan <= 20) /* 1-20 */ @@ -2450,7 +3001,8 @@ grp = 2; else /* 125-200 */ grp = 3; - DPRINTF(("chan %d, attenuation group=%d\n", chan, grp)); + DPRINTF(sc, IWN_DEBUG_CALIBRATE | IWN_DEBUG_TXPOW, + "%s: chan %d, attenuation group=%d\n", __func__, chan, grp); /* get channel's sub-band */ for (i = 0; i < IWN_NBANDS; i++) @@ -2458,7 +3010,8 @@ sc->bands[i].lo <= chan && chan <= sc->bands[i].hi) break; chans = sc->bands[i].chans; - DPRINTF(("chan %d sub-band=%d\n", chan, i)); + DPRINTF(sc, IWN_DEBUG_CALIBRATE | IWN_DEBUG_TXPOW, + "%s: chan %d sub-band=%d\n", __func__, chan, i); for (c = 0; c < IWN_NTXCHAINS; c++) { uint8_t power, gain, temp; @@ -2473,16 +3026,18 @@ temp = interpolate(chan, chans[0].num, chans[0].samples[c][1].temp, chans[1].num, chans[1].samples[c][1].temp, 1); - DPRINTF(("Tx chain %d: power=%d gain=%d temp=%d\n", - c, power, gain, temp)); + DPRINTF(sc, IWN_DEBUG_CALIBRATE | IWN_DEBUG_TXPOW, + "%s: Tx chain %d: power=%d gain=%d temp=%d\n", + __func__, c, power, gain, temp); /* compute temperature compensation */ tdiff = ((sc->temp - temp) * 2) / tdiv[grp]; - DPRINTF(("temperature compensation=%d (current=%d, " - "EEPROM=%d)\n", tdiff, sc->temp, temp)); + DPRINTF(sc, IWN_DEBUG_CALIBRATE | IWN_DEBUG_TXPOW, + "%s: temperature compensation=%d (current=%d, EEPROM=%d)\n", + __func__, tdiff, sc->temp, temp); for (ridx = 0; ridx <= IWN_RIDX_MAX; ridx++) { - maxchpwr = sc->maxpwr[chan] * 2; + maxchpwr = ch->ic_maxpower; if ((ridx / 8) & 1) { /* MIMO: decrease Tx power (-3dB) */ maxchpwr -= 6; @@ -2503,7 +3058,7 @@ idx = gain - (pwr - power) - tdiff - vdiff; if ((ridx / 8) & 1) /* MIMO */ - idx += (int32_t)letoh32(uc->atten[grp][c]); + idx += (int32_t)le32toh(uc->atten[grp][c]); if (cmd.band == 0) idx += 9; /* 5GHz */ @@ -2516,14 +3071,16 @@ else if (idx > IWN_MAX_PWR_INDEX) idx = IWN_MAX_PWR_INDEX; - DPRINTF(("Tx chain %d, rate idx %d: power=%d\n", - c, ridx, idx)); + DPRINTF(sc, IWN_DEBUG_CALIBRATE | IWN_DEBUG_TXPOW, + "%s: Tx chain %d, rate idx %d: power=%d\n", + __func__, c, ridx, idx); cmd.power[ridx].rf_gain[c] = rf_gain[idx]; cmd.power[ridx].dsp_gain[c] = dsp_gain[idx]; } } - DPRINTF(("setting tx power for chan %d\n", chan)); + DPRINTF(sc, IWN_DEBUG_CALIBRATE | IWN_DEBUG_TXPOW, + "%s: set tx power for chan %d\n", __func__, chan); return iwn_cmd(sc, IWN_CMD_TXPOWER, &cmd, sizeof cmd, async); #undef interpolate @@ -2531,25 +3088,34 @@ } /* - * Get the best (maximum) RSSI among Rx antennas (in dBm). + * Get the best (maximum) RSSI among the + * connected antennas and convert to dBm. */ -int -iwn_get_rssi(const struct iwn_rx_stat *stat) +int8_t +iwn_get_rssi(struct iwn_softc *sc, const struct iwn_rx_stat *stat) { - uint8_t mask, agc; - int rssi; + int mask, agc, rssi; - mask = (letoh16(stat->antenna) >> 4) & 0x7; - agc = (letoh16(stat->agc) >> 7) & 0x7f; + mask = (le16toh(stat->antenna) >> 4) & 0x7; + agc = (le16toh(stat->agc) >> 7) & 0x7f; rssi = 0; +#if 0 if (mask & (1 << 0)) /* Ant A */ rssi = max(rssi, stat->rssi[0]); if (mask & (1 << 1)) /* Ant B */ rssi = max(rssi, stat->rssi[2]); if (mask & (1 << 2)) /* Ant C */ rssi = max(rssi, stat->rssi[4]); - +#else + rssi = max(rssi, stat->rssi[0]); + rssi = max(rssi, stat->rssi[2]); + rssi = max(rssi, stat->rssi[4]); +#endif + DPRINTF(sc, IWN_DEBUG_RECV, "%s: agc %d mask 0x%x rssi %d %d %d " + "result %d\n", __func__, agc, mask, + stat->rssi[0], stat->rssi[2], stat->rssi[4], + rssi - agc - IWN_RSSI_TO_DBM); return rssi - agc - IWN_RSSI_TO_DBM; } @@ -2563,10 +3129,11 @@ total = nbant = 0; for (i = 0; i < 3; i++) { - if ((noise = letoh32(stats->noise[i]) & 0xff) == 0) - continue; - total += noise; - nbant++; + noise = le32toh(stats->noise[i]) & 0xff; + if (noise != 0) { + total += noise; + nbant++; + } } /* there should be at least one antenna but check anyway */ return (nbant == 0) ? -127 : (total / nbant) - 107; @@ -2581,10 +3148,10 @@ struct iwn_ucode_info *uc = &sc->ucode_info; int32_t r1, r2, r3, r4, temp; - r1 = letoh32(uc->temp[0].chan20MHz); - r2 = letoh32(uc->temp[1].chan20MHz); - r3 = letoh32(uc->temp[2].chan20MHz); - r4 = letoh32(sc->rawtemp); + r1 = le32toh(uc->temp[0].chan20MHz); + r2 = le32toh(uc->temp[1].chan20MHz); + r3 = le32toh(uc->temp[2].chan20MHz); + r4 = le32toh(sc->rawtemp); if (r1 == r3) /* prevents division by 0 (should not happen) */ return 0; @@ -2595,7 +3162,6 @@ temp = (259 * (r4 - r2)) / (r3 - r1); temp = (temp * 97) / 100 + 8; - DPRINTF(("temperature %dK/%dC\n", temp, IWN_KTOC(temp))); return IWN_KTOC(temp); } @@ -2623,13 +3189,14 @@ calib->energy_cck = 100; /* write initial sensitivity values */ - if ((error = iwn_send_sensitivity(sc)) != 0) + error = iwn_send_sensitivity(sc); + if (error != 0) return error; memset(&cmd, 0, sizeof cmd); cmd.code = IWN_SET_DIFF_GAIN; /* differential gains initially set to 0 for all 3 antennas */ - DPRINTF(("setting differential gains\n")); + DPRINTF(sc, IWN_DEBUG_CALIBRATE, "%s: calibrate phy\n", __func__); return iwn_cmd(sc, IWN_PHY_CALIB, &cmd, sizeof cmd, 1); } @@ -2648,8 +3215,8 @@ /* accumulate RSSI and noise for all 3 antennas */ for (i = 0; i < 3; i++) { - calib->rssi[i] += letoh32(stats->rssi[i]) & 0xff; - calib->noise[i] += letoh32(stats->noise[i]) & 0xff; + calib->rssi[i] += le32toh(stats->rssi[i]) & 0xff; + calib->noise[i] += le32toh(stats->noise[i]) & 0xff; } /* we update differential gain only once after 20 beacons */ @@ -2686,8 +3253,9 @@ cmd.gain[i] |= IWN_GAIN_SET; } } - DPRINTF(("setting differential gains Ant A/B/C: %x/%x/%x (%x)\n", - cmd.gain[0], cmd.gain[1], cmd.gain[2], sc->antmsk)); + DPRINTF(sc, IWN_DEBUG_CALIBRATE, + "%s: set differential gains Ant A/B/C: %x/%x/%x (%x)\n", + __func__,cmd.gain[0], cmd.gain[1], cmd.gain[2], sc->antmsk); if (iwn_cmd(sc, IWN_PHY_CALIB, &cmd, sizeof cmd, 1) == 0) calib->state = IWN_CALIB_STATE_RUN; } @@ -2723,21 +3291,22 @@ int i, needs_update = 0; /* check that we've been enabled long enough */ - if ((rxena = letoh32(stats->general.load)) == 0) + if ((rxena = le32toh(stats->general.load)) == 0) return; /* compute number of false alarms since last call for OFDM */ - fa = letoh32(stats->ofdm.bad_plcp) - calib->bad_plcp_ofdm; - fa += letoh32(stats->ofdm.fa) - calib->fa_ofdm; + fa = le32toh(stats->ofdm.bad_plcp) - calib->bad_plcp_ofdm; + fa += le32toh(stats->ofdm.fa) - calib->fa_ofdm; fa *= 200 * 1024; /* 200TU */ /* save counters values for next call */ - calib->bad_plcp_ofdm = letoh32(stats->ofdm.bad_plcp); - calib->fa_ofdm = letoh32(stats->ofdm.fa); + calib->bad_plcp_ofdm = le32toh(stats->ofdm.bad_plcp); + calib->fa_ofdm = le32toh(stats->ofdm.fa); if (fa > 50 * rxena) { /* high false alarm count, decrease sensitivity */ - DPRINTFN(2, ("OFDM high false alarm count: %u\n", fa)); + DPRINTF(sc, IWN_DEBUG_CALIBRATE, + "%s: OFDM high false alarm count: %u\n", __func__, fa); inc_clip(calib->corr_ofdm_x1, 1, 140); inc_clip(calib->corr_ofdm_mrc_x1, 1, 270); inc_clip(calib->corr_ofdm_x4, 1, 120); @@ -2745,7 +3314,8 @@ } else if (fa < 5 * rxena) { /* low false alarm count, increase sensitivity */ - DPRINTFN(2, ("OFDM low false alarm count: %u\n", fa)); + DPRINTF(sc, IWN_DEBUG_CALIBRATE, + "%s: OFDM low false alarm count: %u\n", __func__, fa); dec_clip(calib->corr_ofdm_x1, 1, 105); dec_clip(calib->corr_ofdm_mrc_x1, 1, 220); dec_clip(calib->corr_ofdm_x4, 1, 85); @@ -2754,7 +3324,7 @@ /* compute maximum noise among 3 antennas */ for (i = 0; i < 3; i++) - noise[i] = (letoh32(stats->general.noise[i]) >> 8) & 0xff; + noise[i] = (le32toh(stats->general.noise[i]) >> 8) & 0xff; val = max(noise[0], noise[1]); val = max(noise[2], val); /* insert it into our samples table */ @@ -2768,7 +3338,7 @@ /* compute maximum energy among 3 antennas */ for (i = 0; i < 3; i++) - energy[i] = letoh32(stats->general.energy[i]); + energy[i] = le32toh(stats->general.energy[i]); val = min(energy[0], energy[1]); val = min(energy[2], val); /* insert it into our samples table */ @@ -2782,17 +3352,18 @@ energy_min += 6; /* compute number of false alarms since last call for CCK */ - fa = letoh32(stats->cck.bad_plcp) - calib->bad_plcp_cck; - fa += letoh32(stats->cck.fa) - calib->fa_cck; + fa = le32toh(stats->cck.bad_plcp) - calib->bad_plcp_cck; + fa += le32toh(stats->cck.fa) - calib->fa_cck; fa *= 200 * 1024; /* 200TU */ /* save counters values for next call */ - calib->bad_plcp_cck = letoh32(stats->cck.bad_plcp); - calib->fa_cck = letoh32(stats->cck.fa); + calib->bad_plcp_cck = le32toh(stats->cck.bad_plcp); + calib->fa_cck = le32toh(stats->cck.fa); if (fa > 50 * rxena) { /* high false alarm count, decrease sensitivity */ - DPRINTFN(2, ("CCK high false alarm count: %u\n", fa)); + DPRINTF(sc, IWN_DEBUG_CALIBRATE, + "%s: CCK high false alarm count: %u\n", __func__, fa); calib->cck_state = IWN_CCK_STATE_HIFA; calib->low_fa = 0; @@ -2811,7 +3382,8 @@ } else if (fa < 5 * rxena) { /* low false alarm count, increase sensitivity */ - DPRINTFN(2, ("CCK low false alarm count: %u\n", fa)); + DPRINTF(sc, IWN_DEBUG_CALIBRATE, + "%s: CCK low false alarm count: %u\n", __func__, fa); calib->cck_state = IWN_CCK_STATE_LOFA; calib->low_fa++; @@ -2824,7 +3396,8 @@ } } else { /* not worth to increase or decrease sensitivity */ - DPRINTFN(2, ("CCK normal false alarm count: %u\n", fa)); + DPRINTF(sc, IWN_DEBUG_CALIBRATE, + "%s: CCK normal false alarm count: %u\n", __func__, fa); calib->low_fa = 0; calib->noise_ref = noise_ref; @@ -2864,39 +3437,40 @@ cmd.corr_barker = htole16(190); cmd.corr_barker_mrc = htole16(390); - DPRINTFN(2, ("setting sensitivity %d/%d/%d/%d/%d/%d/%d\n", + DPRINTF(sc, IWN_DEBUG_RESET, + "%s: set sensitivity %d/%d/%d/%d/%d/%d/%d\n", __func__, calib->corr_ofdm_x1, calib->corr_ofdm_mrc_x1, calib->corr_ofdm_x4, calib->corr_ofdm_mrc_x4, calib->corr_cck_x4, - calib->corr_cck_mrc_x4, calib->energy_cck)); + calib->corr_cck_mrc_x4, calib->energy_cck); return iwn_cmd(sc, IWN_SENSITIVITY, &cmd, sizeof cmd, 1); } int -iwn_auth(struct iwn_softc *sc) +iwn_auth(struct iwn_softc *sc, struct ieee80211vap *vap) { - struct ieee80211com *ic = &sc->sc_ic; - struct ieee80211_node *ni = ic->ic_bss; + struct ifnet *ifp = sc->sc_ifp; + struct ieee80211com *ic = ifp->if_l2com; + struct ieee80211_node *ni = vap->iv_bss; struct iwn_node_info node; int error; + sc->calib.state = IWN_CALIB_STATE_INIT; + /* update adapter's configuration */ + sc->config.associd = 0; IEEE80211_ADDR_COPY(sc->config.bssid, ni->ni_bssid); - sc->config.chan = ieee80211_chan2ieee(ic, ni->ni_chan); + sc->config.chan = htole16(ieee80211_chan2ieee(ic, ni->ni_chan)); sc->config.flags = htole32(IWN_CONFIG_TSF); - if (IEEE80211_IS_CHAN_2GHZ(ni->ni_chan)) { - sc->config.flags |= htole32(IWN_CONFIG_AUTO | - IWN_CONFIG_24GHZ); - } - switch (ic->ic_curmode) { - case IEEE80211_MODE_11A: + if (IEEE80211_IS_CHAN_2GHZ(ni->ni_chan)) + sc->config.flags |= htole32(IWN_CONFIG_AUTO | IWN_CONFIG_24GHZ); + if (IEEE80211_IS_CHAN_A(ni->ni_chan)) { sc->config.cck_mask = 0; sc->config.ofdm_mask = 0x15; - break; - case IEEE80211_MODE_11B: + } else if (IEEE80211_IS_CHAN_B(ni->ni_chan)) { sc->config.cck_mask = 0x03; sc->config.ofdm_mask = 0; - break; - default: /* assume 802.11b/g */ + } else { + /* XXX assume 802.11b/g */ sc->config.cck_mask = 0x0f; sc->config.ofdm_mask = 0x15; } @@ -2904,18 +3478,33 @@ sc->config.flags |= htole32(IWN_CONFIG_SHSLOT); if (ic->ic_flags & IEEE80211_F_SHPREAMBLE) sc->config.flags |= htole32(IWN_CONFIG_SHPREAMBLE); - DPRINTF(("config chan %d flags %x cck %x ofdm %x\n", sc->config.chan, - sc->config.flags, sc->config.cck_mask, sc->config.ofdm_mask)); + sc->config.filter &= ~htole32(IWN_FILTER_BSS); + + DPRINTF(sc, IWN_DEBUG_STATE, + "%s: config chan %d mode %d flags 0x%x cck 0x%x ofdm 0x%x " + "ht_single 0x%x ht_dual 0x%x rxchain 0x%x " + "myaddr %6D wlap %6D bssid %6D associd %d filter 0x%x\n", + __func__, + le16toh(sc->config.chan), sc->config.mode, le32toh(sc->config.flags), + sc->config.cck_mask, sc->config.ofdm_mask, + sc->config.ht_single_mask, sc->config.ht_dual_mask, + le16toh(sc->config.rxchain), + sc->config.myaddr, ":", sc->config.wlap, ":", sc->config.bssid, ":", + le16toh(sc->config.associd), le32toh(sc->config.filter)); error = iwn_cmd(sc, IWN_CMD_CONFIGURE, &sc->config, sizeof (struct iwn_config), 1); if (error != 0) { - printf("%s: could not configure\n", sc->sc_dev.dv_xname); + device_printf(sc->sc_dev, + "%s: could not configure, error %d\n", __func__, error); return error; } + sc->sc_curchan = ic->ic_curchan; /* configuration has changed, set Tx power accordingly */ - if ((error = iwn_set_txpower(sc, ni->ni_chan, 1)) != 0) { - printf("%s: could not set Tx power\n", sc->sc_dev.dv_xname); + error = iwn_set_txpower(sc, ni->ni_chan, 1); + if (error != 0) { + device_printf(sc->sc_dev, + "%s: could not set Tx power, error %d\n", __func__, error); return error; } @@ -2924,19 +3513,21 @@ * add the broadcast node again. */ memset(&node, 0, sizeof node); - IEEE80211_ADDR_COPY(node.macaddr, etherbroadcastaddr); + IEEE80211_ADDR_COPY(node.macaddr, ifp->if_broadcastaddr); node.id = IWN_ID_BROADCAST; - DPRINTF(("adding broadcast node\n")); + DPRINTF(sc, IWN_DEBUG_STATE, "%s: add broadcast node\n", __func__); error = iwn_cmd(sc, IWN_CMD_ADD_NODE, &node, sizeof node, 1); if (error != 0) { - printf("%s: could not add broadcast node\n", - sc->sc_dev.dv_xname); + device_printf(sc->sc_dev, + "%s: could not add broadcast node, error %d\n", + __func__, error); return error; } - DPRINTF(("setting MRR for node %d\n", node.id)); - if ((error = iwn_setup_node_mrr(sc, node.id, 1)) != 0) { - printf("%s: could not setup MRR for broadcast node\n", - sc->sc_dev.dv_xname, node.id); + error = iwn_set_link_quality(sc, node.id, ic->ic_curchan, 1); + if (error != 0) { + device_printf(sc->sc_dev, + "%s: could not setup MRR for broadcast node, error %d\n", + __func__, error); return error; } @@ -2947,12 +3538,16 @@ * Configure the adapter for associated state. */ int -iwn_run(struct iwn_softc *sc) +iwn_run(struct iwn_softc *sc, struct ieee80211vap *vap) { - struct ieee80211com *ic = &sc->sc_ic; - struct ieee80211_node *ni = ic->ic_bss; +#define MS(v,x) (((v) & x) >> x##_S) + struct ifnet *ifp = sc->sc_ifp; + struct ieee80211com *ic = ifp->if_l2com; + struct ieee80211_node *ni = vap->iv_bss; struct iwn_node_info node; - int error; + int error, maxrxampdu, ampdudensity; + + sc->calib.state = IWN_CALIB_STATE_INIT; if (ic->ic_opmode == IEEE80211_M_MONITOR) { /* link LED blinks while monitoring */ @@ -2963,30 +3558,59 @@ iwn_enable_tsf(sc, ni); /* update adapter's configuration */ - sc->config.associd = htole16(ni->ni_associd & ~0xc000); + sc->config.associd = htole16(IEEE80211_AID(ni->ni_associd)); /* short preamble/slot time are negotiated when associating */ - sc->config.flags &= ~htole32(IWN_CONFIG_SHPREAMBLE | - IWN_CONFIG_SHSLOT); + sc->config.flags &= ~htole32(IWN_CONFIG_SHPREAMBLE | IWN_CONFIG_SHSLOT); if (ic->ic_flags & IEEE80211_F_SHSLOT) sc->config.flags |= htole32(IWN_CONFIG_SHSLOT); if (ic->ic_flags & IEEE80211_F_SHPREAMBLE) sc->config.flags |= htole32(IWN_CONFIG_SHPREAMBLE); + if (IEEE80211_IS_CHAN_HT(ni->ni_chan)) { + sc->config.flags &= ~htole32(IWN_CONFIG_HT); + if (IEEE80211_IS_CHAN_HT40U(ni->ni_chan)) + sc->config.flags |= htole32(IWN_CONFIG_HT40U); + else if (IEEE80211_IS_CHAN_HT40D(ni->ni_chan)) + sc->config.flags |= htole32(IWN_CONFIG_HT40D); + else + sc->config.flags |= htole32(IWN_CONFIG_HT20); + sc->config.rxchain = htole16( + (3 << IWN_RXCHAIN_VALID_S) + | (3 << IWN_RXCHAIN_MIMO_CNT_S) + | (1 << IWN_RXCHAIN_CNT_S) + | IWN_RXCHAIN_MIMO_FORCE); + + maxrxampdu = MS(ni->ni_htparam, IEEE80211_HTCAP_MAXRXAMPDU); + ampdudensity = MS(ni->ni_htparam, IEEE80211_HTCAP_MPDUDENSITY); + } else + maxrxampdu = ampdudensity = 0; sc->config.filter |= htole32(IWN_FILTER_BSS); - DPRINTF(("config chan %d flags %x\n", sc->config.chan, - sc->config.flags)); + DPRINTF(sc, IWN_DEBUG_STATE, + "%s: config chan %d mode %d flags 0x%x cck 0x%x ofdm 0x%x " + "ht_single 0x%x ht_dual 0x%x rxchain 0x%x " + "myaddr %6D wlap %6D bssid %6D associd %d filter 0x%x\n", + __func__, + le16toh(sc->config.chan), sc->config.mode, le32toh(sc->config.flags), + sc->config.cck_mask, sc->config.ofdm_mask, + sc->config.ht_single_mask, sc->config.ht_dual_mask, + le16toh(sc->config.rxchain), + sc->config.myaddr, ":", sc->config.wlap, ":", sc->config.bssid, ":", + le16toh(sc->config.associd), le32toh(sc->config.filter)); error = iwn_cmd(sc, IWN_CMD_CONFIGURE, &sc->config, sizeof (struct iwn_config), 1); if (error != 0) { - printf("%s: could not update configuration\n", - sc->sc_dev.dv_xname); + device_printf(sc->sc_dev, + "%s: could not update configuration, error %d\n", + __func__, error); return error; } + sc->sc_curchan = ni->ni_chan; /* configuration has changed, set Tx power accordingly */ - if ((error = iwn_set_txpower(sc, ni->ni_chan, 1)) != 0) { - printf("%s: could not set Tx power\n", - sc->sc_dev.dv_xname); + error = iwn_set_txpower(sc, ni->ni_chan, 1); + if (error != 0) { + device_printf(sc->sc_dev, + "%s: could not set Tx power, error %d\n", __func__, error); return error; } @@ -2994,41 +3618,41 @@ memset(&node, 0, sizeof node); IEEE80211_ADDR_COPY(node.macaddr, ni->ni_macaddr); node.id = IWN_ID_BSS; - node.htflags = htole32(3 << IWN_AMDPU_SIZE_FACTOR_SHIFT | - 5 << IWN_AMDPU_DENSITY_SHIFT); - DPRINTF(("adding BSS node\n")); + node.htflags = htole32( + (maxrxampdu << IWN_MAXRXAMPDU_S) | + (ampdudensity << IWN_MPDUDENSITY_S)); + DPRINTF(sc, IWN_DEBUG_STATE, "%s: add BSS node, id %d htflags 0x%x\n", + __func__, node.id, le32toh(node.htflags)); error = iwn_cmd(sc, IWN_CMD_ADD_NODE, &node, sizeof node, 1); if (error != 0) { - printf("%s: could not add BSS node\n", sc->sc_dev.dv_xname); + device_printf(sc->sc_dev,"could not add BSS node\n"); return error; } - DPRINTF(("setting MRR for node %d\n", node.id)); - if ((error = iwn_setup_node_mrr(sc, node.id, 1)) != 0) { - printf("%s: could not setup MRR for node %d\n", - sc->sc_dev.dv_xname, node.id); + error = iwn_set_link_quality(sc, node.id, ni->ni_chan, 1); + if (error != 0) { + device_printf(sc->sc_dev, + "%s: could not setup MRR for node %d, error %d\n", + __func__, node.id, error); return error; } - if (ic->ic_opmode == IEEE80211_M_STA) { - /* fake a join to init the tx rate */ - iwn_newassoc(ic, ni, 1); - } - - if ((error = iwn_init_sensitivity(sc)) != 0) { - printf("%s: could not set sensitivity\n", - sc->sc_dev.dv_xname); + error = iwn_init_sensitivity(sc); + if (error != 0) { + device_printf(sc->sc_dev, + "%s: could not set sensitivity, error %d\n", + __func__, error); return error; } - /* start periodic calibration timer */ + /* start/restart periodic calibration timer */ sc->calib.state = IWN_CALIB_STATE_ASSOC; - sc->calib_cnt = 0; - timeout_add(&sc->calib_to, hz / 2); + iwn_calib_reset(sc); /* link LED always on while associated */ iwn_set_led(sc, IWN_LED_LINK, 0, 1); return 0; +#undef MS } /* @@ -3036,9 +3660,11 @@ * into a mbuf instead of using the pre-allocated set of commands. */ int -iwn_scan(struct iwn_softc *sc, uint16_t flags) +iwn_scan(struct iwn_softc *sc) { - struct ieee80211com *ic = &sc->sc_ic; + struct ifnet *ifp = sc->sc_ifp; + struct ieee80211com *ic = ifp->if_l2com; + struct ieee80211_scan_state *ss = ic->ic_scan; /*XXX*/ struct iwn_tx_ring *ring = &sc->txq[4]; struct iwn_tx_desc *desc; struct iwn_tx_data *data; @@ -3052,23 +3678,17 @@ struct ieee80211_channel *c; enum ieee80211_phymode mode; uint8_t *frm; - int pktlen, error; + int pktlen, error, nrates; + bus_addr_t physaddr; desc = &ring->desc[ring->cur]; data = &ring->data[ring->cur]; - MGETHDR(data->m, M_DONTWAIT, MT_DATA); + /* XXX malloc */ + data->m = m_getcl(M_DONTWAIT, MT_DATA, 0); if (data->m == NULL) { - printf("%s: could not allocate mbuf for scan command\n", - sc->sc_dev.dv_xname); - return ENOMEM; - } - MCLGET(data->m, M_DONTWAIT); - if (!(data->m->m_flags & M_EXT)) { - m_freem(data->m); - data->m = NULL; - printf("%s: could not allocate mbuf for scan command\n", - sc->sc_dev.dv_xname); + device_printf(sc->sc_dev, + "%s: could not allocate mbuf for scan command\n", __func__); return ENOMEM; } @@ -3080,6 +3700,8 @@ hdr = (struct iwn_scan_hdr *)cmd->data; memset(hdr, 0, sizeof (struct iwn_scan_hdr)); + + /* XXX use scan state */ /* * Move to the next channel if no packets are received within 5 msecs * after sending the probe request (this helps to reduce the duration @@ -3089,7 +3711,7 @@ hdr->plcp_threshold = htole16(1); /* min # of packets */ /* select Ant B and Ant C for scanning */ - hdr->rxchain = htole16(0x3e1 | 7 << IWN_RXCHAIN_ANTMSK_SHIFT); + hdr->rxchain = htole16(0x3e1 | (7 << IWN_RXCHAIN_VALID_S)); tx = (struct iwn_cmd_data *)(hdr + 1); memset(tx, 0, sizeof (struct iwn_cmd_data)); @@ -3098,22 +3720,22 @@ tx->lifetime = htole32(IWN_LIFETIME_INFINITE); tx->rflags = IWN_RFLAG_ANT_B; - if (flags & IEEE80211_CHAN_A) { + if (IEEE80211_IS_CHAN_A(ic->ic_curchan)) { hdr->crc_threshold = htole16(1); /* send probe requests at 6Mbps */ - tx->rate = iwn_ridx_to_plcp[IWN_OFDM6]; + tx->rate = iwn_ridx_to_plcp[IWN_RATE_OFDM6]; } else { hdr->flags = htole32(IWN_CONFIG_24GHZ | IWN_CONFIG_AUTO); /* send probe requests at 1Mbps */ - tx->rate = iwn_ridx_to_plcp[IWN_CCK1]; + tx->rate = iwn_ridx_to_plcp[IWN_RATE_CCK1]; tx->rflags |= IWN_RFLAG_CCK; } essid = (struct iwn_scan_essid *)(tx + 1); memset(essid, 0, 4 * sizeof (struct iwn_scan_essid)); essid[0].id = IEEE80211_ELEMID_SSID; - essid[0].len = ic->ic_des_esslen; - memcpy(essid[0].data, ic->ic_des_essid, ic->ic_des_esslen); + essid[0].len = ss->ss_ssid[0].len; + memcpy(essid[0].data, ss->ss_ssid[0].ssid, ss->ss_ssid[0].len); /* * Build a probe request frame. Most of the following code is a @@ -3123,80 +3745,97 @@ wh->i_fc[0] = IEEE80211_FC0_VERSION_0 | IEEE80211_FC0_TYPE_MGT | IEEE80211_FC0_SUBTYPE_PROBE_REQ; wh->i_fc[1] = IEEE80211_FC1_DIR_NODS; - IEEE80211_ADDR_COPY(wh->i_addr1, etherbroadcastaddr); - IEEE80211_ADDR_COPY(wh->i_addr2, ic->ic_myaddr); - IEEE80211_ADDR_COPY(wh->i_addr3, etherbroadcastaddr); + IEEE80211_ADDR_COPY(wh->i_addr1, ifp->if_broadcastaddr); + IEEE80211_ADDR_COPY(wh->i_addr2, IF_LLADDR(ifp)); + IEEE80211_ADDR_COPY(wh->i_addr3, ifp->if_broadcastaddr); *(u_int16_t *)&wh->i_dur[0] = 0; /* filled by h/w */ *(u_int16_t *)&wh->i_seq[0] = 0; /* filled by h/w */ frm = (uint8_t *)(wh + 1); /* add SSID IE */ - frm = ieee80211_add_ssid(frm, ic->ic_des_essid, ic->ic_des_esslen); + *frm++ = IEEE80211_ELEMID_SSID; + *frm++ = ss->ss_ssid[0].len; + memcpy(frm, ss->ss_ssid[0].ssid, ss->ss_ssid[0].len); + frm += ss->ss_ssid[0].len; - mode = ieee80211_chan2mode(ic, ic->ic_ibss_chan); + mode = ieee80211_chan2mode(ic->ic_curchan); rs = &ic->ic_sup_rates[mode]; /* add supported rates IE */ - frm = ieee80211_add_rates(frm, rs); + *frm++ = IEEE80211_ELEMID_RATES; + nrates = rs->rs_nrates; + if (nrates > IEEE80211_RATE_SIZE) + nrates = IEEE80211_RATE_SIZE; + *frm++ = nrates; + memcpy(frm, rs->rs_rates, nrates); + frm += nrates; /* add supported xrates IE */ - if (rs->rs_nrates > IEEE80211_RATE_SIZE) - frm = ieee80211_add_xrates(frm, rs); + if (rs->rs_nrates > IEEE80211_RATE_SIZE) { + nrates = rs->rs_nrates - IEEE80211_RATE_SIZE; + *frm++ = IEEE80211_ELEMID_XRATES; + *frm++ = (uint8_t)nrates; + memcpy(frm, rs->rs_rates + IEEE80211_RATE_SIZE, nrates); + frm += nrates; + } /* setup length of probe request */ tx->len = htole16(frm - (uint8_t *)wh); + c = ic->ic_curchan; chan = (struct iwn_scan_chan *)frm; - for (c = &ic->ic_channels[1]; - c <= &ic->ic_channels[IEEE80211_CHAN_MAX]; c++) { - if ((c->ic_flags & flags) != flags) - continue; + chan->chan = ieee80211_chan2ieee(ic, c); + chan->flags = 0; + if ((c->ic_flags & IEEE80211_CHAN_PASSIVE) == 0) { + chan->flags |= IWN_CHAN_ACTIVE; + if (ss->ss_nssid > 0) + chan->flags |= IWN_CHAN_DIRECT; + } + chan->dsp_gain = 0x6e; + if (IEEE80211_IS_CHAN_5GHZ(c)) { + chan->rf_gain = 0x3b; + chan->active = htole16(10); + chan->passive = htole16(110); + } else { + chan->rf_gain = 0x28; + chan->active = htole16(20); + chan->passive = htole16(120); + } - chan->chan = ieee80211_chan2ieee(ic, c); - chan->flags = 0; - if (!(c->ic_flags & IEEE80211_CHAN_PASSIVE)) { - chan->flags |= IWN_CHAN_ACTIVE; - if (ic->ic_des_esslen != 0) - chan->flags |= IWN_CHAN_DIRECT; - } - chan->dsp_gain = 0x6e; - if (IEEE80211_IS_CHAN_5GHZ(c)) { - chan->rf_gain = 0x3b; - chan->active = htole16(10); - chan->passive = htole16(110); - } else { - chan->rf_gain = 0x28; - chan->active = htole16(20); - chan->passive = htole16(120); - } - hdr->nchan++; - chan++; + DPRINTF(sc, IWN_DEBUG_STATE, "%s: chan %u flags 0x%x rf_gain 0x%x " + "dsp_gain 0x%x active 0x%x passive 0x%x\n", __func__, + chan->chan, chan->flags, chan->rf_gain, chan->dsp_gain, + chan->active, chan->passive); + hdr->nchan++; + chan++; - frm += sizeof (struct iwn_scan_chan); - } + frm += sizeof (struct iwn_scan_chan); hdr->len = htole16(frm - (uint8_t *)hdr); pktlen = frm - (uint8_t *)cmd; - error = bus_dmamap_load(sc->sc_dmat, data->map, cmd, pktlen, NULL, - BUS_DMA_NOWAIT); + error = bus_dmamap_load(ring->data_dmat, data->map, cmd, pktlen, + iwn_dma_map_addr, &physaddr, BUS_DMA_NOWAIT); if (error != 0) { - printf("%s: could not map scan command\n", - sc->sc_dev.dv_xname); + device_printf(sc->sc_dev, + "%s: could not map scan command, error %d\n", + __func__, error); m_freem(data->m); data->m = NULL; return error; } IWN_SET_DESC_NSEGS(desc, 1); - IWN_SET_DESC_SEG(desc, 0, data->map->dm_segs[0].ds_addr, - data->map->dm_segs[0].ds_len); + IWN_SET_DESC_SEG(desc, 0, physaddr, pktlen); sc->shared->len[ring->qid][ring->cur] = htole16(8); - if (ring->cur < IWN_TX_WINDOW) { + if (ring->cur < IWN_TX_WINDOW) sc->shared->len[ring->qid][ring->cur + IWN_TX_RING_COUNT] = htole16(8); - } + + bus_dmamap_sync(ring->desc_dma.tag, ring->desc_dma.map, + BUS_DMASYNC_PREWRITE); + bus_dmamap_sync(ring->data_dmat, data->map, BUS_DMASYNC_PREWRITE); /* kick cmd ring */ ring->cur = (ring->cur + 1) % IWN_TX_RING_COUNT; @@ -3208,8 +3847,8 @@ int iwn_config(struct iwn_softc *sc) { - struct ieee80211com *ic = &sc->sc_ic; - struct ifnet *ifp = &ic->ic_if; + struct ifnet *ifp = sc->sc_ifp; + struct ieee80211com *ic = ifp->if_l2com; struct iwn_power power; struct iwn_bluetooth bluetooth; struct iwn_node_info node; @@ -3218,10 +3857,12 @@ /* set power mode */ memset(&power, 0, sizeof power); power.flags = htole16(IWN_POWER_CAM | 0x8); - DPRINTF(("setting power mode\n")); + DPRINTF(sc, IWN_DEBUG_RESET, "%s: set power mode\n", __func__); error = iwn_cmd(sc, IWN_CMD_SET_POWER_MODE, &power, sizeof power, 0); if (error != 0) { - printf("%s: could not set power mode\n", sc->sc_dev.dv_xname); + device_printf(sc->sc_dev, + "%s: could not set power mode, error %d\n", + __func__, error); return error; } @@ -3230,27 +3871,26 @@ bluetooth.flags = 3; bluetooth.lead = 0xaa; bluetooth.kill = 1; - DPRINTF(("configuring bluetooth coexistence\n")); + DPRINTF(sc, IWN_DEBUG_RESET, "%s: config bluetooth coexistence\n", + __func__); error = iwn_cmd(sc, IWN_CMD_BLUETOOTH, &bluetooth, sizeof bluetooth, 0); if (error != 0) { - printf("%s: could not configure bluetooth coexistence\n", - sc->sc_dev.dv_xname); + device_printf(sc->sc_dev, + "%s: could not configure bluetooth coexistence, error %d\n", + __func__, error); return error; } /* configure adapter */ memset(&sc->config, 0, sizeof (struct iwn_config)); - IEEE80211_ADDR_COPY(ic->ic_myaddr, LLADDR(ifp->if_sadl)); - IEEE80211_ADDR_COPY(sc->config.myaddr, ic->ic_myaddr); - IEEE80211_ADDR_COPY(sc->config.wlap, ic->ic_myaddr); + IEEE80211_ADDR_COPY(sc->config.myaddr, IF_LLADDR(ifp)); + IEEE80211_ADDR_COPY(sc->config.wlap, IF_LLADDR(ifp)); /* set default channel */ - sc->config.chan = ieee80211_chan2ieee(ic, ic->ic_ibss_chan); + sc->config.chan = htole16(ieee80211_chan2ieee(ic, ic->ic_curchan)); sc->config.flags = htole32(IWN_CONFIG_TSF); - if (IEEE80211_IS_CHAN_2GHZ(ic->ic_ibss_chan)) { - sc->config.flags |= htole32(IWN_CONFIG_AUTO | - IWN_CONFIG_24GHZ); - } + if (IEEE80211_IS_CHAN_2GHZ(ic->ic_curchan)) + sc->config.flags |= htole32(IWN_CONFIG_AUTO | IWN_CONFIG_24GHZ); sc->config.filter = 0; switch (ic->ic_opmode) { case IEEE80211_M_STA: @@ -3269,50 +3909,72 @@ sc->config.filter |= htole32(IWN_FILTER_MULTICAST | IWN_FILTER_CTL | IWN_FILTER_PROMISC); break; + default: + break; } sc->config.cck_mask = 0x0f; /* not yet negotiated */ sc->config.ofdm_mask = 0xff; /* not yet negotiated */ sc->config.ht_single_mask = 0xff; sc->config.ht_dual_mask = 0xff; - sc->config.rxchain = htole16(0x2800 | 7 << IWN_RXCHAIN_ANTMSK_SHIFT); - DPRINTF(("setting configuration\n")); + sc->config.rxchain = htole16(0x2800 | (7 << IWN_RXCHAIN_VALID_S)); + + DPRINTF(sc, IWN_DEBUG_STATE, + "%s: config chan %d mode %d flags 0x%x cck 0x%x ofdm 0x%x " + "ht_single 0x%x ht_dual 0x%x rxchain 0x%x " + "myaddr %6D wlap %6D bssid %6D associd %d filter 0x%x\n", + __func__, + le16toh(sc->config.chan), sc->config.mode, le32toh(sc->config.flags), + sc->config.cck_mask, sc->config.ofdm_mask, + sc->config.ht_single_mask, sc->config.ht_dual_mask, + le16toh(sc->config.rxchain), + sc->config.myaddr, ":", sc->config.wlap, ":", sc->config.bssid, ":", + le16toh(sc->config.associd), le32toh(sc->config.filter)); error = iwn_cmd(sc, IWN_CMD_CONFIGURE, &sc->config, sizeof (struct iwn_config), 0); if (error != 0) { - printf("%s: configure command failed\n", sc->sc_dev.dv_xname); + device_printf(sc->sc_dev, + "%s: configure command failed, error %d\n", + __func__, error); return error; } + sc->sc_curchan = ic->ic_curchan; /* configuration has changed, set Tx power accordingly */ - if ((error = iwn_set_txpower(sc, ic->ic_ibss_chan, 0)) != 0) { - printf("%s: could not set Tx power\n", sc->sc_dev.dv_xname); + error = iwn_set_txpower(sc, ic->ic_curchan, 0); + if (error != 0) { + device_printf(sc->sc_dev, + "%s: could not set Tx power, error %d\n", __func__, error); return error; } /* add broadcast node */ memset(&node, 0, sizeof node); - IEEE80211_ADDR_COPY(node.macaddr, etherbroadcastaddr); + IEEE80211_ADDR_COPY(node.macaddr, ic->ic_ifp->if_broadcastaddr); node.id = IWN_ID_BROADCAST; - DPRINTF(("adding broadcast node\n")); + node.rate = iwn_plcp_signal(2); + DPRINTF(sc, IWN_DEBUG_RESET, "%s: add broadcast node\n", __func__); error = iwn_cmd(sc, IWN_CMD_ADD_NODE, &node, sizeof node, 0); if (error != 0) { - printf("%s: could not add broadcast node\n", - sc->sc_dev.dv_xname); + device_printf(sc->sc_dev, + "%s: could not add broadcast node, error %d\n", + __func__, error); return error; } - DPRINTF(("setting MRR for node %d\n", node.id)); - if ((error = iwn_setup_node_mrr(sc, node.id, 0)) != 0) { - printf("%s: could not setup MRR for node %d\n", - sc->sc_dev.dv_xname, node.id); + error = iwn_set_link_quality(sc, node.id, ic->ic_curchan, 0); + if (error != 0) { + device_printf(sc->sc_dev, + "%s: could not setup MRR for node %d, error %d\n", + __func__, node.id, error); return error; } - if ((error = iwn_set_critical_temp(sc)) != 0) { - printf("%s: could not set critical temperature\n", - sc->sc_dev.dv_xname); + error = iwn_set_critical_temp(sc); + if (error != 0) { + device_printf(sc->sc_dev, + "%s: could not set critical temperature, error %d\n", + __func__, error); return error; } - return 0; } @@ -3348,7 +4010,7 @@ IWN_WRITE(sc, IWN_MEM_WDATA, 64); /* set sched. frame limit */ IWN_WRITE(sc, IWN_MEM_WADDR, base + IWN_QUEUE_OFFSET(qid) + 4); - IWN_WRITE(sc, IWN_MEM_WDATA, 64 << 16); + IWN_WRITE(sc, IWN_MEM_WDATA, 10 << 16); } /* enable interrupts for all 16 queues */ @@ -3384,10 +4046,9 @@ break; DELAY(10); } - if (ntries == 100) { - printf("%s: timeout waiting for master\n", - sc->sc_dev.dv_xname); - } + if (ntries == 100) + device_printf(sc->sc_dev, + "%s: timeout waiting for master\n", __func__); } int @@ -3412,8 +4073,8 @@ DELAY(10); } if (ntries == 1000) { - printf("%s: timeout waiting for clock stabilization\n", - sc->sc_dev.dv_xname); + device_printf(sc->sc_dev, + "%s: timeout waiting for clock stabilization\n", __func__); return ETIMEDOUT; } return 0; @@ -3428,19 +4089,19 @@ IWN_WRITE(sc, IWN_INTR_MIT, 512 / 32); /* voodoo from the reference driver */ - tmp = pci_conf_read(sc->sc_pct, sc->sc_pcitag, PCI_CLASS_REG); - tmp = PCI_REVISION(tmp); + tmp = pci_read_config(sc->sc_dev, PCIR_REVID,1); if ((tmp & 0x80) && (tmp & 0x7f) < 8) { /* enable "no snoop" field */ - tmp = pci_conf_read(sc->sc_pct, sc->sc_pcitag, 0xe8); + tmp = pci_read_config(sc->sc_dev, 0xe8, 1); tmp &= ~IWN_DIS_NOSNOOP; - pci_conf_write(sc->sc_pct, sc->sc_pcitag, 0xe8, tmp); + /* clear device specific PCI configuration register 0x41 */ + pci_write_config(sc->sc_dev, 0xe8, tmp, 1); } /* disable L1 entry to work around a hardware bug */ - tmp = pci_conf_read(sc->sc_pct, sc->sc_pcitag, 0xf0); + tmp = pci_read_config(sc->sc_dev, 0xf0, 1); tmp &= ~IWN_ENA_L1; - pci_conf_write(sc->sc_pct, sc->sc_pcitag, 0xf0, tmp); + pci_write_config(sc->sc_dev, 0xf0, tmp, 1 ); hw = IWN_READ(sc, IWN_HWCONFIG); IWN_WRITE(sc, IWN_HWCONFIG, hw | 0x310); @@ -3454,17 +4115,27 @@ iwn_mem_unlock(sc); } -int -iwn_init(struct ifnet *ifp) +void +iwn_init_locked(struct iwn_softc *sc) { - struct iwn_softc *sc = ifp->if_softc; - struct ieee80211com *ic = &sc->sc_ic; + struct ifnet *ifp = sc->sc_ifp; uint32_t tmp; int error, qid; - if ((error = iwn_reset(sc)) != 0) { - printf("%s: could not reset adapter\n", sc->sc_dev.dv_xname); - goto fail1; + IWN_LOCK_ASSERT(sc); + + /* load the firmware */ + if (sc->fw_fp == NULL && (error = iwn_load_firmware(sc)) != 0) { + device_printf(sc->sc_dev, + "%s: could not load firmware, error %d\n", __func__, error); + return; + } + + error = iwn_reset(sc); + if (error != 0) { + device_printf(sc->sc_dev, + "%s: could not reset adapter, error %d\n", __func__, error); + return; } iwn_mem_lock(sc); @@ -3530,15 +4201,16 @@ /* check that the radio is not disabled by RF switch */ if (!(IWN_READ(sc, IWN_GPIO_CTL) & IWN_GPIO_RF_ENABLED)) { - printf("%s: radio is disabled by hardware switch\n", - sc->sc_dev.dv_xname); - error = EPERM; /* XXX ;-) */ - goto fail1; + device_printf(sc->sc_dev, + "radio is disabled by hardware switch\n"); + return; } - if ((error = iwn_load_firmware(sc)) != 0) { - printf("%s: could not load firmware\n", sc->sc_dev.dv_xname); - goto fail1; + error = iwn_transfer_firmware(sc); + if (error != 0) { + device_printf(sc->sc_dev, + "%s: could not load firmware, error %d\n", __func__, error); + return; } /* firmware has notified us that it is alive.. */ @@ -3546,56 +4218,56 @@ sc->rawtemp = sc->ucode_info.temp[3].chan20MHz; sc->temp = iwn_get_temperature(sc); - DPRINTF(("temperature=%d\n", sc->temp)); - sc->sensor.value = IWN_CTOMUK(sc->temp); - sc->sensor.flags &= ~SENSOR_FINVALID; + DPRINTF(sc, IWN_DEBUG_RESET, "%s: temperature=%d\n", + __func__, sc->temp); - if ((error = iwn_config(sc)) != 0) { - printf("%s: could not configure device\n", - sc->sc_dev.dv_xname); - goto fail1; + error = iwn_config(sc); + if (error != 0) { + device_printf(sc->sc_dev, + "%s: could not configure device, error %d\n", + __func__, error); + return; } - ifp->if_flags &= ~IFF_OACTIVE; - ifp->if_flags |= IFF_RUNNING; + ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; + ifp->if_drv_flags |= IFF_DRV_RUNNING; +} - if (ic->ic_opmode != IEEE80211_M_MONITOR) - ieee80211_begin_scan(ifp); - else - ieee80211_new_state(ic, IEEE80211_S_RUN, -1); +void +iwn_init(void *arg) +{ + struct iwn_softc *sc = arg; + struct ifnet *ifp = sc->sc_ifp; + struct ieee80211com *ic = ifp->if_l2com; - return 0; + IWN_LOCK(sc); + iwn_init_locked(sc); + IWN_UNLOCK(sc); -fail1: iwn_stop(ifp, 1); - return error; + if (ifp->if_drv_flags & IFF_DRV_RUNNING) + ieee80211_start_all(ic); } void -iwn_stop(struct ifnet *ifp, int disable) +iwn_stop_locked(struct iwn_softc *sc) { - struct iwn_softc *sc = ifp->if_softc; - struct ieee80211com *ic = &sc->sc_ic; + struct ifnet *ifp = sc->sc_ifp; uint32_t tmp; int i; - ifp->if_timer = sc->sc_tx_timer = 0; - ifp->if_flags &= ~(IFF_RUNNING | IFF_OACTIVE); - - /* in case we were scanning, release the scan "lock" */ - ic->ic_scan_lock = IEEE80211_SCAN_UNLOCKED; - - ieee80211_new_state(ic, IEEE80211_S_INIT, -1); + IWN_LOCK_ASSERT(sc); IWN_WRITE(sc, IWN_RESET, IWN_NEVO_RESET); + sc->sc_tx_timer = 0; + callout_stop(&sc->sc_timer_to); + ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE); + /* disable interrupts */ IWN_WRITE(sc, IWN_MASK, 0); IWN_WRITE(sc, IWN_INTR, 0xffffffff); IWN_WRITE(sc, IWN_INTR_STATUS, 0xffffffff); - /* make sure we no longer hold the memory lock */ - iwn_mem_unlock(sc); - /* reset all Tx rings */ for (i = 0; i < IWN_NTXQUEUES; i++) iwn_reset_tx_ring(sc, &sc->txq[i]); @@ -3603,21 +4275,213 @@ /* reset Rx ring */ iwn_reset_rx_ring(sc, &sc->rxq); - /* temperature is no longer valid */ - sc->sensor.value = 0; - sc->sensor.flags |= SENSOR_FINVALID; - iwn_mem_lock(sc); iwn_mem_write(sc, IWN_MEM_CLOCK2, 0x200); iwn_mem_unlock(sc); DELAY(5); - iwn_stop_master(sc); + tmp = IWN_READ(sc, IWN_RESET); IWN_WRITE(sc, IWN_RESET, tmp | IWN_SW_RESET); } -struct cfdriver iwn_cd = { - NULL, "iwn", DV_IFNET +void +iwn_stop(struct iwn_softc *sc) +{ + IWN_LOCK(sc); + iwn_stop_locked(sc); + IWN_UNLOCK(sc); +} + +/* + * Callback from net80211 to start a scan. + */ +static void +iwn_scan_start(struct ieee80211com *ic) +{ + struct ifnet *ifp = ic->ic_ifp; + struct iwn_softc *sc = ifp->if_softc; + + IWN_LOCK(sc); + /* make the link LED blink while we're scanning */ + iwn_set_led(sc, IWN_LED_LINK, 20, 2); + IWN_UNLOCK(sc); +} + +/* + * Callback from net80211 to terminate a scan. + */ +static void +iwn_scan_end(struct ieee80211com *ic) +{ + /* ignore */ +} + +/* + * Callback from net80211 to force a channel change. + */ +static void +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); + + 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); +} + +/* + * Callback from net80211 to start scanning of the current channel. + */ +static void +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_LOCK(sc); + error = iwn_scan(sc); + IWN_UNLOCK(sc); + if (error != 0) + ieee80211_cancel_scan(vap); +} + +/* + * Callback from net80211 to handle the minimum dwell time being met. + * The intent is to terminate the scan but we just let the firmware + * notify us when it's finished as we have no safe way to abort it. + */ +static void +iwn_scan_mindwell(struct ieee80211_scan_state *ss) +{ + /* NB: don't try to abort scan; wait for firmware to finish */ +} + +static void +iwn_hwreset(void *arg0, int pending) +{ + struct iwn_softc *sc = arg0; + struct ifnet *ifp = sc->sc_ifp; + struct ieee80211com *ic = ifp->if_l2com; + + iwn_init(sc); + ieee80211_notify_radio(ic, 1); +} + +static void +iwn_radioon(void *arg0, int pending) +{ + 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 +iwn_sysctlattach(struct iwn_softc *sc) +{ + struct sysctl_ctx_list *ctx = device_get_sysctl_ctx(sc->sc_dev); + struct sysctl_oid *tree = device_get_sysctl_tree(sc->sc_dev); + +#ifdef IWN_DEBUG + sc->sc_debug = 0; + SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, + "debug", CTLFLAG_RW, &sc->sc_debug, 0, "control debugging printfs"); +#endif +} + +#ifdef IWN_DEBUG +static const char * +iwn_intr_str(uint8_t cmd) +{ + switch (cmd) { + /* Notifications */ + case IWN_UC_READY: return "UC_READY"; + case IWN_ADD_NODE_DONE: return "ADD_NODE_DONE"; + case IWN_TX_DONE: return "TX_DONE"; + case IWN_START_SCAN: return "START_SCAN"; + case IWN_STOP_SCAN: return "STOP_SCAN"; + case IWN_RX_STATISTICS: return "RX_STATS"; + case IWN_BEACON_STATISTICS: return "BEACON_STATS"; + case IWN_STATE_CHANGED: return "STATE_CHANGED"; + case IWN_BEACON_MISSED: return "BEACON_MISSED"; + case IWN_AMPDU_RX_START: return "AMPDU_RX_START"; + case IWN_AMPDU_RX_DONE: return "AMPDU_RX_DONE"; + case IWN_RX_DONE: return "RX_DONE"; + + /* Command Notifications */ + case IWN_CMD_CONFIGURE: return "IWN_CMD_CONFIGURE"; + case IWN_CMD_ASSOCIATE: return "IWN_CMD_ASSOCIATE"; + case IWN_CMD_EDCA_PARAMS: return "IWN_CMD_EDCA_PARAMS"; + case IWN_CMD_TSF: return "IWN_CMD_TSF"; + case IWN_CMD_TX_LINK_QUALITY: return "IWN_CMD_TX_LINK_QUALITY"; + case IWN_CMD_SET_LED: return "IWN_CMD_SET_LED"; + case IWN_CMD_SET_POWER_MODE: return "IWN_CMD_SET_POWER_MODE"; + case IWN_CMD_SCAN: return "IWN_CMD_SCAN"; + case IWN_CMD_TXPOWER: return "IWN_CMD_TXPOWER"; + case IWN_CMD_BLUETOOTH: return "IWN_CMD_BLUETOOTH"; + case IWN_CMD_SET_CRITICAL_TEMP: return "IWN_CMD_SET_CRITICAL_TEMP"; + case IWN_SENSITIVITY: return "IWN_SENSITIVITY"; + case IWN_PHY_CALIB: return "IWN_PHY_CALIB"; + } + return "UNKNOWN INTR NOTIF/CMD"; +} +#endif /* IWN_DEBUG */ + +static device_method_t iwn_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, iwn_probe), + DEVMETHOD(device_attach, iwn_attach), + DEVMETHOD(device_detach, iwn_detach), + DEVMETHOD(device_shutdown, iwn_shutdown), + DEVMETHOD(device_suspend, iwn_suspend), + DEVMETHOD(device_resume, iwn_resume), + + { 0, 0 } +}; + +static driver_t iwn_driver = { + "iwn", + iwn_methods, + sizeof (struct iwn_softc) }; +static devclass_t iwn_devclass; +DRIVER_MODULE(iwn, pci, iwn_driver, iwn_devclass, 0, 0); +MODULE_DEPEND(iwn, pci, 1, 1, 1); +MODULE_DEPEND(iwn, firmware, 1, 1, 1); +MODULE_DEPEND(iwn, wlan, 1, 1, 1); +MODULE_DEPEND(iwn, wlan_amrr, 1, 1, 1);