diff options
author | rpaulo <rpaulo@FreeBSD.org> | 2009-12-29 19:47:34 +0000 |
---|---|---|
committer | rpaulo <rpaulo@FreeBSD.org> | 2009-12-29 19:47:34 +0000 |
commit | 91f3d0f75178d899001091cc66ff1f70e1ed7c84 (patch) | |
tree | 58f718e1c13559ab25f526c0531db9fb01671981 /sys/dev/iwn | |
parent | 04f9b92f28cff9016ff2989291b8a6edebbffa2f (diff) | |
download | FreeBSD-src-91f3d0f75178d899001091cc66ff1f70e1ed7c84.zip FreeBSD-src-91f3d0f75178d899001091cc66ff1f70e1ed7c84.tar.gz |
iwn(4) update. Notable changes:
* new firmware
* untested support for 1000 and 6000 series
* bgscan support
* remove unnecessary RXON changes
* allow setting of country/regdomain by enforcing channel flags read
from the EEPROM
* suspend/resume fixes
* RF kill switch fixes
* LED adjustments
* several bus_dma*() related fixes
* addressed some LORs
* many other bug fixes
Submitted by: Bernhard Schmidt <bschmidt at techwires.net>
Obtained from: Brandon Gooch <jamesbrandongooch at gmail dot com> (LED
related changes), Benjamin Kaduk <kaduk at mit dot edu>
(LOR fixes), OpenBSD
Diffstat (limited to 'sys/dev/iwn')
-rw-r--r-- | sys/dev/iwn/if_iwn.c | 1879 | ||||
-rw-r--r-- | sys/dev/iwn/if_iwnreg.h | 260 | ||||
-rw-r--r-- | sys/dev/iwn/if_iwnvar.h | 45 |
3 files changed, 1477 insertions, 707 deletions
diff --git a/sys/dev/iwn/if_iwn.c b/sys/dev/iwn/if_iwn.c index 9d1376b..9e032f7 100644 --- a/sys/dev/iwn/if_iwn.c +++ b/sys/dev/iwn/if_iwn.c @@ -19,8 +19,8 @@ */ /* - * Driver for Intel Wireless WiFi Link 4965 and Intel WiFi Link 5000 Series - * 802.11 network adapters. + * Driver for Intel WiFi Link 4965 and 1000/5000/6000 Series 802.11 network + * adapters. */ #include <sys/cdefs.h> @@ -94,6 +94,8 @@ int iwn_alloc_sched(struct iwn_softc *); void iwn_free_sched(struct iwn_softc *); int iwn_alloc_kw(struct iwn_softc *); void iwn_free_kw(struct iwn_softc *); +int iwn_alloc_ict(struct iwn_softc *); +void iwn_free_ict(struct iwn_softc *); int iwn_alloc_fwmem(struct iwn_softc *); void iwn_free_fwmem(struct iwn_softc *); int iwn_alloc_rx_ring(struct iwn_softc *, struct iwn_rx_ring *); @@ -103,12 +105,15 @@ int iwn_alloc_tx_ring(struct iwn_softc *, struct iwn_tx_ring *, int); void iwn_reset_tx_ring(struct iwn_softc *, struct iwn_tx_ring *); void iwn_free_tx_ring(struct iwn_softc *, struct iwn_tx_ring *); +void iwn5000_ict_reset(struct iwn_softc *); int iwn_read_eeprom(struct iwn_softc *, uint8_t macaddr[IEEE80211_ADDR_LEN]); void iwn4965_read_eeprom(struct iwn_softc *); void iwn4965_print_power_group(struct iwn_softc *, int); void iwn5000_read_eeprom(struct iwn_softc *); -static void iwn_read_eeprom_channels(struct iwn_softc *, uint32_t, int); +static void iwn_read_eeprom_channels(struct iwn_softc *, int, + uint32_t); +void iwn_read_eeprom_enhinfo(struct iwn_softc *); struct ieee80211_node *iwn_node_alloc(struct ieee80211vap *, const uint8_t mac[IEEE80211_ADDR_LEN]); void iwn_newassoc(struct ieee80211_node *, int); @@ -120,6 +125,10 @@ static void iwn_timer_timeout(void *); static void iwn_calib_reset(struct iwn_softc *); void iwn_rx_done(struct iwn_softc *, struct iwn_rx_desc *, struct iwn_rx_data *); +#if 0 /* HT */ +void iwn_rx_compressed_ba(struct iwn_softc *, struct iwn_rx_desc *, + struct iwn_rx_data *); +#endif void iwn5000_rx_calib_results(struct iwn_softc *, struct iwn_rx_desc *, struct iwn_rx_data *); void iwn_rx_statistics(struct iwn_softc *, struct iwn_rx_desc *, @@ -134,7 +143,7 @@ void iwn_cmd_done(struct iwn_softc *, struct iwn_rx_desc *); void iwn_notif_intr(struct iwn_softc *); void iwn_wakeup_intr(struct iwn_softc *); void iwn_rftoggle_intr(struct iwn_softc *); -void iwn_fatal_intr(struct iwn_softc *, uint32_t, uint32_t); +void iwn_fatal_intr(struct iwn_softc *); void iwn_intr(void *); void iwn4965_update_sched(struct iwn_softc *, int, int, uint8_t, uint16_t); @@ -154,19 +163,16 @@ int iwn4965_add_node(struct iwn_softc *, struct iwn_node_info *, int); int iwn5000_add_node(struct iwn_softc *, struct iwn_node_info *, int); -int iwn_set_link_quality(struct iwn_softc *, uint8_t, - const struct ieee80211_channel *, int); -int iwn_add_broadcast_node(struct iwn_softc *, - const struct ieee80211_channel *, int); +int iwn_set_link_quality(struct iwn_softc *, uint8_t, int); +int iwn_add_broadcast_node(struct iwn_softc *, int); int iwn_wme_update(struct ieee80211com *); +static void iwn_update_mcast(struct ifnet *); void iwn_set_led(struct iwn_softc *, uint8_t, uint8_t, uint8_t); int iwn_set_critical_temp(struct iwn_softc *); int iwn_set_timing(struct iwn_softc *, struct ieee80211_node *); void iwn4965_power_calibration(struct iwn_softc *, int); -int iwn4965_set_txpower(struct iwn_softc *, - struct ieee80211_channel *, int); -int iwn5000_set_txpower(struct iwn_softc *, - struct ieee80211_channel *, int); +int iwn4965_set_txpower(struct iwn_softc *, int); +int iwn5000_set_txpower(struct iwn_softc *, int); int iwn4965_get_rssi(struct iwn_softc *, struct iwn_rx_stat *); int iwn5000_get_rssi(struct iwn_softc *, struct iwn_rx_stat *); int iwn_get_noise(const struct iwn_rx_general_stats *); @@ -189,6 +195,7 @@ int iwn_auth(struct iwn_softc *, struct ieee80211vap *vap); int iwn_run(struct iwn_softc *, struct ieee80211vap *vap); int iwn5000_query_calibration(struct iwn_softc *); int iwn5000_send_calibration(struct iwn_softc *); +int iwn5000_send_wimax_coex(struct iwn_softc *); int iwn4965_post_alive(struct iwn_softc *); int iwn5000_post_alive(struct iwn_softc *); int iwn4965_load_bootcode(struct iwn_softc *, const uint8_t *, @@ -198,15 +205,13 @@ int iwn5000_load_firmware_section(struct iwn_softc *, uint32_t, const uint8_t *, int); int iwn5000_load_firmware(struct iwn_softc *); int iwn_read_firmware(struct iwn_softc *); -void iwn_unload_firmware(struct iwn_softc *); int iwn_clock_wait(struct iwn_softc *); -int iwn4965_apm_init(struct iwn_softc *); -int iwn5000_apm_init(struct iwn_softc *); +int iwn_apm_init(struct iwn_softc *); void iwn_apm_stop_master(struct iwn_softc *); void iwn_apm_stop(struct iwn_softc *); int iwn4965_nic_config(struct iwn_softc *); int iwn5000_nic_config(struct iwn_softc *); -int iwn_hw_prepare(struct iwn_softc *sc); +int iwn_hw_prepare(struct iwn_softc *); int iwn_hw_init(struct iwn_softc *); void iwn_hw_stop(struct iwn_softc *); void iwn_init_locked(struct iwn_softc *); @@ -218,6 +223,9 @@ static void iwn_scan_end(struct ieee80211com *); static void iwn_set_channel(struct ieee80211com *); static void iwn_scan_curchan(struct ieee80211_scan_state *, unsigned long); static void iwn_scan_mindwell(struct ieee80211_scan_state *); +static int iwn_setregdomain(struct ieee80211com *, + struct ieee80211_regdomain *, int, + struct ieee80211_channel []); static void iwn_hw_reset(void *, int); static void iwn_radio_on(void *, int); static void iwn_radio_off(void *, int); @@ -284,6 +292,8 @@ static const struct iwn_ident iwn_ident_table [] = { { 0x8086, 0x4239, "Intel(R) PRO/Wireless 6000" }, { 0x8086, 0x422B, "Intel(R) PRO/Wireless 6000" }, { 0x8086, 0x422C, "Intel(R) PRO/Wireless 6000" }, + { 0x8086, 0x0086, "Intel(R) PRO/Wireless 6050" }, + { 0x8086, 0x0087, "Intel(R) PRO/Wireless 6050" }, { 0, 0, NULL } }; @@ -291,7 +301,6 @@ static const struct iwn_hal iwn4965_hal = { iwn4965_load_firmware, iwn4965_read_eeprom, iwn4965_post_alive, - iwn4965_apm_init, iwn4965_nic_config, iwn4965_update_sched, iwn4965_get_temperature, @@ -301,7 +310,10 @@ static const struct iwn_hal iwn4965_hal = { iwn4965_set_gains, iwn4965_add_node, iwn4965_tx_done, - &iwn4965_sensitivity_limits, +#if 0 /* HT */ + iwn4965_ampdu_tx_start, + iwn4965_ampdu_tx_stop, +#endif IWN4965_NTXQUEUES, IWN4965_NDMACHNLS, IWN4965_ID_BROADCAST, @@ -310,14 +322,13 @@ static const struct iwn_hal iwn4965_hal = { IWN4965_FW_TEXT_MAXSZ, IWN4965_FW_DATA_MAXSZ, IWN4965_FWSZ, - IWN4965_SCHED_TXFACT, + IWN4965_SCHED_TXFACT }; static const struct iwn_hal iwn5000_hal = { iwn5000_load_firmware, iwn5000_read_eeprom, iwn5000_post_alive, - iwn5000_apm_init, iwn5000_nic_config, iwn5000_update_sched, iwn5000_get_temperature, @@ -327,7 +338,10 @@ static const struct iwn_hal iwn5000_hal = { iwn5000_set_gains, iwn5000_add_node, iwn5000_tx_done, - &iwn5000_sensitivity_limits, +#if 0 /* HT */ + iwn5000_ampdu_tx_start, + iwn5000_ampdu_tx_stop, +#endif IWN5000_NTXQUEUES, IWN5000_NDMACHNLS, IWN5000_ID_BROADCAST, @@ -336,7 +350,7 @@ static const struct iwn_hal iwn5000_hal = { IWN5000_FW_TEXT_MAXSZ, IWN5000_FW_DATA_MAXSZ, IWN5000_FWSZ, - IWN5000_SCHED_TXFACT, + IWN5000_SCHED_TXFACT }; static int @@ -434,14 +448,6 @@ iwn_attach(device_t dev) goto fail; } - /* Power ON adapter. */ - error = hal->apm_init(sc); - if (error != 0) { - device_printf(dev, "could not power ON adapter, error %d\n", - error); - goto fail; - } - /* Allocate DMA memory for firmware transfers. */ error = iwn_alloc_fwmem(sc); if (error != 0) { @@ -459,6 +465,15 @@ iwn_attach(device_t dev) goto fail; } + /* Allocate ICT table for 5000 Series. */ + if (sc->hw_type != IWN_HW_REV_TYPE_4965 && + (error = iwn_alloc_ict(sc)) != 0) { + device_printf(dev, + "%s: could not allocate ICT table, error %d\n", + __func__, error); + goto fail; + } + /* Allocate TX scheduler "rings". */ error = iwn_alloc_sched(sc); if (error != 0) { @@ -490,8 +505,15 @@ iwn_attach(device_t dev) /* Clear pending interrupts. */ IWN_WRITE(sc, IWN_INT, 0xffffffff); - /* Initialization firmware has not been loaded yet. */ - sc->sc_flags |= IWN_FLAG_FIRST_BOOT; + /* Count the number of available chains. */ + sc->ntxchains = + ((sc->txchainmask >> 2) & 1) + + ((sc->txchainmask >> 1) & 1) + + ((sc->txchainmask >> 0) & 1); + sc->nrxchains = + ((sc->rxchainmask >> 2) & 1) + + ((sc->rxchainmask >> 1) & 1) + + ((sc->rxchainmask >> 0) & 1); ifp = sc->sc_ifp = if_alloc(IFT_IEEE80211); if (ifp == NULL) { @@ -512,13 +534,13 @@ iwn_attach(device_t dev) | IEEE80211_C_SHSLOT /* short slot time supported */ | IEEE80211_C_WPA | IEEE80211_C_SHPREAMBLE /* short preamble supported */ -#if 0 | IEEE80211_C_BGSCAN /* background scanning */ +#if 0 | IEEE80211_C_IBSS /* ibss/adhoc mode */ #endif | IEEE80211_C_WME /* WME */ ; -#if 0 +#if 0 /* HT */ /* XXX disable until HT channel setup works */ ic->ic_htcaps = IEEE80211_HTCAP_SMPS_ENA /* SM PS mode enabled */ @@ -532,6 +554,18 @@ iwn_attach(device_t dev) | IEEE80211_HTC_AMPDU /* tx A-MPDU */ | IEEE80211_HTC_AMSDU /* tx A-MSDU */ ; + + /* Set HT capabilities. */ + ic->ic_htcaps = +#if IWN_RBUF_SIZE == 8192 + IEEE80211_HTCAP_AMSDU7935 | +#endif + IEEE80211_HTCAP_SMPS_DIS | + IEEE80211_HTCAP_CBW20_40 | + IEEE80211_HTCAP_SGI20 | + IEEE80211_HTCAP_SGI40; + if (sc->hw_type != IWN_HW_REV_TYPE_4965) + ic->ic_htcaps |= IEEE80211_HTCAP_GF; #endif /* Read MAC address, channels, etc from EEPROM. */ @@ -542,13 +576,19 @@ iwn_attach(device_t dev) goto fail; } - /* Power OFF adapter. */ - iwn_apm_stop(sc); - device_printf(sc->sc_dev, "MIMO %dT%dR, %.4s, address %6D\n", sc->ntxchains, sc->nrxchains, sc->eeprom_domain, macaddr, ":"); +#if 0 /* HT */ + /* Set supported HT rates. */ + ic->ic_sup_mcs[0] = 0xff; + if (sc->nrxchains > 1) + ic->ic_sup_mcs[1] = 0xff; + if (sc->nrxchains > 2) + ic->ic_sup_mcs[2] = 0xff; +#endif + if_initname(ifp, device_get_name(dev), device_get_unit(dev)); ifp->if_softc = sc; ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; @@ -566,11 +606,19 @@ iwn_attach(device_t dev) ic->ic_node_alloc = iwn_node_alloc; ic->ic_newassoc = iwn_newassoc; ic->ic_wme.wme_update = iwn_wme_update; + ic->ic_update_mcast = iwn_update_mcast; ic->ic_scan_start = iwn_scan_start; ic->ic_scan_end = iwn_scan_end; ic->ic_set_channel = iwn_set_channel; ic->ic_scan_curchan = iwn_scan_curchan; ic->ic_scan_mindwell = iwn_scan_mindwell; + ic->ic_setregdomain = iwn_setregdomain; +#if 0 /* HT */ + ic->ic_ampdu_rx_start = iwn_ampdu_rx_start; + ic->ic_ampdu_rx_stop = iwn_ampdu_rx_stop; + ic->ic_ampdu_tx_start = iwn_ampdu_tx_start; + ic->ic_ampdu_tx_stop = iwn_ampdu_tx_stop; +#endif iwn_radiotap_attach(sc); iwn_sysctlattach(sc); @@ -601,65 +649,63 @@ iwn_hal_attach(struct iwn_softc *sc) switch (sc->hw_type) { case IWN_HW_REV_TYPE_4965: sc->sc_hal = &iwn4965_hal; + sc->limits = &iwn4965_sensitivity_limits; sc->fwname = "iwn4965fw"; - sc->critical_temp = IWN_CTOK(110); - sc->txantmsk = IWN_ANT_A | IWN_ANT_B; - sc->rxantmsk = IWN_ANT_ABC; - sc->ntxchains = 2; - sc->nrxchains = 3; + sc->txchainmask = IWN_ANT_AB; + sc->rxchainmask = IWN_ANT_ABC; break; case IWN_HW_REV_TYPE_5100: sc->sc_hal = &iwn5000_hal; + sc->limits = &iwn5000_sensitivity_limits; sc->fwname = "iwn5000fw"; - sc->critical_temp = 110; - sc->txantmsk = IWN_ANT_B; - sc->rxantmsk = IWN_ANT_A | IWN_ANT_B; - sc->ntxchains = 1; - sc->nrxchains = 2; + sc->txchainmask = IWN_ANT_B; + sc->rxchainmask = IWN_ANT_AB; break; case IWN_HW_REV_TYPE_5150: sc->sc_hal = &iwn5000_hal; + sc->limits = &iwn5150_sensitivity_limits; sc->fwname = "iwn5150fw"; - /* NB: critical temperature will be read from EEPROM. */ - sc->txantmsk = IWN_ANT_A; - sc->rxantmsk = IWN_ANT_A | IWN_ANT_B; - sc->ntxchains = 1; - sc->nrxchains = 2; + sc->txchainmask = IWN_ANT_A; + sc->rxchainmask = IWN_ANT_AB; break; case IWN_HW_REV_TYPE_5300: case IWN_HW_REV_TYPE_5350: sc->sc_hal = &iwn5000_hal; + sc->limits = &iwn5000_sensitivity_limits; sc->fwname = "iwn5000fw"; - sc->critical_temp = 110; - sc->txantmsk = sc->rxantmsk = IWN_ANT_ABC; - sc->ntxchains = sc->nrxchains = 3; + sc->txchainmask = IWN_ANT_ABC; + sc->rxchainmask = IWN_ANT_ABC; break; case IWN_HW_REV_TYPE_1000: sc->sc_hal = &iwn5000_hal; + sc->limits = &iwn5000_sensitivity_limits; sc->fwname = "iwn1000fw"; - sc->critical_temp = 110; - sc->txantmsk = IWN_ANT_A; - sc->rxantmsk = IWN_ANT_A | IWN_ANT_B; - sc->ntxchains = 1; - sc->nrxchains = 2; + sc->txchainmask = IWN_ANT_A; + sc->rxchainmask = IWN_ANT_AB; break; case IWN_HW_REV_TYPE_6000: sc->sc_hal = &iwn5000_hal; + sc->limits = &iwn6000_sensitivity_limits; sc->fwname = "iwn6000fw"; - sc->critical_temp = 110; - sc->txantmsk = IWN_ANT_ABC; - sc->rxantmsk = IWN_ANT_ABC; - sc->ntxchains = 3; - sc->nrxchains = 3; + switch (pci_get_device(sc->sc_dev)) { + case 0x422C: + case 0x4239: + sc->sc_flags |= IWN_FLAG_INTERNAL_PA; + sc->txchainmask = IWN_ANT_BC; + sc->rxchainmask = IWN_ANT_BC; + break; + default: + sc->txchainmask = IWN_ANT_ABC; + sc->rxchainmask = IWN_ANT_ABC; + break; + } break; case IWN_HW_REV_TYPE_6050: sc->sc_hal = &iwn5000_hal; - sc->fwname = "iwn6050fw"; - sc->critical_temp = 110; - sc->txantmsk = IWN_ANT_ABC; - sc->rxantmsk = IWN_ANT_ABC; - sc->ntxchains = 3; - sc->nrxchains = 3; + sc->limits = &iwn6000_sensitivity_limits; + sc->fwname = "iwn6000fw"; + sc->txchainmask = IWN_ANT_AB; + sc->rxchainmask = IWN_ANT_AB; break; default: device_printf(sc->sc_dev, "adapter type %d not supported\n", @@ -749,16 +795,15 @@ iwn_cleanup(device_t dev) ieee80211_ifdetach(ic); } - iwn_unload_firmware(sc); - + /* Free DMA resources. */ iwn_free_rx_ring(sc, &sc->rxq); - if (sc->sc_hal != NULL) for (i = 0; i < sc->sc_hal->ntxqs; i++) iwn_free_tx_ring(sc, &sc->txq[i]); - iwn_free_sched(sc); iwn_free_kw(sc); + if (sc->ict != NULL) + iwn_free_ict(sc); iwn_free_fwmem(sc); if (sc->irq != NULL) { @@ -814,6 +859,7 @@ static __inline uint32_t iwn_prph_read(struct iwn_softc *sc, uint32_t addr) { IWN_WRITE(sc, IWN_PRPH_RADDR, IWN_PRPH_DWORD | addr); + IWN_BARRIER_READ_WRITE(sc); return IWN_READ(sc, IWN_PRPH_RDATA); } @@ -821,6 +867,7 @@ static __inline void iwn_prph_write(struct iwn_softc *sc, uint32_t addr, uint32_t data) { IWN_WRITE(sc, IWN_PRPH_WADDR, IWN_PRPH_DWORD | addr); + IWN_BARRIER_WRITE(sc); IWN_WRITE(sc, IWN_PRPH_WDATA, data); } @@ -848,6 +895,7 @@ static __inline uint32_t iwn_mem_read(struct iwn_softc *sc, uint32_t addr) { IWN_WRITE(sc, IWN_MEM_RADDR, addr); + IWN_BARRIER_READ_WRITE(sc); return IWN_READ(sc, IWN_MEM_RDATA); } @@ -855,6 +903,7 @@ static __inline void iwn_mem_write(struct iwn_softc *sc, uint32_t addr, uint32_t data) { IWN_WRITE(sc, IWN_MEM_WADDR, addr); + IWN_BARRIER_WRITE(sc); IWN_WRITE(sc, IWN_MEM_WDATA, data); } @@ -921,8 +970,11 @@ iwn_eeprom_unlock(struct iwn_softc *sc) int iwn_init_otprom(struct iwn_softc *sc) { - int error; + uint32_t base; + uint16_t next; + int count, error; + /* Wait for clock stabilization before accessing prph. */ error = iwn_clock_wait(sc); if (error != 0) return error; @@ -935,11 +987,37 @@ iwn_init_otprom(struct iwn_softc *sc) iwn_prph_clrbits(sc, IWN_APMG_PS, IWN_APMG_PS_RESET_REQ); iwn_nic_unlock(sc); + /* Set auto clock gate disable bit for HW with OTP shadow RAM. */ + if (sc->hw_type != IWN_HW_REV_TYPE_1000) { + IWN_SETBITS(sc, IWN_DBG_LINK_PWR_MGMT, + IWN_RESET_LINK_PWR_MGMT_DIS); + } IWN_CLRBITS(sc, IWN_EEPROM_GP, IWN_EEPROM_GP_IF_OWNER); /* Clear ECC status. */ IWN_SETBITS(sc, IWN_OTP_GP, IWN_OTP_GP_ECC_CORR_STTS | IWN_OTP_GP_ECC_UNCORR_STTS); + /* + * Find last valid OTP block (contains the EEPROM image) for HW + * without OTP shadow RAM. + */ + if (sc->hw_type == IWN_HW_REV_TYPE_1000) { + /* Switch to absolute addressing mode. */ + IWN_CLRBITS(sc, IWN_OTP_GP, IWN_OTP_GP_RELATIVE_ACCESS); + base = 0; + for (count = 0; count < IWN1000_OTP_NBLOCKS; count++) { + error = iwn_read_prom_data(sc, base, &next, 2); + if (error != 0) + return error; + if (next == 0) /* End of linked-list. */ + break; + base = le16toh(next); + } + if (base == 0 || count == IWN1000_OTP_NBLOCKS) + return EIO; + /* Skip "next" word. */ + sc->prom_base = base + 1; + } return 0; } @@ -950,15 +1028,16 @@ iwn_read_prom_data(struct iwn_softc *sc, uint32_t addr, void *data, int count) int ntries; uint8_t *out = data; + addr += sc->prom_base; for (; count > 0; count -= 2, addr++) { IWN_WRITE(sc, IWN_EEPROM, addr << 2); - for (ntries = 0; ntries < 100; ntries++) { + for (ntries = 0; ntries < 10; ntries++) { val = IWN_READ(sc, IWN_EEPROM); if (val & IWN_EEPROM_READ_VALID) break; DELAY(5); } - if (ntries == 100) { + if (ntries == 10) { device_printf(sc->sc_dev, "timeout reading ROM at 0x%x\n", addr); return ETIMEDOUT; @@ -1035,7 +1114,7 @@ fail: return error; } -static void +void iwn_dma_contig_free(struct iwn_dma_info *dma) { if (dma->tag != NULL) { @@ -1080,6 +1159,20 @@ iwn_free_kw(struct iwn_softc *sc) } int +iwn_alloc_ict(struct iwn_softc *sc) +{ + /* ICT table must be aligned on a 4KB boundary. */ + return iwn_dma_contig_alloc(sc, &sc->ict_dma, + (void **)&sc->ict, IWN_ICT_SIZE, 4096, BUS_DMA_NOWAIT); +} + +void +iwn_free_ict(struct iwn_softc *sc) +{ + iwn_dma_contig_free(&sc->ict_dma); +} + +int iwn_alloc_fwmem(struct iwn_softc *sc) { /* Must be aligned on a 16-byte boundary. */ @@ -1115,7 +1208,7 @@ iwn_alloc_rx_ring(struct iwn_softc *sc, struct iwn_rx_ring *ring) error = bus_dma_tag_create(bus_get_dma_tag(sc->sc_dev), 1, 0, BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, MJUMPAGESIZE, 1, - MJUMPAGESIZE, BUS_DMA_NOWAIT, NULL, NULL, &ring->desc_dma.tag); + MJUMPAGESIZE, BUS_DMA_NOWAIT, NULL, NULL, &ring->data_dmat); if (error != 0) { device_printf(sc->sc_dev, "%s: bus_dma_tag_create_failed, error %d\n", @@ -1134,17 +1227,6 @@ iwn_alloc_rx_ring(struct iwn_softc *sc, struct iwn_rx_ring *ring) goto fail; } - error = bus_dma_tag_create(bus_get_dma_tag(sc->sc_dev), 1, 0, - BUS_SPACE_MAXADDR_32BIT, - BUS_SPACE_MAXADDR, NULL, NULL, MJUMPAGESIZE, 1, - MJUMPAGESIZE, BUS_DMA_NOWAIT, NULL, NULL, &ring->desc_dma.tag); - if (error != 0) { - device_printf(sc->sc_dev, - "%s: bus_dma_tag_create_failed, error %d\n", - __func__, error); - goto fail; - } - /* * Allocate and map RX buffers. */ @@ -1152,7 +1234,7 @@ iwn_alloc_rx_ring(struct iwn_softc *sc, struct iwn_rx_ring *ring) struct iwn_rx_data *data = &ring->data[i]; bus_addr_t paddr; - error = bus_dmamap_create(ring->desc_dma.tag, 0, &data->map); + error = bus_dmamap_create(ring->data_dmat, 0, &data->map); if (error != 0) { device_printf(sc->sc_dev, "%s: bus_dmamap_create failed, error %d\n", @@ -1169,7 +1251,7 @@ iwn_alloc_rx_ring(struct iwn_softc *sc, struct iwn_rx_ring *ring) } /* Map page. */ - error = bus_dmamap_load(ring->desc_dma.tag, data->map, + error = bus_dmamap_load(ring->data_dmat, data->map, mtod(data->m, caddr_t), MJUMPAGESIZE, iwn_dma_map_addr, &paddr, BUS_DMA_NOWAIT); if (error != 0 && error != EFBIG) { @@ -1180,6 +1262,8 @@ iwn_alloc_rx_ring(struct iwn_softc *sc, struct iwn_rx_ring *ring) error = ENOMEM; /* XXX unique code */ goto fail; } + bus_dmamap_sync(ring->data_dmat, data->map, + BUS_DMASYNC_PREWRITE); /* Set physical address of RX buffer (256-byte aligned). */ ring->desc[i] = htole32(paddr >> 8); @@ -1228,11 +1312,13 @@ iwn_free_rx_ring(struct iwn_softc *sc, struct iwn_rx_ring *ring) struct iwn_rx_data *data = &ring->data[i]; if (data->m != NULL) { - bus_dmamap_sync(ring->desc_dma.tag, data->map, + bus_dmamap_sync(ring->data_dmat, data->map, BUS_DMASYNC_POSTREAD); - bus_dmamap_unload(ring->desc_dma.tag, data->map); + bus_dmamap_unload(ring->data_dmat, data->map); m_freem(data->m); } + if (data->map != NULL) + bus_dmamap_destroy(ring->data_dmat, data->map); } } @@ -1278,7 +1364,7 @@ iwn_alloc_tx_ring(struct iwn_softc *sc, struct iwn_tx_ring *ring, int qid) error = bus_dma_tag_create(bus_get_dma_tag(sc->sc_dev), 1, 0, BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, MCLBYTES, IWN_MAX_SCATTER - 1, - MCLBYTES, BUS_DMA_NOWAIT, NULL, NULL, &ring->desc_dma.tag); + MCLBYTES, BUS_DMA_NOWAIT, NULL, NULL, &ring->data_dmat); if (error != 0) { device_printf(sc->sc_dev, "%s: bus_dma_tag_create_failed, error %d\n", @@ -1294,13 +1380,15 @@ iwn_alloc_tx_ring(struct iwn_softc *sc, struct iwn_tx_ring *ring, int qid) data->scratch_paddr = paddr + 12; paddr += sizeof (struct iwn_tx_cmd); - error = bus_dmamap_create(ring->desc_dma.tag, 0, &data->map); + error = bus_dmamap_create(ring->data_dmat, 0, &data->map); if (error != 0) { device_printf(sc->sc_dev, "%s: bus_dmamap_create failed, error %d\n", __func__, error); goto fail; } + bus_dmamap_sync(ring->data_dmat, data->map, + BUS_DMASYNC_PREWRITE); } return 0; fail: @@ -1317,9 +1405,7 @@ iwn_reset_tx_ring(struct iwn_softc *sc, struct iwn_tx_ring *ring) struct iwn_tx_data *data = &ring->data[i]; if (data->m != NULL) { - bus_dmamap_sync(ring->desc_dma.tag, data->map, - BUS_DMASYNC_POSTWRITE); - bus_dmamap_unload(ring->desc_dma.tag, data->map); + bus_dmamap_unload(ring->data_dmat, data->map); m_freem(data->m); data->m = NULL; } @@ -1341,21 +1427,45 @@ iwn_free_tx_ring(struct iwn_softc *sc, struct iwn_tx_ring *ring) iwn_dma_contig_free(&ring->desc_dma); iwn_dma_contig_free(&ring->cmd_dma); - if (ring->data != NULL) { - for (i = 0; i < IWN_TX_RING_COUNT; i++) { - struct iwn_tx_data *data = &ring->data[i]; + for (i = 0; i < IWN_TX_RING_COUNT; i++) { + struct iwn_tx_data *data = &ring->data[i]; - if (data->m != NULL) { - bus_dmamap_sync(ring->desc_dma.tag, data->map, - BUS_DMASYNC_POSTWRITE); - bus_dmamap_unload(ring->desc_dma.tag, - data->map); - m_freem(data->m); - } + if (data->m != NULL) { + bus_dmamap_sync(ring->data_dmat, data->map, + BUS_DMASYNC_POSTWRITE); + bus_dmamap_unload(ring->data_dmat, data->map); + m_freem(data->m); } + if (data->map != NULL) + bus_dmamap_destroy(ring->data_dmat, data->map); } } +void +iwn5000_ict_reset(struct iwn_softc *sc) +{ + /* Disable interrupts. */ + IWN_WRITE(sc, IWN_INT_MASK, 0); + + /* Reset ICT table. */ + memset(sc->ict, 0, IWN_ICT_SIZE); + sc->ict_cur = 0; + + /* Set physical address of ICT table (4KB aligned.) */ + DPRINTF(sc, IWN_DEBUG_RESET, "%s: enabling ICT\n", __func__); + IWN_WRITE(sc, IWN_DRAM_INT_TBL, IWN_DRAM_INT_TBL_ENABLE | + IWN_DRAM_INT_TBL_WRAP_CHECK | sc->ict_dma.paddr >> 12); + + /* Enable periodic RX interrupt. */ + sc->int_mask |= IWN_INT_RX_PERIODIC; + /* Switch to ICT interrupt mode in driver. */ + sc->sc_flags |= IWN_FLAG_USE_ICT; + + /* Re-enable interrupts. */ + IWN_WRITE(sc, IWN_INT, 0xffffffff); + IWN_WRITE(sc, IWN_INT_MASK, sc->int_mask); +} + int iwn_read_eeprom(struct iwn_softc *sc, uint8_t macaddr[IEEE80211_ADDR_LEN]) { @@ -1370,6 +1480,15 @@ iwn_read_eeprom(struct iwn_softc *sc, uint8_t macaddr[IEEE80211_ADDR_LEN]) DPRINTF(sc, IWN_DEBUG_RESET, "%s found\n", (sc->sc_flags & IWN_FLAG_HAS_OTPROM) ? "OTPROM" : "EEPROM"); + /* Adapter has to be powered on for EEPROM access to work. */ + error = iwn_apm_init(sc); + if (error != 0) { + device_printf(sc->sc_dev, + "%s: could not power ON adapter, error %d\n", + __func__, error); + return error; + } + if ((IWN_READ(sc, IWN_EEPROM_GP) & 0x7) == 0) { device_printf(sc->sc_dev, "%s: bad ROM signature\n", __func__); return EIO; @@ -1382,12 +1501,14 @@ iwn_read_eeprom(struct iwn_softc *sc, uint8_t macaddr[IEEE80211_ADDR_LEN]) return error; } - if ((sc->sc_flags & IWN_FLAG_HAS_OTPROM) && - ((error = iwn_init_otprom(sc)) != 0)) { - device_printf(sc->sc_dev, - "%s: could not initialize OTPROM, error %d\n", - __func__, error); - return error; + if (sc->sc_flags & IWN_FLAG_HAS_OTPROM) { + error = iwn_init_otprom(sc); + if (error != 0) { + device_printf(sc->sc_dev, + "%s: could not initialize OTPROM, error %d\n", + __func__, error); + return error; + } } iwn_read_prom_data(sc, IWN_EEPROM_RFCFG, &val, 2); @@ -1400,6 +1521,8 @@ iwn_read_eeprom(struct iwn_softc *sc, uint8_t macaddr[IEEE80211_ADDR_LEN]) /* Read adapter-specific information from EEPROM. */ hal->read_eeprom(sc); + iwn_apm_stop(sc); /* Power OFF adapter. */ + iwn_eeprom_unlock(sc); return 0; } @@ -1407,15 +1530,18 @@ iwn_read_eeprom(struct iwn_softc *sc, uint8_t macaddr[IEEE80211_ADDR_LEN]) void iwn4965_read_eeprom(struct iwn_softc *sc) { + uint32_t addr; int i; uint16_t val; /* Read regulatory domain (4 ASCII characters.) */ iwn_read_prom_data(sc, IWN4965_EEPROM_DOMAIN, sc->eeprom_domain, 4); - /* Read the list of authorized channels. */ - for (i = 0; i < 7; i++) - iwn_read_eeprom_channels(sc, iwn4965_regulatory_bands[i], i); + /* Read the list of authorized channels (20MHz ones only.) */ + for (i = 0; i < 5; i++) { + addr = iwn4965_regulatory_bands[i]; + iwn_read_eeprom_channels(sc, i, addr); + } /* Read maximum allowed TX power for 2GHz and 5GHz bands. */ iwn_read_prom_data(sc, IWN4965_EEPROM_MAXPOW, &val, 2); @@ -1441,7 +1567,7 @@ iwn4965_read_eeprom(struct iwn_softc *sc) #ifdef IWN_DEBUG /* Print samples. */ - if (sc->sc_debug & IWN_DEBUG_ANY || 1) { + if (sc->sc_debug & IWN_DEBUG_ANY) { for (i = 0; i < IWN_NBANDS; i++) iwn4965_print_power_group(sc, i); } @@ -1486,7 +1612,7 @@ iwn4965_print_power_group(struct iwn_softc *sc, int i) void iwn5000_read_eeprom(struct iwn_softc *sc) { - int32_t temp, volt, delta; + int32_t temp, volt; uint32_t addr, base; int i; uint16_t val; @@ -1497,24 +1623,27 @@ iwn5000_read_eeprom(struct iwn_softc *sc) iwn_read_prom_data(sc, base + IWN5000_EEPROM_DOMAIN, sc->eeprom_domain, 4); - /* Read the list of authorized channels. */ - for (i = 0; i < 7; i++) { + /* Read the list of authorized channels (20MHz ones only.) */ + for (i = 0; i < 5; i++) { addr = base + iwn5000_regulatory_bands[i]; - iwn_read_eeprom_channels(sc, addr, i); + iwn_read_eeprom_channels(sc, i, addr); } + /* Read enhanced TX power information for 6000 Series. */ + if (sc->hw_type >= IWN_HW_REV_TYPE_6000) + iwn_read_eeprom_enhinfo(sc); + iwn_read_prom_data(sc, IWN5000_EEPROM_CAL, &val, 2); base = le16toh(val); if (sc->hw_type == IWN_HW_REV_TYPE_5150) { - /* Compute critical temperature (in Kelvin.) */ + /* Compute temperature offset. */ iwn_read_prom_data(sc, base + IWN5000_EEPROM_TEMP, &val, 2); temp = le16toh(val); iwn_read_prom_data(sc, base + IWN5000_EEPROM_VOLT, &val, 2); volt = le16toh(val); - delta = temp - (volt / -5); - sc->critical_temp = (IWN_CTOK(110) - delta) * -5; - DPRINTF(sc, IWN_DEBUG_CALIBRATE, "temp=%d volt=%d delta=%dK\n", - temp, volt, delta); + sc->temp_off = temp - (volt / -5); + DPRINTF(sc, IWN_DEBUG_CALIBRATE, "temp=%d volt=%d offset=%dK\n", + temp, volt, sc->temp_off); } else { /* Read crystal calibration. */ iwn_read_prom_data(sc, base + IWN5000_EEPROM_CRYSTAL, @@ -1524,19 +1653,38 @@ iwn5000_read_eeprom(struct iwn_softc *sc) } } +/* + * Translate EEPROM flags to net80211. + */ +static uint32_t +iwn_eeprom_channel_flags(struct iwn_eeprom_chan *channel) +{ + uint32_t nflags; + + nflags = 0; + if ((channel->flags & IWN_EEPROM_CHAN_ACTIVE) == 0) + nflags |= IEEE80211_CHAN_PASSIVE; + if ((channel->flags & IWN_EEPROM_CHAN_IBSS) == 0) + nflags |= IEEE80211_CHAN_NOADHOC; + if (channel->flags & IWN_EEPROM_CHAN_RADAR) { + nflags |= IEEE80211_CHAN_DFS; + /* XXX apparently IBSS may still be marked */ + nflags |= IEEE80211_CHAN_NOADHOC; + } + + return nflags; +} + static void -iwn_read_eeprom_band(struct iwn_softc *sc, const struct iwn_chan_band *band, - uint32_t flags, uint32_t addr) +iwn_read_eeprom_band(struct iwn_softc *sc, int n) { struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; - struct iwn_eeprom_chan channels[IWN_MAX_CHAN_PER_BAND]; + struct iwn_eeprom_chan *channels = sc->eeprom_channels[n]; + const struct iwn_chan_band *band = &iwn_bands[n]; struct ieee80211_channel *c; int i, chan, nflags; - iwn_read_prom_data(sc, addr, channels, - band->nchan * sizeof (struct iwn_eeprom_chan)); - for (i = 0; i < band->nchan; i++) { if (!(channels[i].flags & IWN_EEPROM_CHAN_VALID)) { DPRINTF(sc, IWN_DEBUG_RESET, @@ -1546,18 +1694,7 @@ iwn_read_eeprom_band(struct iwn_softc *sc, const struct iwn_chan_band *band, continue; } chan = band->chan[i]; - - /* Translate EEPROM flags to net80211 */ - nflags = 0; - if ((channels[i].flags & IWN_EEPROM_CHAN_ACTIVE) == 0) - nflags |= IEEE80211_CHAN_PASSIVE; - if ((channels[i].flags & IWN_EEPROM_CHAN_IBSS) == 0) - nflags |= IEEE80211_CHAN_NOADHOC; - if (channels[i].flags & IWN_EEPROM_CHAN_RADAR) { - nflags |= IEEE80211_CHAN_DFS; - /* XXX apparently IBSS may still be marked */ - nflags |= IEEE80211_CHAN_NOADHOC; - } + nflags = iwn_eeprom_channel_flags(&channels[i]); DPRINTF(sc, IWN_DEBUG_RESET, "add chan %d flags 0x%x maxpwr %d\n", @@ -1565,10 +1702,12 @@ iwn_read_eeprom_band(struct iwn_softc *sc, const struct iwn_chan_band *band, c = &ic->ic_channels[ic->ic_nchans++]; c->ic_ieee = chan; - c->ic_freq = ieee80211_ieee2mhz(chan, flags); c->ic_maxregpower = channels[i].maxpwr; c->ic_maxpower = 2*c->ic_maxregpower; - if (flags & IEEE80211_CHAN_2GHZ) { + if (n == 0) { /* 2GHz band */ + c->ic_freq = ieee80211_ieee2mhz(chan, + IEEE80211_CHAN_G); + /* G =>'s B is supported */ c->ic_flags = IEEE80211_CHAN_B | nflags; @@ -1576,31 +1715,33 @@ iwn_read_eeprom_band(struct iwn_softc *sc, const struct iwn_chan_band *band, c[0] = c[-1]; c->ic_flags = IEEE80211_CHAN_G | nflags; } else { /* 5GHz band */ + c->ic_freq = ieee80211_ieee2mhz(chan, + IEEE80211_CHAN_A); c->ic_flags = IEEE80211_CHAN_A | nflags; sc->sc_flags |= IWN_FLAG_HAS_5GHZ; } +#if 0 /* HT */ /* 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? */ +#endif } } +#if 0 /* HT */ static void -iwn_read_eeprom_ht40(struct iwn_softc *sc, const struct iwn_chan_band *band, - uint32_t flags, uint32_t addr) +iwn_read_eeprom_ht40(struct iwn_softc *sc, int n) { struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; - struct iwn_eeprom_chan channels[IWN_MAX_CHAN_PER_BAND]; + struct iwn_eeprom_chan *channels = sc->eeprom_channels[n]; + const struct iwn_chan_band *band = &iwn_bands[n]; struct ieee80211_channel *c, *cent, *extc; int i; - iwn_read_prom_data(sc, addr, channels, - band->nchan * sizeof (struct iwn_eeprom_chan)); - for (i = 0; i < band->nchan; i++) { if (!(channels[i].flags & IWN_EEPROM_CHAN_VALID) || !(channels[i].flags & IWN_EEPROM_CHAN_WIDE)) { @@ -1615,7 +1756,7 @@ iwn_read_eeprom_ht40(struct iwn_softc *sc, const struct iwn_chan_band *band, * center channel, then the extension channel above. */ cent = ieee80211_find_channel_byieee(ic, band->chan[i], - flags & ~IEEE80211_CHAN_HT); + band->flags & ~IEEE80211_CHAN_HT); if (cent == NULL) { /* XXX shouldn't happen */ device_printf(sc->sc_dev, "%s: no entry for channel %d\n", @@ -1623,7 +1764,7 @@ iwn_read_eeprom_ht40(struct iwn_softc *sc, const struct iwn_chan_band *band, continue; } extc = ieee80211_find_channel(ic, cent->ic_freq+20, - flags & ~IEEE80211_CHAN_HT); + band->flags & ~IEEE80211_CHAN_HT); if (extc == NULL) { DPRINTF(sc, IWN_DEBUG_RESET, "skip chan %d, extension channel not found\n", @@ -1647,29 +1788,65 @@ iwn_read_eeprom_ht40(struct iwn_softc *sc, const struct iwn_chan_band *band, c->ic_flags |= IEEE80211_CHAN_HT40D; } } +#endif static void -iwn_read_eeprom_channels(struct iwn_softc *sc, uint32_t addr, int n) +iwn_read_eeprom_channels(struct iwn_softc *sc, int n, uint32_t addr) { struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; - static const uint32_t iwnband_flags[] = { - IEEE80211_CHAN_G, - IEEE80211_CHAN_A, - IEEE80211_CHAN_A, - IEEE80211_CHAN_A, - IEEE80211_CHAN_A, - IEEE80211_CHAN_G | IEEE80211_CHAN_HT40, - IEEE80211_CHAN_A | IEEE80211_CHAN_HT40 - }; + + iwn_read_prom_data(sc, addr, &sc->eeprom_channels[n], + iwn_bands[n].nchan * sizeof (struct iwn_eeprom_chan)); if (n < 5) - iwn_read_eeprom_band(sc, &iwn_bands[n], iwnband_flags[n], addr); + iwn_read_eeprom_band(sc, n); +#if 0 /* HT */ else - iwn_read_eeprom_ht40(sc, &iwn_bands[n], iwnband_flags[n], addr); + iwn_read_eeprom_ht40(sc, n); +#endif ieee80211_sort_channels(ic->ic_channels, ic->ic_nchans); } +#define nitems(_a) (sizeof((_a)) / sizeof((_a)[0])) + +void +iwn_read_eeprom_enhinfo(struct iwn_softc *sc) +{ + struct iwn_eeprom_enhinfo enhinfo[35]; + uint16_t val, base; + int8_t maxpwr; + int i; + + iwn_read_prom_data(sc, IWN5000_EEPROM_REG, &val, 2); + base = le16toh(val); + iwn_read_prom_data(sc, base + IWN6000_EEPROM_ENHINFO, + enhinfo, sizeof enhinfo); + + memset(sc->enh_maxpwr, 0, sizeof sc->enh_maxpwr); + for (i = 0; i < nitems(enhinfo); i++) { + if (enhinfo[i].chan == 0 || enhinfo[i].reserved != 0) + continue; /* Skip invalid entries. */ + + maxpwr = 0; + if (sc->txchainmask & IWN_ANT_A) + maxpwr = MAX(maxpwr, enhinfo[i].chain[0]); + if (sc->txchainmask & IWN_ANT_B) + maxpwr = MAX(maxpwr, enhinfo[i].chain[1]); + if (sc->txchainmask & IWN_ANT_C) + maxpwr = MAX(maxpwr, enhinfo[i].chain[2]); + if (sc->ntxchains == 2) + maxpwr = MAX(maxpwr, enhinfo[i].mimo2); + else if (sc->ntxchains == 3) + maxpwr = MAX(maxpwr, enhinfo[i].mimo3); + maxpwr /= 2; /* Convert half-dBm to dBm. */ + + DPRINTF(sc, IWN_DEBUG_RESET, "enhinfo %d, maxpwr=%d\n", i, + maxpwr); + sc->enh_maxpwr[i] = maxpwr; + } +} + struct ieee80211_node * iwn_node_alloc(struct ieee80211vap *vap, const uint8_t mac[IEEE80211_ADDR_LEN]) { @@ -1680,9 +1857,10 @@ void iwn_newassoc(struct ieee80211_node *ni, int isnew) { struct ieee80211vap *vap = ni->ni_vap; + struct iwn_node *wn = (void *)ni; ieee80211_amrr_node_init(&IWN_VAP(vap)->iv_amrr, - &IWN_NODE(ni)->amn, ni); + &wn->amn, ni); } int @@ -1757,13 +1935,15 @@ static void iwn_timer_timeout(void *arg) { struct iwn_softc *sc = arg; + uint32_t flags = 0; IWN_LOCK_ASSERT(sc); if (sc->calib_cnt && --sc->calib_cnt == 0) { DPRINTF(sc, IWN_DEBUG_CALIBRATE, "%s\n", "send statistics request"); - (void) iwn_cmd(sc, IWN_CMD_GET_STATISTICS, NULL, 0, 1); + (void) iwn_cmd(sc, IWN_CMD_GET_STATISTICS, &flags, + sizeof flags, 1); sc->calib_cnt = 60; /* do calibration every 60s */ } iwn_watchdog(sc); /* NB: piggyback tx watchdog */ @@ -1777,30 +1957,6 @@ iwn_calib_reset(struct iwn_softc *sc) sc->calib_cnt = 60; /* do calibration every 60s */ } -static __inline int -maprate(int iwnrate) -{ - switch (iwnrate) { - /* CCK rates */ - case 10: return 2; - case 20: return 4; - case 55: return 11; - case 110: return 22; - /* OFDM rates */ - case 0xd: return 12; - case 0xf: return 18; - case 0x5: return 24; - case 0x7: return 36; - case 0x9: return 48; - case 0xb: return 72; - case 0x1: return 96; - case 0x3: return 108; - /* XXX MCS */ - } - /* unknown rate: should not happen */ - return 0; -} - /* * Process an RX_DONE (4965AGN only) or MPDU_RX_DONE firmware notification. * Each MPDU_RX_DONE notification must be preceded by an RX_PHY one. @@ -1826,7 +1982,7 @@ iwn_rx_done(struct iwn_softc *sc, struct iwn_rx_desc *desc, /* Check for prior RX_PHY notification. */ if (!sc->last_rx_valid) { DPRINTF(sc, IWN_DEBUG_ANY, - "%s: missing AMPDU_RX_START\n", __func__); + "%s: missing RX_PHY\n", __func__); ifp->if_ierrors++; return; } @@ -1835,7 +1991,7 @@ iwn_rx_done(struct iwn_softc *sc, struct iwn_rx_desc *desc, } else stat = (struct iwn_rx_stat *)(desc + 1); - bus_dmamap_sync(ring->desc_dma.tag, data->map, BUS_DMASYNC_POSTREAD); + bus_dmamap_sync(ring->data_dmat, data->map, BUS_DMASYNC_POSTREAD); if (stat->cfg_phy_len > IWN_STAT_MAXLEN) { device_printf(sc->sc_dev, @@ -1878,7 +2034,9 @@ iwn_rx_done(struct iwn_softc *sc, struct iwn_rx_desc *desc, ifp->if_ierrors++; return; } - error = bus_dmamap_load(ring->desc_dma.tag, data->map, + bus_dmamap_unload(ring->data_dmat, data->map); + + error = bus_dmamap_load(ring->data_dmat, data->map, mtod(m1, caddr_t), MJUMPAGESIZE, iwn_dma_map_addr, &paddr, BUS_DMA_NOWAIT); if (error != 0 && error != EFBIG) { @@ -1893,7 +2051,8 @@ iwn_rx_done(struct iwn_softc *sc, struct iwn_rx_desc *desc, data->m = m1; /* Update RX descriptor. */ ring->desc[ring->cur] = htole32(paddr >> 8); - bus_dmamap_sync(ring->desc_dma.tag, data->map, BUS_DMASYNC_PREWRITE); + bus_dmamap_sync(ring->desc_dma.tag, ring->desc_dma.map, + BUS_DMASYNC_PREWRITE); /* Finalize mbuf. */ m->m_pkthdr.rcvif = ifp; @@ -1913,9 +2072,26 @@ iwn_rx_done(struct iwn_softc *sc, struct iwn_rx_desc *desc, tap->wr_tsft = htole64(stat->tstamp); tap->wr_flags = 0; - if (stat->flags & htole16(IWN_RXON_SHPREAMBLE)) + if (stat->flags & htole16(IWN_STAT_FLAG_SHPREAMBLE)) tap->wr_flags |= IEEE80211_RADIOTAP_F_SHORTPRE; - tap->wr_rate = maprate(stat->rate); + switch (stat->rate) { + /* CCK rates. */ + case 10: tap->wr_rate = 2; break; + case 20: tap->wr_rate = 4; break; + case 55: tap->wr_rate = 11; break; + case 110: tap->wr_rate = 22; break; + /* OFDM rates. */ + case 0xd: tap->wr_rate = 12; break; + case 0xf: tap->wr_rate = 18; break; + case 0x5: tap->wr_rate = 24; break; + case 0x7: tap->wr_rate = 36; break; + case 0x9: tap->wr_rate = 48; break; + case 0xb: tap->wr_rate = 72; break; + case 0x1: tap->wr_rate = 96; break; + case 0x3: tap->wr_rate = 108; break; + /* Unknown rate: should not happen. */ + default: tap->wr_rate = 0; + } tap->wr_dbm_antsignal = rssi; tap->wr_dbm_antnoise = nf; } @@ -1933,6 +2109,20 @@ iwn_rx_done(struct iwn_softc *sc, struct iwn_rx_desc *desc, IWN_LOCK(sc); } +#if 0 /* HT */ +/* Process an incoming Compressed BlockAck. */ +void +iwn_rx_compressed_ba(struct iwn_softc *sc, struct iwn_rx_desc *desc, + struct iwn_rx_data *data) +{ + struct iwn_compressed_ba *ba = (struct iwn_compressed_ba *)(desc + 1); + struct iwn_tx_ring *txq; + + txq = &sc->txq[letoh16(ba->qid)]; + /* XXX TBD */ +} +#endif + /* * Process a CALIBRATION_RESULT notification sent by the initialization * firmware on response to a CMD_CALIB_CONFIG command (5000 only.) @@ -1945,7 +2135,7 @@ iwn5000_rx_calib_results(struct iwn_softc *sc, struct iwn_rx_desc *desc, int len, idx = -1; /* Runtime firmware should not send such a notification. */ - if (!(sc->sc_flags & IWN_FLAG_FIRST_BOOT)) + if (sc->sc_flags & IWN_FLAG_CALIB_DONE) return; len = (le32toh(desc->len) & 0x3fff) - 4; @@ -1961,8 +2151,9 @@ iwn5000_rx_calib_results(struct iwn_softc *sc, struct iwn_rx_desc *desc, case IWN5000_PHY_CALIB_TX_IQ: idx = 2; break; - case IWN5000_PHY_CALIB_TX_IQ_PERD: - if (sc->hw_type != IWN_HW_REV_TYPE_5150) + case IWN5000_PHY_CALIB_TX_IQ_PERIODIC: + if (sc->hw_type < IWN_HW_REV_TYPE_6000 && + sc->hw_type != IWN_HW_REV_TYPE_5150) idx = 3; break; case IWN5000_PHY_CALIB_BASE_BAND: @@ -2056,10 +2247,11 @@ iwn4965_tx_done(struct iwn_softc *sc, struct iwn_rx_desc *desc, DPRINTF(sc, IWN_DEBUG_XMIT, "%s: " "qid %d idx %d retries %d nkill %d rate %x duration %d status %x\n", - __func__, desc->qid, desc->idx, stat->retrycnt, - stat->killcnt, stat->rate, le16toh(stat->duration), + __func__, desc->qid, desc->idx, stat->ackfailcnt, + stat->btkillcnt, stat->rate, le16toh(stat->duration), le32toh(stat->status)); - iwn_tx_done(sc, desc, stat->retrycnt, le32toh(stat->status) & 0xff); + + iwn_tx_done(sc, desc, stat->ackfailcnt, le32toh(stat->status) & 0xff); } void @@ -2070,33 +2262,36 @@ iwn5000_tx_done(struct iwn_softc *sc, struct iwn_rx_desc *desc, DPRINTF(sc, IWN_DEBUG_XMIT, "%s: " "qid %d idx %d retries %d nkill %d rate %x duration %d status %x\n", - __func__, desc->qid, desc->idx, stat->retrycnt, - stat->killcnt, stat->rate, le16toh(stat->duration), + __func__, desc->qid, desc->idx, stat->ackfailcnt, + stat->btkillcnt, stat->rate, le16toh(stat->duration), le32toh(stat->status)); +#ifdef notyet /* Reset TX scheduler slot. */ iwn5000_reset_sched(sc, desc->qid & 0xf, desc->idx); - iwn_tx_done(sc, desc, stat->retrycnt, le16toh(stat->status) & 0xff); +#endif + iwn_tx_done(sc, desc, stat->ackfailcnt, le16toh(stat->status) & 0xff); } /* * Adapter-independent backend for TX_DONE firmware notifications. */ void -iwn_tx_done(struct iwn_softc *sc, struct iwn_rx_desc *desc, int retrycnt, +iwn_tx_done(struct iwn_softc *sc, struct iwn_rx_desc *desc, int ackfailcnt, uint8_t status) { struct ifnet *ifp = sc->sc_ifp; struct iwn_tx_ring *ring = &sc->txq[desc->qid & 0xf]; struct iwn_tx_data *data = &ring->data[desc->idx]; + struct iwn_node *wn = (void *)data->ni; struct mbuf *m; struct ieee80211_node *ni; KASSERT(data->ni != NULL, ("no node")); /* Unmap and free mbuf. */ - bus_dmamap_sync(ring->desc_dma.tag, data->map, BUS_DMASYNC_POSTWRITE); - bus_dmamap_unload(ring->desc_dma.tag, data->map); + bus_dmamap_sync(ring->data_dmat, data->map, BUS_DMASYNC_POSTWRITE); + bus_dmamap_unload(ring->data_dmat, data->map); m = data->m, data->m = NULL; ni = data->ni, data->ni = NULL; @@ -2122,6 +2317,18 @@ iwn_tx_done(struct iwn_softc *sc, struct iwn_rx_desc *desc, int retrycnt, ieee80211_process_callback(ni, m, (status & IWN_TX_FAIL) != 0); } + + /* + * Update rate control statistics for the node. + */ + if (status & 0x80) { + ifp->if_oerrors++; + ieee80211_amrr_tx_complete(&wn->amn, + IEEE80211_AMRR_FAILURE, ackfailcnt); + } else { + ieee80211_amrr_tx_complete(&wn->amn, + IEEE80211_AMRR_SUCCESS, ackfailcnt); + } m_freem(m); ieee80211_free_node(ni); @@ -2130,7 +2337,6 @@ iwn_tx_done(struct iwn_softc *sc, struct iwn_rx_desc *desc, int retrycnt, sc->qfullmsk &= ~(1 << ring->qid); if (sc->qfullmsk == 0 && (ifp->if_drv_flags & IFF_DRV_OACTIVE)) { - printf("hier :(\n"); ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; iwn_start_locked(ifp); } @@ -2154,9 +2360,7 @@ iwn_cmd_done(struct iwn_softc *sc, struct iwn_rx_desc *desc) /* If the command was mapped in an mbuf, free it. */ if (data->m != NULL) { - bus_dmamap_sync(ring->desc_dma.tag, data->map, - BUS_DMASYNC_POSTWRITE); - bus_dmamap_unload(ring->desc_dma.tag, data->map); + bus_dmamap_unload(ring->data_dmat, data->map); m_freem(data->m); data->m = NULL; } @@ -2182,8 +2386,8 @@ iwn_notif_intr(struct iwn_softc *sc) struct iwn_rx_data *data = &sc->rxq.data[sc->rxq.cur]; struct iwn_rx_desc *desc; - bus_dmamap_sync(sc->rxq.stat_dma.tag, data->map, - BUS_DMASYNC_PREWRITE); + bus_dmamap_sync(sc->rxq.data_dmat, data->map, + BUS_DMASYNC_POSTREAD); desc = mtod(data->m, struct iwn_rx_desc *); DPRINTF(sc, IWN_DEBUG_RECV, @@ -2206,6 +2410,13 @@ iwn_notif_intr(struct iwn_softc *sc) iwn_rx_done(sc, desc, data); break; +#if 0 /* HT */ + case IWN_RX_COMPRESSED_BA: + /* A Compressed BlockAck has been received. */ + iwn_rx_compressed_ba(sc, desc, data); + break; +#endif + case IWN_TX_DONE: /* An 802.11 frame has been transmitted. */ sc->sc_hal->tx_done(sc, desc, data); @@ -2222,8 +2433,12 @@ iwn_notif_intr(struct iwn_softc *sc) (struct iwn_beacon_missed *)(desc + 1); int misses = le32toh(miss->consecutive); - bus_dmamap_sync(sc->rxq.stat_dma.tag, data->map, - BUS_DMASYNC_PREWRITE); + bus_dmamap_sync(sc->rxq.data_dmat, data->map, + BUS_DMASYNC_POSTREAD); + + /* XXX not sure why we're notified w/ zero */ + if (misses == 0) + break; DPRINTF(sc, IWN_DEBUG_STATE, "%s: beacons missed %d/%d\n", __func__, misses, le32toh(miss->total)); @@ -2234,8 +2449,11 @@ iwn_notif_intr(struct iwn_softc *sc) */ if (vap->iv_state == IEEE80211_S_RUN && misses > 5) (void) iwn_init_sensitivity(sc); - if (misses >= vap->iv_bmissthreshold) + if (misses >= vap->iv_bmissthreshold) { + IWN_UNLOCK(sc); ieee80211_beacon_miss(ic); + IWN_LOCK(sc); + } break; } case IWN_UC_READY: @@ -2244,8 +2462,8 @@ iwn_notif_intr(struct iwn_softc *sc) (struct iwn_ucode_info *)(desc + 1); /* The microcontroller is ready. */ - bus_dmamap_sync(sc->rxq.stat_dma.tag, data->map, - BUS_DMASYNC_PREWRITE); + bus_dmamap_sync(sc->rxq.data_dmat, data->map, + BUS_DMASYNC_POSTREAD); DPRINTF(sc, IWN_DEBUG_RESET, "microcode alive notification version=%d.%d " "subtype=%x alive=%x\n", uc->major, uc->minor, @@ -2257,7 +2475,7 @@ iwn_notif_intr(struct iwn_softc *sc) break; } if (uc->subtype == IWN_UCODE_INIT) { - /* Save microcontroller's report. */ + /* Save microcontroller report. */ memcpy(&sc->ucode_info, uc, sizeof (*uc)); } /* Save the address of the error log in SRAM. */ @@ -2273,8 +2491,8 @@ iwn_notif_intr(struct iwn_softc *sc) * noted. However, we handle this in iwn_intr as we * get both the enable/disble intr. */ - bus_dmamap_sync(sc->rxq.stat_dma.tag, data->map, - BUS_DMASYNC_PREWRITE); + bus_dmamap_sync(sc->rxq.data_dmat, data->map, + BUS_DMASYNC_POSTREAD); DPRINTF(sc, IWN_DEBUG_INTR, "state changed to %x\n", le32toh(*status)); break; @@ -2284,8 +2502,8 @@ iwn_notif_intr(struct iwn_softc *sc) struct iwn_start_scan *scan = (struct iwn_start_scan *)(desc + 1); - bus_dmamap_sync(sc->rxq.stat_dma.tag, data->map, - BUS_DMASYNC_PREWRITE); + bus_dmamap_sync(sc->rxq.data_dmat, data->map, + BUS_DMASYNC_POSTREAD); DPRINTF(sc, IWN_DEBUG_ANY, "%s: scanning channel %d status %x\n", __func__, scan->chan, le32toh(scan->status)); @@ -2296,13 +2514,15 @@ iwn_notif_intr(struct iwn_softc *sc) struct iwn_stop_scan *scan = (struct iwn_stop_scan *)(desc + 1); - bus_dmamap_sync(sc->rxq.stat_dma.tag, data->map, - BUS_DMASYNC_PREWRITE); + bus_dmamap_sync(sc->rxq.data_dmat, data->map, + BUS_DMASYNC_POSTREAD); DPRINTF(sc, IWN_DEBUG_STATE, "scan finished nchan=%d status=%d chan=%d\n", scan->nchan, scan->status, scan->chan); + IWN_UNLOCK(sc); ieee80211_scan_next(vap); + IWN_LOCK(sc); break; } case IWN5000_CALIBRATION_RESULT: @@ -2310,6 +2530,7 @@ iwn_notif_intr(struct iwn_softc *sc) break; case IWN5000_CALIBRATION_DONE: + sc->sc_flags |= IWN_FLAG_CALIB_DONE; wakeup(sc); break; } @@ -2336,7 +2557,7 @@ iwn_wakeup_intr(struct iwn_softc *sc) /* Wakeup RX and TX rings. */ IWN_WRITE(sc, IWN_FH_RX_WPTR, sc->rxq.cur & ~7); - for (qid = 0; qid < 6; qid++) { + for (qid = 0; qid < sc->sc_hal->ntxqs; qid++) { struct iwn_tx_ring *ring = &sc->txq[qid]; IWN_WRITE(sc, IWN_HBUS_TARG_WRPTR, qid << 8 | ring->cur); } @@ -2365,18 +2586,17 @@ iwn_rftoggle_intr(struct iwn_softc *sc) * can help us to identify certain classes of problems. */ void -iwn_fatal_intr(struct iwn_softc *sc, uint32_t r1, uint32_t r2) +iwn_fatal_intr(struct iwn_softc *sc) { -#define nitems(_a) (sizeof((_a)) / sizeof((_a)[0])) const struct iwn_hal *hal = sc->sc_hal; - struct ifnet *ifp = sc->sc_ifp; - struct ieee80211com *ic = ifp->if_l2com; - struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); struct iwn_fw_dump dump; int i; IWN_LOCK_ASSERT(sc); + /* Force a complete recalibration on next init. */ + sc->sc_flags &= ~IWN_FLAG_CALIB_DONE; + /* Check that the error log address is valid. */ if (sc->errptr < IWN_FW_DATA_BASE || sc->errptr + sizeof (dump) > @@ -2423,10 +2643,6 @@ iwn_fatal_intr(struct iwn_softc *sc, uint32_t r1, uint32_t r2) i, ring->qid, ring->cur, ring->queued); } printf(" rx ring: cur=%d\n", sc->rxq.cur); - - if (vap != NULL) - ieee80211_cancel_scan(vap); - ieee80211_runtask(ic, &sc->sc_reinit_task); } void @@ -2434,50 +2650,78 @@ iwn_intr(void *arg) { struct iwn_softc *sc = arg; struct ifnet *ifp = sc->sc_ifp; - uint32_t r1, r2; + uint32_t r1, r2, tmp; IWN_LOCK(sc); /* Disable interrupts. */ - IWN_WRITE(sc, IWN_MASK, 0); - - r1 = IWN_READ(sc, IWN_INT); - r2 = IWN_READ(sc, IWN_FH_INT); + IWN_WRITE(sc, IWN_INT_MASK, 0); + + /* Read interrupts from ICT (fast) or from registers (slow). */ + if (sc->sc_flags & IWN_FLAG_USE_ICT) { + tmp = 0; + while (sc->ict[sc->ict_cur] != 0) { + tmp |= sc->ict[sc->ict_cur]; + sc->ict[sc->ict_cur] = 0; /* Acknowledge. */ + sc->ict_cur = (sc->ict_cur + 1) % IWN_ICT_COUNT; + } + tmp = le32toh(tmp); + if (tmp == 0xffffffff) + tmp = 0; /* Shouldn't happen. */ + r1 = (tmp & 0xff00) << 16 | (tmp & 0xff); + r2 = 0; /* Unused. */ + } else { + r1 = IWN_READ(sc, IWN_INT); + if (r1 == 0xffffffff || (r1 & 0xfffffff0) == 0xa5a5a5a0) + return; /* Hardware gone! */ + r2 = IWN_READ(sc, IWN_FH_INT); + } DPRINTF(sc, IWN_DEBUG_INTR, "interrupt reg1=%x reg2=%x\n", r1, r2); - if (r1 == 0 && r2 == 0) { - if (ifp->if_drv_flags & IFF_DRV_OACTIVE) - IWN_WRITE(sc, IWN_MASK, IWN_INT_MASK); + if (r1 == 0 && r2 == 0) goto done; /* Interrupt not for us. */ - } - if (r1 == 0xffffffff || (r1 & 0xfffffff0) == 0xa5a5a5a0) - goto done; /* Hardware gone! */ /* Acknowledge interrupts. */ IWN_WRITE(sc, IWN_INT, r1); - IWN_WRITE(sc, IWN_FH_INT, r2); - - DPRINTF(sc, IWN_DEBUG_INTR, "interrupt reg1=%x reg2=%x\n", r1, r2); + if (!(sc->sc_flags & IWN_FLAG_USE_ICT)) + IWN_WRITE(sc, IWN_FH_INT, r2); if (r1 & IWN_INT_RF_TOGGLED) { iwn_rftoggle_intr(sc); + goto done; } if (r1 & IWN_INT_CT_REACHED) { device_printf(sc->sc_dev, "%s: critical temperature reached!\n", __func__); - /* XXX Reduce TX power? */ } if (r1 & (IWN_INT_SW_ERR | IWN_INT_HW_ERR)) { - iwn_fatal_intr(sc, r1, r2); + iwn_fatal_intr(sc); + ifp->if_flags &= ~IFF_UP; + iwn_stop_locked(sc); goto done; } - if ((r1 & (IWN_INT_FH_RX | IWN_INT_SW_RX)) || - (r2 & IWN_FH_INT_RX)) - iwn_notif_intr(sc); + if ((r1 & (IWN_INT_FH_RX | IWN_INT_SW_RX | IWN_INT_RX_PERIODIC)) || + (r2 & IWN_FH_INT_RX)) { + if (sc->sc_flags & IWN_FLAG_USE_ICT) { + if (r1 & (IWN_INT_FH_RX | IWN_INT_SW_RX)) + IWN_WRITE(sc, IWN_FH_INT, IWN_FH_INT_RX); + IWN_WRITE_1(sc, IWN_INT_PERIODIC, + IWN_INT_PERIODIC_DIS); + iwn_notif_intr(sc); + if (r1 & (IWN_INT_FH_RX | IWN_INT_SW_RX)) { + IWN_WRITE_1(sc, IWN_INT_PERIODIC, + IWN_INT_PERIODIC_ENA); + } + } else + iwn_notif_intr(sc); + } - if ((r1 & IWN_INT_FH_TX) || (r2 & IWN_FH_INT_TX)) + if ((r1 & IWN_INT_FH_TX) || (r2 & IWN_FH_INT_TX)) { + if (sc->sc_flags & IWN_FLAG_USE_ICT) + IWN_WRITE(sc, IWN_FH_INT, IWN_FH_INT_TX); wakeup(sc); /* FH DMA transfer completed. */ + } if (r1 & IWN_INT_ALIVE) wakeup(sc); /* Firmware is alive. */ @@ -2485,10 +2729,11 @@ iwn_intr(void *arg) if (r1 & IWN_INT_WAKEUP) iwn_wakeup_intr(sc); +done: /* Re-enable interrupts. */ - IWN_WRITE(sc, IWN_MASK, IWN_INT_MASK); + if (ifp->if_flags & IFF_UP) + IWN_WRITE(sc, IWN_INT_MASK, sc->int_mask); -done: IWN_UNLOCK(sc); } @@ -2505,7 +2750,7 @@ iwn4965_update_sched(struct iwn_softc *sc, int qid, int idx, uint8_t id, *w = htole16(len + 8); bus_dmamap_sync(sc->sched_dma.tag, sc->sched_dma.map, BUS_DMASYNC_PREWRITE); - if (idx < IWN4965_SCHEDSZ) { + if (idx < IWN_SCHED_WINSZ) { *(w + IWN_TX_RING_COUNT) = *w; bus_dmamap_sync(sc->sched_dma.tag, sc->sched_dma.map, BUS_DMASYNC_PREWRITE); @@ -2544,19 +2789,16 @@ iwn5000_reset_sched(struct iwn_softc *sc, int qid, int idx) } } -/* Determine if a given rate is CCK or OFDM. */ -#define IWN_RATE_IS_OFDM(rate) ((rate) >= 12 && (rate) != 22) - -static const struct iwn_rate * +static uint8_t iwn_plcp_signal(int rate) { int i; for (i = 0; i < IWN_RIDX_MAX + 1; i++) { if (rate == iwn_rates[i].rate) - return &iwn_rates[i]; + return i; } - return &iwn_rates[0]; + return 0; } int @@ -2576,12 +2818,11 @@ iwn_tx_data(struct iwn_softc *sc, struct mbuf *m, struct ieee80211_node *ni, struct ieee80211_frame *wh; struct ieee80211_key *k = NULL; struct mbuf *mnew; - bus_addr_t paddr; bus_dma_segment_t segs[IWN_MAX_SCATTER]; uint32_t flags; u_int hdrlen; - int totlen, error, pad, nsegs, i, rate; - uint8_t type, txant; + int totlen, error, pad, nsegs = 0, i, rate; + uint8_t ridx, type, txant; IWN_LOCK_ASSERT(sc); @@ -2593,8 +2834,7 @@ iwn_tx_data(struct iwn_softc *sc, struct mbuf *m, struct ieee80211_node *ni, data = &ring->data[ring->cur]; /* Choose a TX rate index. */ - /* XXX ni_chan */ - tp = &vap->iv_txparms[ieee80211_chan2mode(ic->ic_curchan)]; + tp = &vap->iv_txparms[ieee80211_chan2mode(ni->ni_chan)]; if (type == IEEE80211_FC0_TYPE_MGT) rate = tp->mgmtrate; else if (IEEE80211_IS_MULTICAST(wh->i_addr1)) @@ -2602,10 +2842,11 @@ iwn_tx_data(struct iwn_softc *sc, struct mbuf *m, struct ieee80211_node *ni, else if (tp->ucastrate != IEEE80211_FIXED_RATE_NONE) rate = tp->ucastrate; else { - (void) ieee80211_amrr_choose(ni, &IWN_NODE(ni)->amn); + (void) ieee80211_amrr_choose(ni, &wn->amn); rate = ni->ni_txrate; } - rinfo = iwn_plcp_signal(rate); + ridx = iwn_plcp_signal(rate); + rinfo = &iwn_rates[ridx]; /* Encrypt the frame if need be. */ if (wh->i_fc[1] & IEEE80211_FC1_WEP) { @@ -2623,7 +2864,7 @@ iwn_tx_data(struct iwn_softc *sc, struct mbuf *m, struct ieee80211_node *ni, struct iwn_tx_radiotap_header *tap = &sc->sc_txtap; tap->wt_flags = 0; - tap->wt_rate = rate; + tap->wt_rate = rinfo->rate; if (k != NULL) tap->wt_flags |= IEEE80211_RADIOTAP_F_WEP; @@ -2658,7 +2899,7 @@ iwn_tx_data(struct iwn_softc *sc, struct mbuf *m, struct ieee80211_node *ni, if (totlen + IEEE80211_CRC_LEN > vap->iv_rtsthreshold) { flags |= IWN_TX_NEED_RTS; } else if ((ic->ic_flags & IEEE80211_F_USEPROT) && - IWN_RATE_IS_OFDM(rate)) { + ridx >= IWN_RIDX_OFDM6) { if (ic->ic_protmode == IEEE80211_PROT_CTSONLY) flags |= IWN_TX_NEED_CTS; else if (ic->ic_protmode == IEEE80211_PROT_RTSCTS) @@ -2672,7 +2913,7 @@ iwn_tx_data(struct iwn_softc *sc, struct mbuf *m, struct ieee80211_node *ni, } else flags |= IWN_TX_FULL_TXOP; } - } else + } if (IEEE80211_IS_MULTICAST(wh->i_addr1) || type != IEEE80211_FC0_TYPE_DATA) @@ -2696,7 +2937,7 @@ iwn_tx_data(struct iwn_softc *sc, struct mbuf *m, struct ieee80211_node *ni, tx->timeout = htole16(0); if (hdrlen & 3) { - /* First segment's length must be a multiple of 4. */ + /* First segment length must be a multiple of 4. */ flags |= IWN_TX_NEED_PADDING; pad = 4 - (hdrlen & 3); } else @@ -2704,22 +2945,25 @@ iwn_tx_data(struct iwn_softc *sc, struct mbuf *m, struct ieee80211_node *ni, tx->len = htole16(totlen); tx->tid = 0; - tx->rts_ntries = 60; /* XXX? */ - tx->data_ntries = 15; /* XXX? */ + tx->rts_ntries = 60; + tx->data_ntries = 15; tx->lifetime = htole32(IWN_LIFETIME_INFINITE); tx->plcp = rinfo->plcp; tx->rflags = rinfo->flags; if (tx->id == hal->broadcast_id) { + /* Group or management frame. */ + tx->linkq = 0; /* XXX Alternate between antenna A and B? */ - txant = IWN_LSB(sc->txantmsk); + txant = IWN_LSB(sc->txchainmask); tx->rflags |= IWN_RFLAG_ANT(txant); - } else - flags |= IWN_TX_LINKQ; + } else { + tx->linkq = 0; + flags |= IWN_TX_LINKQ; /* enable MRR */ + } /* Set physical address of "scratch area". */ - paddr = ring->cmd_dma.paddr + ring->cur * sizeof (struct iwn_tx_cmd); - tx->loaddr = htole32(IWN_LOADDR(paddr)); - tx->hiaddr = IWN_HIADDR(paddr); + tx->loaddr = htole32(IWN_LOADDR(data->scratch_paddr)); + tx->hiaddr = IWN_HIADDR(data->scratch_paddr); /* Copy 802.11 header in TX command. */ memcpy((uint8_t *)(tx + 1), wh, hdrlen); @@ -2729,25 +2973,23 @@ iwn_tx_data(struct iwn_softc *sc, struct mbuf *m, struct ieee80211_node *ni, tx->security = 0; tx->flags = htole32(flags); - error = bus_dmamap_load_mbuf_sg(ring->desc_dma.tag, data->map, m, segs, - &nsegs, BUS_DMA_NOWAIT); - if (error != 0) { + if (m->m_len > 0) { + error = bus_dmamap_load_mbuf_sg(ring->data_dmat, data->map, + m, segs, &nsegs, BUS_DMA_NOWAIT); if (error == EFBIG) { /* too many fragments, linearize */ mnew = m_collapse(m, 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(m); return ENOBUFS; } m = mnew; - error = bus_dmamap_load_mbuf_sg(ring->desc_dma.tag, + error = bus_dmamap_load_mbuf_sg(ring->data_dmat, data->map, m, 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); @@ -2765,8 +3007,8 @@ iwn_tx_data(struct iwn_softc *sc, struct mbuf *m, struct ieee80211_node *ni, /* Fill TX descriptor. */ desc->nsegs = 1 + nsegs; /* First DMA segment is used by the TX command. */ - desc->segs[0].addr = htole32(IWN_LOADDR(paddr)); - desc->segs[0].len = htole16(IWN_HIADDR(paddr) | + desc->segs[0].addr = htole32(IWN_LOADDR(data->cmd_paddr)); + desc->segs[0].len = htole16(IWN_HIADDR(data->cmd_paddr) | (4 + sizeof (*tx) + hdrlen + pad) << 4); /* Other DMA segments are for data payload. */ for (i = 1; i <= nsegs; i++) { @@ -2775,14 +3017,16 @@ iwn_tx_data(struct iwn_softc *sc, struct mbuf *m, struct ieee80211_node *ni, segs[i - 1].ds_len << 4); } - bus_dmamap_sync(ring->desc_dma.tag, data->map, BUS_DMASYNC_PREWRITE); - bus_dmamap_sync(ring->desc_dma.tag, ring->cmd_dma.map, + bus_dmamap_sync(ring->data_dmat, data->map, BUS_DMASYNC_PREWRITE); + bus_dmamap_sync(ring->data_dmat, ring->cmd_dma.map, BUS_DMASYNC_PREWRITE); bus_dmamap_sync(ring->desc_dma.tag, ring->desc_dma.map, BUS_DMASYNC_PREWRITE); +#ifdef notyet /* Update TX scheduler. */ hal->update_sched(sc, ring->qid, ring->cur, tx->id, totlen); +#endif /* Kick TX ring. */ ring->cur = (ring->cur + 1) % IWN_TX_RING_COUNT; @@ -2796,7 +3040,7 @@ iwn_tx_data(struct iwn_softc *sc, struct mbuf *m, struct ieee80211_node *ni, } static int -iwn_tx_data_raw(struct iwn_softc *sc, struct mbuf *m0, +iwn_tx_data_raw(struct iwn_softc *sc, struct mbuf *m, struct ieee80211_node *ni, struct iwn_tx_ring *ring, const struct ieee80211_bpf_params *params) { @@ -2815,12 +3059,12 @@ iwn_tx_data_raw(struct iwn_softc *sc, struct mbuf *m0, bus_dma_segment_t segs[IWN_MAX_SCATTER]; uint32_t flags; u_int hdrlen; - int totlen, error, pad, nsegs, i, rate; - uint8_t type, txant; + int totlen, error, pad, nsegs = 0, i, rate; + uint8_t ridx, type, txant; IWN_LOCK_ASSERT(sc); - wh = mtod(m0, struct ieee80211_frame *); + wh = mtod(m, struct ieee80211_frame *); hdrlen = ieee80211_anyhdrsize(wh); type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK; @@ -2831,13 +3075,15 @@ iwn_tx_data_raw(struct iwn_softc *sc, struct mbuf *m0, rate = params->ibp_rate0; if (!ieee80211_isratevalid(ic->ic_rt, rate)) { /* XXX fall back to mcast/mgmt rate? */ - m_freem(m0); + m_freem(m); return EINVAL; } - rinfo = iwn_plcp_signal(rate); + ridx = iwn_plcp_signal(rate); + rinfo = &iwn_rates[ridx]; - totlen = m0->m_pkthdr.len; + totlen = m->m_pkthdr.len; + /* Prepare TX firmware command. */ cmd = &ring->cmd[ring->cur]; cmd->code = IWN_CMD_TX_DATA; cmd->flags = 0; @@ -2845,7 +3091,7 @@ iwn_tx_data_raw(struct iwn_softc *sc, struct mbuf *m0, cmd->idx = ring->cur; tx = (struct iwn_cmd_data *)cmd->data; - /* NB: no need to bzero tx, all fields are reinitialized here */ + /* NB: No need to clear tx, all fields are reinitialized here. */ tx->scratch = 0; /* clear "scratch" area */ flags = 0; @@ -2882,7 +3128,7 @@ iwn_tx_data_raw(struct iwn_softc *sc, struct mbuf *m0, tx->timeout = htole16(0); if (hdrlen & 3) { - /* First segment's length must be a multiple of 4. */ + /* First segment length must be a multiple of 4. */ flags |= IWN_TX_NEED_PADDING; pad = 4 - (hdrlen & 3); } else @@ -2894,7 +3140,7 @@ iwn_tx_data_raw(struct iwn_softc *sc, struct mbuf *m0, tap->wt_flags = 0; tap->wt_rate = rate; - ieee80211_radiotap_tx(vap, m0); + ieee80211_radiotap_tx(vap, m); } tx->len = htole16(totlen); @@ -2905,12 +3151,10 @@ iwn_tx_data_raw(struct iwn_softc *sc, struct mbuf *m0, tx->lifetime = htole32(IWN_LIFETIME_INFINITE); tx->plcp = rinfo->plcp; tx->rflags = rinfo->flags; - if (tx->id == hal->broadcast_id) { - txant = IWN_LSB(sc->txantmsk); - tx->rflags |= IWN_RFLAG_ANT(txant); - } else { - flags |= IWN_TX_LINKQ; /* enable MRR */ - } + /* Group or management frame. */ + tx->linkq = 0; + txant = IWN_LSB(sc->txchainmask); + tx->rflags |= IWN_RFLAG_ANT(txant); /* Set physical address of "scratch area". */ paddr = ring->cmd_dma.paddr + ring->cur * sizeof (struct iwn_tx_cmd); tx->loaddr = htole32(IWN_LOADDR(paddr)); @@ -2920,48 +3164,46 @@ iwn_tx_data_raw(struct iwn_softc *sc, struct mbuf *m0, memcpy((uint8_t *)(tx + 1), wh, hdrlen); /* Trim 802.11 header. */ - m_adj(m0, hdrlen); + m_adj(m, hdrlen); tx->security = 0; tx->flags = htole32(flags); - error = bus_dmamap_load_mbuf_sg(ring->desc_dma.tag, data->map, m0, segs, - &nsegs, BUS_DMA_NOWAIT); - if (error != 0) { + if (m->m_len > 0) { + error = bus_dmamap_load_mbuf_sg(ring->data_dmat, data->map, + m, segs, &nsegs, BUS_DMA_NOWAIT); if (error == EFBIG) { /* Too many fragments, linearize. */ - mnew = m_collapse(m0, M_DONTWAIT, IWN_MAX_SCATTER); + mnew = m_collapse(m, 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(m); return ENOBUFS; } - m0 = mnew; - error = bus_dmamap_load_mbuf_sg(ring->desc_dma.tag, - data->map, m0, segs, &nsegs, BUS_DMA_NOWAIT); + m = mnew; + error = bus_dmamap_load_mbuf_sg(ring->data_dmat, + data->map, m, 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); + m_freem(m); return error; } } - data->m = m0; + data->m = m; 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); + __func__, ring->qid, ring->cur, m->m_pkthdr.len, nsegs); /* Fill TX descriptor. */ desc->nsegs = 1 + nsegs; /* First DMA segment is used by the TX command. */ - desc->segs[0].addr = htole32(IWN_LOADDR(paddr)); - desc->segs[0].len = htole16(IWN_HIADDR(paddr) | + desc->segs[0].addr = htole32(IWN_LOADDR(data->cmd_paddr)); + desc->segs[0].len = htole16(IWN_HIADDR(data->cmd_paddr) | (4 + sizeof (*tx) + hdrlen + pad) << 4); /* Other DMA segments are for data payload. */ for (i = 1; i <= nsegs; i++) { @@ -2970,8 +3212,16 @@ iwn_tx_data_raw(struct iwn_softc *sc, struct mbuf *m0, segs[i - 1].ds_len << 4); } + bus_dmamap_sync(ring->data_dmat, data->map, BUS_DMASYNC_PREWRITE); + bus_dmamap_sync(ring->data_dmat, ring->cmd_dma.map, + BUS_DMASYNC_PREWRITE); + bus_dmamap_sync(ring->desc_dma.tag, ring->desc_dma.map, + BUS_DMASYNC_PREWRITE); + +#ifdef notyet /* Update TX scheduler. */ hal->update_sched(sc, ring->qid, ring->cur, tx->id, totlen); +#endif /* Kick TX ring. */ ring->cur = (ring->cur + 1) % IWN_TX_RING_COUNT; @@ -3086,8 +3336,9 @@ iwn_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) { struct iwn_softc *sc = ifp->if_softc; struct ieee80211com *ic = ifp->if_l2com; + struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); struct ifreq *ifr = (struct ifreq *) data; - int error = 0, startall = 0; + int error = 0, startall = 0, stop = 0; switch (cmd) { case SIOCSIFFLAGS: @@ -3095,7 +3346,10 @@ iwn_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) if (ifp->if_flags & IFF_UP) { if (!(ifp->if_drv_flags & IFF_DRV_RUNNING)) { iwn_init_locked(sc); - startall = 1; + if (IWN_READ(sc, IWN_GP_CNTRL) & IWN_GP_CNTRL_RFKILL) + startall = 1; + else + stop = 1; } } else { if (ifp->if_drv_flags & IFF_DRV_RUNNING) @@ -3104,6 +3358,8 @@ iwn_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) IWN_UNLOCK(sc); if (startall) ieee80211_start_all(ic); + else if (vap != NULL && stop) + ieee80211_stop(vap); break; case SIOCGIFMEDIA: error = ifmedia_ioctl(ifp, ifr, &ic->ic_media, cmd); @@ -3124,7 +3380,6 @@ iwn_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) int iwn_cmd(struct iwn_softc *sc, int code, const void *buf, int size, int async) { - const struct iwn_hal *hal = sc->sc_hal; struct iwn_tx_ring *ring = &sc->txq[4]; struct iwn_tx_desc *desc; struct iwn_tx_data *data; @@ -3143,18 +3398,11 @@ iwn_cmd(struct iwn_softc *sc, int code, const void *buf, int size, int async) /* Command is too large to fit in a descriptor. */ if (totlen > MCLBYTES) return EINVAL; - MGETHDR(m, M_DONTWAIT, MT_DATA); + m = m_getjcl(M_DONTWAIT, MT_DATA, M_PKTHDR, MJUMPAGESIZE); if (m == NULL) return ENOMEM; - if (totlen > MHLEN) { - MCLGET(m, M_DONTWAIT); - if (!(m->m_flags & M_EXT)) { - m_freem(m); - return ENOMEM; - } - } cmd = mtod(m, struct iwn_tx_cmd *); - error = bus_dmamap_load(ring->cmd_dma.tag, data->map, cmd, + error = bus_dmamap_load(ring->data_dmat, data->map, cmd, totlen, iwn_dma_map_addr, &paddr, BUS_DMA_NOWAIT); if (error != 0) { m_freem(m); @@ -3181,17 +3429,19 @@ iwn_cmd(struct iwn_softc *sc, int code, const void *buf, int size, int async) cmd->flags, cmd->qid, cmd->idx); if (size > sizeof cmd->data) { - bus_dmamap_sync(ring->cmd_dma.tag, data->map, + bus_dmamap_sync(ring->data_dmat, data->map, BUS_DMASYNC_PREWRITE); } else { - bus_dmamap_sync(ring->cmd_dma.tag, ring->cmd_dma.map, + bus_dmamap_sync(ring->data_dmat, ring->cmd_dma.map, BUS_DMASYNC_PREWRITE); } bus_dmamap_sync(ring->desc_dma.tag, ring->desc_dma.map, BUS_DMASYNC_PREWRITE); +#ifdef notyet /* Update TX scheduler. */ - hal->update_sched(sc, ring->qid, ring->cur, 0, 0); + sc->sc_hal->update_sched(sc, ring->qid, ring->cur, 0, 0); +#endif /* Kick command ring. */ ring->cur = (ring->cur + 1) % IWN_TX_RING_COUNT; @@ -3226,6 +3476,7 @@ iwn5000_add_node(struct iwn_softc *sc, struct iwn_node_info *node, int async) return iwn_cmd(sc, IWN_CMD_ADD_NODE, node, sizeof (*node), async); } +#if 0 /* HT */ 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 */ @@ -3238,6 +3489,7 @@ static const uint8_t iwn_mimo_mcs_to_plcp[] = { 0, 0, 0, 0, /* CCK */ 8, 8, 9, 10, 11, 12, 13, 14, 15 /* HT */ }; +#endif static const uint8_t iwn_prev_ridx[] = { /* NB: allow fallback from CCK11 to OFDM9 and from OFDM6 to CCK5 */ 0, 0, 1, 5, /* CCK */ @@ -3249,35 +3501,41 @@ static const uint8_t iwn_prev_ridx[] = { * node operating on the specified channel. */ int -iwn_set_link_quality(struct iwn_softc *sc, uint8_t id, - const struct ieee80211_channel *c, int async) +iwn_set_link_quality(struct iwn_softc *sc, uint8_t id, int async) { + struct ifnet *ifp = sc->sc_ifp; + struct ieee80211com *ic = ifp->if_l2com; struct iwn_cmd_link_quality linkq; - int ridx, i; - uint8_t txant; + const struct iwn_rate *rinfo; + int i; + uint8_t txant, ridx; /* Use the first valid TX antenna. */ - txant = IWN_LSB(sc->txantmsk); + txant = IWN_LSB(sc->txchainmask); memset(&linkq, 0, sizeof linkq); linkq.id = id; linkq.antmsk_1stream = txant; - linkq.antmsk_2stream = IWN_ANT_A | IWN_ANT_B; - linkq.ampdu_max = 64; + linkq.antmsk_2stream = IWN_ANT_AB; + linkq.ampdu_max = 31; linkq.ampdu_threshold = 3; linkq.ampdu_limit = htole16(4000); /* 4ms */ +#if 0 /* HT */ if (IEEE80211_IS_CHAN_HT(c)) linkq.mimo = 1; +#endif if (id == IWN_ID_BSS) ridx = IWN_RIDX_OFDM54; - else if (IEEE80211_IS_CHAN_A(c)) + else if (IEEE80211_IS_CHAN_A(ic->ic_curchan)) ridx = IWN_RIDX_OFDM6; else ridx = IWN_RIDX_CCK1; for (i = 0; i < IWN_MAX_TX_RETRIES; i++) { + rinfo = &iwn_rates[ridx]; +#if 0 /* HT */ if (IEEE80211_IS_CHAN_HT40(c)) { linkq.retry[i].plcp = iwn_mimo_mcs_to_plcp[ridx] | IWN_RIDX_MCS; @@ -3289,15 +3547,15 @@ iwn_set_link_quality(struct iwn_softc *sc, uint8_t id, | IWN_RIDX_MCS; linkq.retry[i].rflags = IWN_RFLAG_HT; /* XXX shortGI */ - } else { - linkq.retry[i].plcp = iwn_ridx_to_plcp[ridx]; - if (ridx <= IWN_RIDX_CCK11) - linkq.retry[i].rflags = IWN_RFLAG_CCK; + } else +#endif + { + linkq.retry[i].plcp = rinfo->plcp; + linkq.retry[i].rflags = rinfo->flags; } linkq.retry[i].rflags |= IWN_RFLAG_ANT(txant); ridx = iwn_prev_ridx[ridx]; } - #ifdef IWN_DEBUG if (sc->sc_debug & IWN_DEBUG_STATE) { printf("%s: set link quality for node %d, mimo %d ssmask %d\n", @@ -3316,8 +3574,7 @@ iwn_set_link_quality(struct iwn_softc *sc, uint8_t id, * Broadcast node is used to send group-addressed and management frames. */ int -iwn_add_broadcast_node(struct iwn_softc *sc, const struct ieee80211_channel *c, - int async) +iwn_add_broadcast_node(struct iwn_softc *sc, int async) { const struct iwn_hal *hal = sc->sc_hal; struct ifnet *ifp = sc->sc_ifp; @@ -3332,7 +3589,8 @@ iwn_add_broadcast_node(struct iwn_softc *sc, const struct ieee80211_channel *c, if (error != 0) return error; - return iwn_set_link_quality(sc, node.id, c, async); + error = iwn_set_link_quality(sc, hal->broadcast_id, async); + return error; } int @@ -3355,14 +3613,22 @@ iwn_wme_update(struct ieee80211com *ic) cmd.ac[i].txoplimit = htole16(IWN_TXOP_TO_US(wmep->wmep_txopLimit)); } + IEEE80211_UNLOCK(ic); IWN_LOCK(sc); (void) iwn_cmd(sc, IWN_CMD_EDCA_PARAMS, &cmd, sizeof cmd, 1 /*async*/); IWN_UNLOCK(sc); + IEEE80211_LOCK(ic); return 0; #undef IWN_TXOP_TO_US #undef IWN_EXP2 } +static void +iwn_update_mcast(struct ifnet *ifp) +{ + /* Ignore */ +} + void iwn_set_led(struct iwn_softc *sc, uint8_t which, uint8_t off, uint8_t on) { @@ -3379,19 +3645,27 @@ iwn_set_led(struct iwn_softc *sc, uint8_t which, uint8_t off, uint8_t on) } /* - * Set the critical temperature at which the firmware will notify us. + * Set the critical temperature at which the firmware will stop the radio + * and notify us. */ int iwn_set_critical_temp(struct iwn_softc *sc) { struct iwn_critical_temp crit; + int32_t temp; IWN_WRITE(sc, IWN_UCODE_GP1_CLR, IWN_UCODE_GP1_CTEMP_STOP_RF); + if (sc->hw_type == IWN_HW_REV_TYPE_5150) + temp = (IWN_CTOK(110) - sc->temp_off) * -5; + else if (sc->hw_type == IWN_HW_REV_TYPE_4965) + temp = IWN_CTOK(110); + else + temp = 110; memset(&crit, 0, sizeof crit); - crit.tempR = htole32(sc->critical_temp); - DPRINTF(sc, IWN_DEBUG_RESET, "setting critical temp to %u\n", - crit.tempR); + crit.tempR = htole32(temp); + DPRINTF(sc, IWN_DEBUG_RESET, "setting critical temp to %d\n", + temp); return iwn_cmd(sc, IWN_CMD_SET_CRITICAL_TEMP, &crit, sizeof crit, 0); } @@ -3420,16 +3694,13 @@ iwn_set_timing(struct iwn_softc *sc, struct ieee80211_node *ni) void iwn4965_power_calibration(struct iwn_softc *sc, int temp) { - struct ifnet *ifp = sc->sc_ifp; - struct ieee80211com *ic = ifp->if_l2com; - /* Adjust TX power if need be (delta >= 3 degC.) */ DPRINTF(sc, IWN_DEBUG_CALIBRATE, "%s: temperature %d->%d\n", __func__, sc->temp, temp); if (abs(temp - sc->temp) >= 3) { /* Record temperature of last calibration. */ sc->temp = temp; - (void)iwn4965_set_txpower(sc, ic->ic_bsschan, 1); + (void)iwn4965_set_txpower(sc, 1); } } @@ -3439,8 +3710,7 @@ iwn4965_power_calibration(struct iwn_softc *sc, int temp) * the current temperature and the current voltage. */ int -iwn4965_set_txpower(struct iwn_softc *sc, struct ieee80211_channel *ch, - int async) +iwn4965_set_txpower(struct iwn_softc *sc, int async) { /* Fixed-point arithmetic division using a n-bit fractional part. */ #define fdivround(a, b, n) \ @@ -3453,6 +3723,7 @@ iwn4965_set_txpower(struct iwn_softc *sc, struct ieee80211_channel *ch, struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; struct iwn_ucode_info *uc = &sc->ucode_info; + struct ieee80211_channel *ch; struct iwn4965_cmd_txpower cmd; struct iwn4965_eeprom_chan_samples *chans; int32_t vdiff, tdiff; @@ -3460,8 +3731,11 @@ iwn4965_set_txpower(struct iwn_softc *sc, struct ieee80211_channel *ch, const uint8_t *rf_gain, *dsp_gain; uint8_t chan; - /* Get channel number. */ - chan = ieee80211_chan2ieee(ic, ch); + /* Retrieve current channel from last RXON. */ + chan = sc->rxon.chan; + DPRINTF(sc, IWN_DEBUG_RESET, "setting TX power for channel %d\n", + chan); + ch = &ic->ic_channels[chan]; memset(&cmd, 0, sizeof cmd); cmd.band = IEEE80211_IS_CHAN_5GHZ(ch) ? 0 : 1; @@ -3487,7 +3761,7 @@ iwn4965_set_txpower(struct iwn_softc *sc, struct ieee80211_channel *ch, "%s: voltage compensation=%d (UCODE=%d, EEPROM=%d)\n", __func__, vdiff, le32toh(uc->volt), sc->eeprom_voltage); - /* Get channel's attenuation group. */ + /* Get channel attenuation group. */ if (chan <= 20) /* 1-20 */ grp = 4; else if (chan <= 43) /* 34-43 */ @@ -3501,7 +3775,7 @@ iwn4965_set_txpower(struct iwn_softc *sc, struct ieee80211_channel *ch, DPRINTF(sc, IWN_DEBUG_CALIBRATE | IWN_DEBUG_TXPOW, "%s: chan %d, attenuation group=%d\n", __func__, chan, grp); - /* Get channel's sub-band. */ + /* Get channel sub-band. */ for (i = 0; i < IWN_NBANDS; i++) if (sc->bands[i].lo != 0 && sc->bands[i].lo <= chan && chan <= sc->bands[i].hi) @@ -3536,6 +3810,7 @@ iwn4965_set_txpower(struct iwn_softc *sc, struct ieee80211_channel *ch, __func__, tdiff, sc->temp, temp); for (ridx = 0; ridx <= IWN_RIDX_MAX; ridx++) { + /* Convert dBm to half-dBm. */ maxchpwr = sc->maxpwr[chan] * 2; if ((ridx / 8) & 1) maxchpwr -= 6; /* MIMO 2T: -3dB */ @@ -3552,7 +3827,7 @@ iwn4965_set_txpower(struct iwn_softc *sc, struct ieee80211_channel *ch, else pwr -= 10; /* Others: -5dB */ - /* Do not exceed channel's max TX power. */ + /* Do not exceed channel max TX power. */ if (pwr > maxchpwr) pwr = maxchpwr; @@ -3588,8 +3863,7 @@ iwn4965_set_txpower(struct iwn_softc *sc, struct ieee80211_channel *ch, } int -iwn5000_set_txpower(struct iwn_softc *sc, struct ieee80211_channel *ch, - int async) +iwn5000_set_txpower(struct iwn_softc *sc, int async) { struct iwn5000_cmd_txpower cmd; @@ -3615,7 +3889,7 @@ iwn4965_get_rssi(struct iwn_softc *sc, struct iwn_rx_stat *stat) uint8_t mask, agc; int rssi; - mask = (le16toh(phy->antenna) >> 4) & 0x7; + mask = (le16toh(phy->antenna) >> 4) & IWN_ANT_ABC; agc = (le16toh(phy->agc) >> 7) & 0x7f; rssi = 0; @@ -3701,18 +3975,27 @@ iwn4965_get_temperature(struct iwn_softc *sc) temp = (259 * (r4 - r2)) / (r3 - r1); temp = (temp * 97) / 100 + 8; + DPRINTF(sc, IWN_DEBUG_ANY, "temperature %dK/%dC\n", temp, + IWN_KTOC(temp)); return IWN_KTOC(temp); } int iwn5000_get_temperature(struct iwn_softc *sc) { + int32_t temp; + /* * Temperature is not used by the driver for 5000 Series because * TX power calibration is handled by firmware. We export it to * users through the sensor framework though. */ - return le32toh(sc->rawtemp); + temp = le32toh(sc->rawtemp); + if (sc->hw_type == IWN_HW_REV_TYPE_5150) { + temp = (temp / -5) + sc->temp_off; + temp = IWN_KTOC(temp); + } + return temp; } /* @@ -3731,13 +4014,13 @@ iwn_init_sensitivity(struct iwn_softc *sc) calib->state = IWN_CALIB_STATE_INIT; calib->cck_state = IWN_CCK_STATE_HIFA; /* Set initial correlation values. */ - calib->ofdm_x1 = hal->limits->min_ofdm_x1; - calib->ofdm_mrc_x1 = hal->limits->min_ofdm_mrc_x1; + calib->ofdm_x1 = sc->limits->min_ofdm_x1; + calib->ofdm_mrc_x1 = sc->limits->min_ofdm_mrc_x1; calib->ofdm_x4 = 90; - calib->ofdm_mrc_x4 = hal->limits->min_ofdm_mrc_x4; + calib->ofdm_mrc_x4 = sc->limits->min_ofdm_mrc_x4; calib->cck_x4 = 125; - calib->cck_mrc_x4 = hal->limits->min_cck_mrc_x4; - calib->energy_cck = hal->limits->energy_cck; + calib->cck_mrc_x4 = sc->limits->min_cck_mrc_x4; + calib->energy_cck = sc->limits->energy_cck; /* Write initial sensitivity. */ error = iwn_send_sensitivity(sc); @@ -3783,21 +4066,21 @@ iwn_collect_noise(struct iwn_softc *sc, val = MAX(calib->rssi[2], val); /* Determine which antennas are connected. */ - sc->antmsk = 0; + sc->chainmask = 0; for (i = 0; i < 3; i++) if (val - calib->rssi[i] <= 15 * 20) - sc->antmsk |= 1 << i; + sc->chainmask |= 1 << i; /* If none of the TX antennas are connected, keep at least one. */ - if ((sc->antmsk & sc->txantmsk) == 0) - sc->antmsk |= IWN_LSB(sc->txantmsk); + if ((sc->chainmask & sc->txchainmask) == 0) + sc->chainmask |= IWN_LSB(sc->txchainmask); (void)hal->set_gains(sc); calib->state = IWN_CALIB_STATE_RUN; #ifdef notyet /* XXX Disable RX chains with no antennas connected. */ - sc->rxon.rxchain = htole16(IWN_RXCHAIN_SEL(sc->antmsk)); - (void)iwn_cmd(sc, IWN_CMD_CONFIGURE, &sc->rxon, hal->rxonsz, 1); + sc->rxon.rxchain = htole16(IWN_RXCHAIN_SEL(sc->chainmask)); + (void)iwn_cmd(sc, IWN_CMD_RXON, &sc->rxon, hal->rxonsz, 1); #endif #if 0 @@ -3826,8 +4109,7 @@ iwn5000_init_gains(struct iwn_softc *sc) { struct iwn_phy_calib cmd; - if (sc->hw_type == IWN_HW_REV_TYPE_6000 || - sc->hw_type == IWN_HW_REV_TYPE_6050) + if (sc->hw_type == IWN_HW_REV_TYPE_6050) return 0; memset(&cmd, 0, sizeof cmd); @@ -3847,16 +4129,16 @@ iwn4965_set_gains(struct iwn_softc *sc) int i, delta, noise; /* Get minimal noise among connected antennas. */ - noise = INT_MAX; /* NB: There's at least one antennaiwn. */ + noise = INT_MAX; /* NB: There's at least one antenna. */ for (i = 0; i < 3; i++) - if (sc->antmsk & (1 << i)) + if (sc->chainmask & (1 << i)) noise = MIN(calib->noise[i], noise); memset(&cmd, 0, sizeof cmd); cmd.code = IWN4965_PHY_CALIB_DIFF_GAIN; /* Set differential gains for connected antennas. */ for (i = 0; i < 3; i++) { - if (sc->antmsk & (1 << i)) { + if (sc->chainmask & (1 << i)) { /* Compute attenuation (in unit of 1.5dB). */ delta = (noise - (int32_t)calib->noise[i]) / 30; /* NB: delta <= 0 */ @@ -3868,7 +4150,7 @@ iwn4965_set_gains(struct iwn_softc *sc) } DPRINTF(sc, IWN_DEBUG_CALIBRATE, "setting differential gains Ant A/B/C: %x/%x/%x (%x)\n", - cmd.gain[0], cmd.gain[1], cmd.gain[2], sc->antmsk); + cmd.gain[0], cmd.gain[1], cmd.gain[2], sc->chainmask); return iwn_cmd(sc, IWN_CMD_PHY_CALIB, &cmd, sizeof cmd, 1); } @@ -3877,21 +4159,22 @@ iwn5000_set_gains(struct iwn_softc *sc) { struct iwn_calib_state *calib = &sc->calib; struct iwn_phy_calib_gain cmd; - int i, delta; + int i, ant, delta; - if (sc->hw_type == IWN_HW_REV_TYPE_6000 || - sc->hw_type == IWN_HW_REV_TYPE_6050) + if (sc->hw_type == IWN_HW_REV_TYPE_6050) return 0; memset(&cmd, 0, sizeof cmd); cmd.code = IWN5000_PHY_CALIB_NOISE_GAIN; cmd.ngroups = 1; cmd.isvalid = 1; - /* Set differential gains for antennas B and C. */ - for (i = 1; i < 3; i++) { - if (sc->antmsk & (1 << i)) { - /* The delta is relative to antenna A. */ - delta = ((int32_t)calib->noise[0] - + /* Get first available RX antenna as referential. */ + ant = IWN_LSB(sc->rxchainmask); + /* Set differential gains for other antennas. */ + for (i = ant + 1; i < 3; i++) { + if (sc->chainmask & (1 << i)) { + /* The delta is relative to antenna "ant". */ + delta = ((int32_t)calib->noise[ant] - (int32_t)calib->noise[i]) / 30; /* Limit to [-4.5dB,+4.5dB]. */ cmd.gain[i - 1] = MIN(abs(delta), 3); @@ -3901,7 +4184,7 @@ iwn5000_set_gains(struct iwn_softc *sc) } DPRINTF(sc, IWN_DEBUG_CALIBRATE, "setting differential gains Ant B/C: %x/%x (%x)\n", - cmd.gain[0], cmd.gain[1], sc->antmsk); + cmd.gain[0], cmd.gain[1], sc->chainmask); return iwn_cmd(sc, IWN_CMD_PHY_CALIB, &cmd, sizeof cmd, 1); } @@ -3929,8 +4212,7 @@ iwn_tune_sensitivity(struct iwn_softc *sc, const struct iwn_rx_stats *stats) needs_update = 1; \ } - const struct iwn_hal *hal = sc->sc_hal; - const struct iwn_sensitivity_limits *limits = hal->limits; + const struct iwn_sensitivity_limits *limits = sc->limits; struct iwn_calib_state *calib = &sc->calib; uint32_t val, rxena, fa; uint32_t energy[3], energy_min; @@ -4065,7 +4347,6 @@ iwn_tune_sensitivity(struct iwn_softc *sc, const struct iwn_rx_stats *stats) int iwn_send_sensitivity(struct iwn_softc *sc) { - const struct iwn_hal *hal = sc->sc_hal; struct iwn_calib_state *calib = &sc->calib; struct iwn_sensitivity_cmd cmd; @@ -4076,7 +4357,7 @@ iwn_send_sensitivity(struct iwn_softc *sc) cmd.corr_ofdm_mrc_x1 = htole16(calib->ofdm_mrc_x1); cmd.corr_ofdm_x4 = htole16(calib->ofdm_x4); cmd.corr_ofdm_mrc_x4 = htole16(calib->ofdm_mrc_x4); - cmd.energy_ofdm = htole16(hal->limits->energy_ofdm); + cmd.energy_ofdm = htole16(sc->limits->energy_ofdm); cmd.energy_ofdm_th = htole16(62); /* CCK modulation. */ cmd.corr_cck_x4 = htole16(calib->cck_x4); @@ -4120,6 +4401,7 @@ iwn_set_pslevel(struct iwn_softc *sc, int dtim, int level, int async) cmd.flags |= htole16(IWN_PS_ALLOW_SLEEP); if (level == 5) cmd.flags |= htole16(IWN_PS_FAST_PD); + /* Retrieve PCIe Active State Power Management (ASPM). */ tmp = pci_read_config(sc->sc_dev, sc->sc_cap_off + 0x10, 1); if (!(tmp & 0x1)) /* L0s Entry disabled. */ cmd.flags |= htole16(IWN_PS_PCI_PMGT); @@ -4155,22 +4437,30 @@ iwn_config(struct iwn_softc *sc) struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; struct iwn_bluetooth bluetooth; + uint32_t txmask; int error; uint16_t rxchain; - /* Set power saving level to CAM during initialization. */ - if ((error = iwn_set_pslevel(sc, 0, 0, 0)) != 0) { - device_printf(sc->sc_dev, - "%s: could not set power saving level, error %d\n", - __func__, error); - return error; + /* Configure valid TX chains for 5000 Series. */ + if (sc->hw_type != IWN_HW_REV_TYPE_4965) { + txmask = htole32(sc->txchainmask); + DPRINTF(sc, IWN_DEBUG_RESET, + "%s: configuring valid TX chains 0x%x\n", __func__, txmask); + error = iwn_cmd(sc, IWN5000_CMD_TX_ANT_CONFIG, &txmask, + sizeof txmask, 0); + if (error != 0) { + device_printf(sc->sc_dev, + "%s: could not configure valid TX chains, " + "error %d\n", __func__, error); + return error; + } } /* Configure bluetooth coexistence. */ memset(&bluetooth, 0, sizeof bluetooth); - bluetooth.flags = 3; - bluetooth.lead = 0xaa; - bluetooth.kill = 1; + bluetooth.flags = IWN_BT_COEX_MODE_4WIRE; + bluetooth.lead_time = IWN_BT_LEAD_TIME_DEF; + bluetooth.max_kill = IWN_BT_MAX_KILL_DEF; DPRINTF(sc, IWN_DEBUG_RESET, "%s: config bluetooth coexistence\n", __func__); error = iwn_cmd(sc, IWN_CMD_BT_COEX, &bluetooth, sizeof bluetooth, 0); @@ -4181,11 +4471,10 @@ iwn_config(struct iwn_softc *sc) return error; } - /* Configure adapter. */ + /* Set mode, channel, RX filter and enable RX. */ memset(&sc->rxon, 0, sizeof (struct iwn_rxon)); IEEE80211_ADDR_COPY(sc->rxon.myaddr, IF_LLADDR(ifp)); IEEE80211_ADDR_COPY(sc->rxon.wlap, IF_LLADDR(ifp)); - /* Set default channel. */ sc->rxon.chan = ieee80211_chan2ieee(ic, ic->ic_curchan); sc->rxon.flags = htole32(IWN_RXON_TSF | IWN_RXON_CTS_TO_SELF); if (IEEE80211_IS_CHAN_2GHZ(ic->ic_curchan)) @@ -4195,13 +4484,6 @@ iwn_config(struct iwn_softc *sc) sc->rxon.mode = IWN_MODE_STA; sc->rxon.filter = htole32(IWN_FILTER_MULTICAST); break; - case IEEE80211_M_IBSS: - case IEEE80211_M_AHDEMO: - sc->rxon.mode = IWN_MODE_IBSS; - break; - case IEEE80211_M_HOSTAP: - sc->rxon.mode = IWN_MODE_HOSTAP; - break; case IEEE80211_M_MONITOR: sc->rxon.mode = IWN_MODE_MONITOR; sc->rxon.filter = htole32(IWN_FILTER_MULTICAST | @@ -4215,37 +4497,47 @@ iwn_config(struct iwn_softc *sc) sc->rxon.ofdm_mask = 0xff; /* not yet negotiated */ sc->rxon.ht_single_mask = 0xff; sc->rxon.ht_dual_mask = 0xff; - rxchain = IWN_RXCHAIN_VALID(IWN_ANT_ABC) | IWN_RXCHAIN_IDLE_COUNT(2) | - IWN_RXCHAIN_MIMO_COUNT(2); + sc->rxon.ht_triple_mask = 0xff; + rxchain = + IWN_RXCHAIN_VALID(sc->rxchainmask) | + IWN_RXCHAIN_MIMO_COUNT(2) | + IWN_RXCHAIN_IDLE_COUNT(2); sc->rxon.rxchain = htole16(rxchain); DPRINTF(sc, IWN_DEBUG_RESET, "%s: setting configuration\n", __func__); - error = iwn_cmd(sc, IWN_CMD_CONFIGURE, &sc->rxon, hal->rxonsz, 0); + error = iwn_cmd(sc, IWN_CMD_RXON, &sc->rxon, hal->rxonsz, 0); + if (error != 0) { + device_printf(sc->sc_dev, + "%s: RXON command failed\n", __func__); + return error; + } + + error = iwn_add_broadcast_node(sc, 0); if (error != 0) { device_printf(sc->sc_dev, - "%s: configure command failed\n", __func__); + "%s: could not add broadcast node\n", __func__); return error; } - sc->sc_curchan = ic->ic_curchan; /* Configuration has changed, set TX power accordingly. */ - error = hal->set_txpower(sc, ic->ic_curchan, 0); + error = hal->set_txpower(sc, 0); if (error != 0) { device_printf(sc->sc_dev, "%s: could not set TX power\n", __func__); return error; } - error = iwn_add_broadcast_node(sc, ic->ic_curchan, 0); + error = iwn_set_critical_temp(sc); if (error != 0) { device_printf(sc->sc_dev, - "%s: could not add broadcast node\n", __func__); + "%s: ccould not set critical temperature\n", __func__); return error; } - error = iwn_set_critical_temp(sc); + /* Set power saving level to CAM during initialization. */ + error = iwn_set_pslevel(sc, 0, 0, 0); if (error != 0) { device_printf(sc->sc_dev, - "%s: could not set critical temperature\n", __func__); + "%s: could not set power saving level\n", __func__); return error; } return 0; @@ -4264,7 +4556,6 @@ iwn_scan(struct iwn_softc *sc) struct ieee80211_frame *wh; struct ieee80211_rateset *rs; struct ieee80211_channel *c; - enum ieee80211_phymode mode; int buflen, error, nrates; uint16_t rxchain; uint8_t *buf, *frm, txant; @@ -4286,14 +4577,16 @@ iwn_scan(struct iwn_softc *sc) hdr->quiet_threshold = htole16(1); /* min # of packets */ /* Select antennas for scanning. */ - rxchain = IWN_RXCHAIN_FORCE | IWN_RXCHAIN_VALID(IWN_ANT_ABC) | - IWN_RXCHAIN_MIMO(IWN_ANT_ABC); + rxchain = + IWN_RXCHAIN_VALID(sc->rxchainmask) | + IWN_RXCHAIN_FORCE_MIMO_SEL(sc->rxchainmask) | + IWN_RXCHAIN_DRIVER_FORCE; if (IEEE80211_IS_CHAN_A(ic->ic_curchan) && sc->hw_type == IWN_HW_REV_TYPE_4965) { /* Ant A must be avoided in 5GHz because of an HW bug. */ - rxchain |= IWN_RXCHAIN_SEL(IWN_ANT_B | IWN_ANT_C); + rxchain |= IWN_RXCHAIN_FORCE_SEL(IWN_ANT_BC); } else /* Use all available RX antennas. */ - rxchain |= IWN_RXCHAIN_SEL(IWN_ANT_ABC); + rxchain |= IWN_RXCHAIN_FORCE_SEL(sc->rxchainmask); hdr->rxchain = htole16(rxchain); hdr->filter = htole32(IWN_FILTER_MULTICAST | IWN_FILTER_BEACON); @@ -4303,17 +4596,18 @@ iwn_scan(struct iwn_softc *sc) tx->lifetime = htole32(IWN_LIFETIME_INFINITE); if (IEEE80211_IS_CHAN_A(ic->ic_curchan)) { - hdr->crc_threshold = htole16(1); /* Send probe requests at 6Mbps. */ tx->plcp = iwn_rates[IWN_RIDX_OFDM6].plcp; + rs = &ic->ic_sup_rates[IEEE80211_MODE_11A]; } else { hdr->flags = htole32(IWN_RXON_24GHZ | IWN_RXON_AUTO); /* Send probe requests at 1Mbps. */ tx->plcp = iwn_rates[IWN_RIDX_CCK1].plcp; tx->rflags = IWN_RFLAG_CCK; + rs = &ic->ic_sup_rates[IEEE80211_MODE_11G]; } /* Use the first valid TX antenna. */ - txant = IWN_LSB(sc->txantmsk); + txant = IWN_LSB(sc->txchainmask); tx->rflags |= IWN_RFLAG_ANT(txant); essid = (struct iwn_scan_essid *)(tx + 1); @@ -4322,6 +4616,7 @@ iwn_scan(struct iwn_softc *sc) 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 * copy & paste of what is done in net80211. @@ -4344,9 +4639,6 @@ iwn_scan(struct iwn_softc *sc) memcpy(frm, ss->ss_ssid[0].ssid, ss->ss_ssid[0].len); frm += ss->ss_ssid[0].len; - mode = ieee80211_chan2mode(ic->ic_curchan); - rs = &ic->ic_sup_rates[mode]; - /* Add supported rates IE. */ *frm++ = IEEE80211_ELEMID_RATES; nrates = rs->rs_nrates; @@ -4370,30 +4662,48 @@ iwn_scan(struct iwn_softc *sc) c = ic->ic_curchan; chan = (struct iwn_scan_chan *)frm; - chan->chan = ieee80211_chan2ieee(ic, c); + chan->chan = htole16(ieee80211_chan2ieee(ic, c)); chan->flags = 0; - if (!(c->ic_flags & IEEE80211_CHAN_PASSIVE)) - chan->flags |= htole32(IWN_CHAN_ACTIVE); if (ss->ss_nssid > 0) chan->flags |= htole32(IWN_CHAN_NPBREQS(1)); chan->dsp_gain = 0x6e; - if (IEEE80211_IS_CHAN_5GHZ(c)) { + if (IEEE80211_IS_CHAN_5GHZ(c) && + !(c->ic_flags & IEEE80211_CHAN_PASSIVE)) { chan->rf_gain = 0x3b; chan->active = htole16(24); chan->passive = htole16(110); - } else { + chan->flags |= htole32(IWN_CHAN_ACTIVE); + } else if (IEEE80211_IS_CHAN_5GHZ(c)) { + chan->rf_gain = 0x3b; + chan->active = htole16(24); + if (sc->rxon.associd) + chan->passive = htole16(78); + else + chan->passive = htole16(110); + hdr->crc_threshold = htole16(1); + } else if (!(c->ic_flags & IEEE80211_CHAN_PASSIVE)) { chan->rf_gain = 0x28; chan->active = htole16(36); chan->passive = htole16(120); + chan->flags |= htole32(IWN_CHAN_ACTIVE); + } else { + chan->rf_gain = 0x28; + chan->active = htole16(36); + if (sc->rxon.associd) + chan->passive = htole16(88); + else + chan->passive = htole16(120); + hdr->crc_threshold = htole16(1); } - hdr->nchan++; - chan++; - DPRINTF(sc, IWN_DEBUG_STATE, "%s: chan %u flags 0x%x rf_gain 0x%x " + 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++; buflen = (uint8_t *)chan - buf; hdr->len = htole16(buflen); @@ -4415,7 +4725,7 @@ iwn_auth(struct iwn_softc *sc, struct ieee80211vap *vap) sc->calib.state = IWN_CALIB_STATE_INIT; - /* Update adapter's configuration. */ + /* Update adapter configuration. */ IEEE80211_ADDR_COPY(sc->rxon.bssid, ni->ni_bssid); sc->rxon.chan = htole16(ieee80211_chan2ieee(ic, ni->ni_chan)); sc->rxon.flags = htole32(IWN_RXON_TSF | IWN_RXON_CTS_TO_SELF); @@ -4447,28 +4757,28 @@ iwn_auth(struct iwn_softc *sc, struct ieee80211vap *vap) le16toh(sc->rxon.rxchain), sc->rxon.myaddr, ":", sc->rxon.wlap, ":", sc->rxon.bssid, ":", le16toh(sc->rxon.associd), le32toh(sc->rxon.filter)); - error = iwn_cmd(sc, IWN_CMD_CONFIGURE, &sc->rxon, hal->rxonsz, 1); + error = iwn_cmd(sc, IWN_CMD_RXON, &sc->rxon, hal->rxonsz, 1); if (error != 0) { device_printf(sc->sc_dev, - "%s: could not configure, error %d\n", __func__, error); + "%s: RXON command failed, error %d\n", __func__, error); return error; } - sc->sc_curchan = ic->ic_curchan; /* Configuration has changed, set TX power accordingly. */ - if ((error = hal->set_txpower(sc, ni->ni_chan, 1)) != 0) { + error = hal->set_txpower(sc, 1); + if (error != 0) { device_printf(sc->sc_dev, "%s: could not set Tx power, error %d\n", __func__, error); return error; } /* - * Reconfiguring RXON clears the firmware's nodes table so we must + * Reconfiguring RXON clears the firmware nodes table so we must * add the broadcast node again. */ - error = iwn_add_broadcast_node(sc, ic->ic_curchan, 1); + error = iwn_add_broadcast_node(sc, 1); if (error != 0) { device_printf(sc->sc_dev, - "%s: 1 could not add broadcast node, error %d\n", + "%s: could not add broadcast node, error %d\n", __func__, error); return error; } @@ -4487,12 +4797,12 @@ iwn_run(struct iwn_softc *sc, struct ieee80211vap *vap) struct ieee80211com *ic = ifp->if_l2com; struct ieee80211_node *ni = vap->iv_bss; struct iwn_node_info node; - int error, maxrxampdu, ampdudensity; + int error; sc->calib.state = IWN_CALIB_STATE_INIT; if (ic->ic_opmode == IEEE80211_M_MONITOR) { - /* link LED blinks while monitoring */ + /* Link LED blinks while monitoring. */ iwn_set_led(sc, IWN_LED_LINK, 5, 5); return 0; } @@ -4503,14 +4813,33 @@ iwn_run(struct iwn_softc *sc, struct ieee80211vap *vap) return error; } - /* Update adapter's configuration. */ + /* Update adapter configuration. */ + IEEE80211_ADDR_COPY(sc->rxon.bssid, ni->ni_bssid); + sc->rxon.chan = htole16(ieee80211_chan2ieee(ic, ni->ni_chan)); sc->rxon.associd = htole16(IEEE80211_AID(ni->ni_associd)); /* Short preamble and slot time are negotiated when associating. */ sc->rxon.flags &= ~htole32(IWN_RXON_SHPREAMBLE | IWN_RXON_SHSLOT); + sc->rxon.flags |= htole32(IWN_RXON_TSF | IWN_RXON_CTS_TO_SELF); + if (IEEE80211_IS_CHAN_2GHZ(ni->ni_chan)) + sc->rxon.flags |= htole32(IWN_RXON_AUTO | IWN_RXON_24GHZ); + else + sc->rxon.flags &= ~htole32(IWN_RXON_AUTO | IWN_RXON_24GHZ); if (ic->ic_flags & IEEE80211_F_SHSLOT) sc->rxon.flags |= htole32(IWN_RXON_SHSLOT); if (ic->ic_flags & IEEE80211_F_SHPREAMBLE) sc->rxon.flags |= htole32(IWN_RXON_SHPREAMBLE); + if (IEEE80211_IS_CHAN_A(ni->ni_chan)) { + sc->rxon.cck_mask = 0; + sc->rxon.ofdm_mask = 0x15; + } else if (IEEE80211_IS_CHAN_B(ni->ni_chan)) { + sc->rxon.cck_mask = 0x03; + sc->rxon.ofdm_mask = 0; + } else { + /* XXX assume 802.11b/g */ + sc->rxon.cck_mask = 0x0f; + sc->rxon.ofdm_mask = 0x15; + } +#if 0 /* HT */ if (IEEE80211_IS_CHAN_HT(ni->ni_chan)) { sc->rxon.flags &= ~htole32(IWN_RXON_HT); if (IEEE80211_IS_CHAN_HT40U(ni->ni_chan)) @@ -4529,6 +4858,7 @@ iwn_run(struct iwn_softc *sc, struct ieee80211vap *vap) ampdudensity = MS(ni->ni_htparam, IEEE80211_HTCAP_MPDUDENSITY); } else maxrxampdu = ampdudensity = 0; +#endif sc->rxon.filter |= htole32(IWN_FILTER_BSS); DPRINTF(sc, IWN_DEBUG_STATE, @@ -4542,17 +4872,17 @@ iwn_run(struct iwn_softc *sc, struct ieee80211vap *vap) le16toh(sc->rxon.rxchain), sc->rxon.myaddr, ":", sc->rxon.wlap, ":", sc->rxon.bssid, ":", le16toh(sc->rxon.associd), le32toh(sc->rxon.filter)); - error = iwn_cmd(sc, IWN_CMD_CONFIGURE, &sc->rxon, hal->rxonsz, 1); + error = iwn_cmd(sc, IWN_CMD_RXON, &sc->rxon, hal->rxonsz, 1); if (error != 0) { 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. */ - error = hal->set_txpower(sc, ni->ni_chan, 1); + error = hal->set_txpower(sc, 1); if (error != 0) { device_printf(sc->sc_dev, "%s: could not set Tx power, error %d\n", __func__, error); @@ -4563,8 +4893,10 @@ iwn_run(struct iwn_softc *sc, struct ieee80211vap *vap) memset(&node, 0, sizeof node); IEEE80211_ADDR_COPY(node.macaddr, ni->ni_macaddr); node.id = IWN_ID_BSS; - node.htflags = htole32(IWN_AMDPU_SIZE_FACTOR(maxrxampdu) - | IWN_AMDPU_DENSITY(ampdudensity)); +#ifdef notyet + node.htflags = htole32(IWN_AMDPU_SIZE_FACTOR(3) | + IWN_AMDPU_DENSITY(5)); /* 2us */ +#endif DPRINTF(sc, IWN_DEBUG_STATE, "%s: add BSS node, id %d htflags 0x%x\n", __func__, node.id, le32toh(node.htflags)); error = hal->add_node(sc, &node, 1); @@ -4572,7 +4904,9 @@ iwn_run(struct iwn_softc *sc, struct ieee80211vap *vap) device_printf(sc->sc_dev, "could not add BSS node\n"); return error; } - error = iwn_set_link_quality(sc, node.id, ni->ni_chan, 1); + DPRINTF(sc, IWN_DEBUG_STATE, "setting link quality for node %d\n", + node.id); + error = iwn_set_link_quality(sc, node.id, 1); if (error != 0) { device_printf(sc->sc_dev, "%s: could not setup MRR for node %d, error %d\n", @@ -4599,6 +4933,221 @@ iwn_run(struct iwn_softc *sc, struct ieee80211vap *vap) #undef MS } +#if 0 /* HT */ +/* + * This function is called by upper layer when an ADDBA request is received + * from another STA and before the ADDBA response is sent. + */ +int +iwn_ampdu_rx_start(struct ieee80211com *ic, struct ieee80211_node *ni, + uint8_t tid) +{ + struct ieee80211_rx_ba *ba = &ni->ni_rx_ba[tid]; + struct iwn_softc *sc = ic->ic_softc; + struct iwn_node *wn = (void *)ni; + struct iwn_node_info node; + + memset(&node, 0, sizeof node); + node.id = wn->id; + node.control = IWN_NODE_UPDATE; + node.flags = IWN_FLAG_SET_ADDBA; + node.addba_tid = tid; + node.addba_ssn = htole16(ba->ba_winstart); + DPRINTF(sc, IWN_DEBUG_RECV, "ADDBA RA=%d TID=%d SSN=%d\n", + wn->id, tid, ba->ba_winstart)); + return sc->sc_hal->add_node(sc, &node, 1); +} + +/* + * This function is called by upper layer on teardown of an HT-immediate + * Block Ack agreement (eg. uppon receipt of a DELBA frame.) + */ +void +iwn_ampdu_rx_stop(struct ieee80211com *ic, struct ieee80211_node *ni, + uint8_t tid) +{ + struct iwn_softc *sc = ic->ic_softc; + struct iwn_node *wn = (void *)ni; + struct iwn_node_info node; + + memset(&node, 0, sizeof node); + node.id = wn->id; + node.control = IWN_NODE_UPDATE; + node.flags = IWN_FLAG_SET_DELBA; + node.delba_tid = tid; + DPRINTF(sc, IWN_DEBUG_RECV, "DELBA RA=%d TID=%d\n", wn->id, tid); + (void)sc->sc_hal->add_node(sc, &node, 1); +} + +/* + * This function is called by upper layer when an ADDBA response is received + * from another STA. + */ +int +iwn_ampdu_tx_start(struct ieee80211com *ic, struct ieee80211_node *ni, + uint8_t tid) +{ + struct ieee80211_tx_ba *ba = &ni->ni_tx_ba[tid]; + struct iwn_softc *sc = ic->ic_softc; + const struct iwn_hal *hal = sc->sc_hal; + struct iwn_node *wn = (void *)ni; + struct iwn_node_info node; + int error; + + /* Enable TX for the specified RA/TID. */ + wn->disable_tid &= ~(1 << tid); + memset(&node, 0, sizeof node); + node.id = wn->id; + node.control = IWN_NODE_UPDATE; + node.flags = IWN_FLAG_SET_DISABLE_TID; + node.disable_tid = htole16(wn->disable_tid); + error = hal->add_node(sc, &node, 1); + if (error != 0) + return error; + + if ((error = iwn_nic_lock(sc)) != 0) + return error; + hal->ampdu_tx_start(sc, ni, tid, ba->ba_winstart); + iwn_nic_unlock(sc); + return 0; +} + +void +iwn_ampdu_tx_stop(struct ieee80211com *ic, struct ieee80211_node *ni, + uint8_t tid) +{ + struct ieee80211_tx_ba *ba = &ni->ni_tx_ba[tid]; + struct iwn_softc *sc = ic->ic_softc; + int error; + + error = iwn_nic_lock(sc); + if (error != 0) + return; + sc->sc_hal->ampdu_tx_stop(sc, tid, ba->ba_winstart); + iwn_nic_unlock(sc); +} + +void +iwn4965_ampdu_tx_start(struct iwn_softc *sc, struct ieee80211_node *ni, + uint8_t tid, uint16_t ssn) +{ + struct iwn_node *wn = (void *)ni; + int qid = 7 + tid; + + /* Stop TX scheduler while we're changing its configuration. */ + iwn_prph_write(sc, IWN4965_SCHED_QUEUE_STATUS(qid), + IWN4965_TXQ_STATUS_CHGACT); + + /* Assign RA/TID translation to the queue. */ + iwn_mem_write_2(sc, sc->sched_base + IWN4965_SCHED_TRANS_TBL(qid), + wn->id << 4 | tid); + + /* Enable chain-building mode for the queue. */ + iwn_prph_setbits(sc, IWN4965_SCHED_QCHAIN_SEL, 1 << qid); + + /* Set starting sequence number from the ADDBA request. */ + IWN_WRITE(sc, IWN_HBUS_TARG_WRPTR, qid << 8 | (ssn & 0xff)); + iwn_prph_write(sc, IWN4965_SCHED_QUEUE_RDPTR(qid), ssn); + + /* Set scheduler window size. */ + iwn_mem_write(sc, sc->sched_base + IWN4965_SCHED_QUEUE_OFFSET(qid), + IWN_SCHED_WINSZ); + /* Set scheduler frame limit. */ + iwn_mem_write(sc, sc->sched_base + IWN4965_SCHED_QUEUE_OFFSET(qid) + 4, + IWN_SCHED_LIMIT << 16); + + /* Enable interrupts for the queue. */ + iwn_prph_setbits(sc, IWN4965_SCHED_INTR_MASK, 1 << qid); + + /* Mark the queue as active. */ + iwn_prph_write(sc, IWN4965_SCHED_QUEUE_STATUS(qid), + IWN4965_TXQ_STATUS_ACTIVE | IWN4965_TXQ_STATUS_AGGR_ENA | + iwn_tid2fifo[tid] << 1); +} + +void +iwn4965_ampdu_tx_stop(struct iwn_softc *sc, uint8_t tid, uint16_t ssn) +{ + int qid = 7 + tid; + + /* Stop TX scheduler while we're changing its configuration. */ + iwn_prph_write(sc, IWN4965_SCHED_QUEUE_STATUS(qid), + IWN4965_TXQ_STATUS_CHGACT); + + /* Set starting sequence number from the ADDBA request. */ + IWN_WRITE(sc, IWN_HBUS_TARG_WRPTR, qid << 8 | (ssn & 0xff)); + iwn_prph_write(sc, IWN4965_SCHED_QUEUE_RDPTR(qid), ssn); + + /* Disable interrupts for the queue. */ + iwn_prph_clrbits(sc, IWN4965_SCHED_INTR_MASK, 1 << qid); + + /* Mark the queue as inactive. */ + iwn_prph_write(sc, IWN4965_SCHED_QUEUE_STATUS(qid), + IWN4965_TXQ_STATUS_INACTIVE | iwn_tid2fifo[tid] << 1); +} + +void +iwn5000_ampdu_tx_start(struct iwn_softc *sc, struct ieee80211_node *ni, + uint8_t tid, uint16_t ssn) +{ + struct iwn_node *wn = (void *)ni; + int qid = 10 + tid; + + /* Stop TX scheduler while we're changing its configuration. */ + iwn_prph_write(sc, IWN5000_SCHED_QUEUE_STATUS(qid), + IWN5000_TXQ_STATUS_CHGACT); + + /* Assign RA/TID translation to the queue. */ + iwn_mem_write_2(sc, sc->sched_base + IWN5000_SCHED_TRANS_TBL(qid), + wn->id << 4 | tid); + + /* Enable chain-building mode for the queue. */ + iwn_prph_setbits(sc, IWN5000_SCHED_QCHAIN_SEL, 1 << qid); + + /* Enable aggregation for the queue. */ + iwn_prph_setbits(sc, IWN5000_SCHED_AGGR_SEL, 1 << qid); + + /* Set starting sequence number from the ADDBA request. */ + IWN_WRITE(sc, IWN_HBUS_TARG_WRPTR, qid << 8 | (ssn & 0xff)); + iwn_prph_write(sc, IWN5000_SCHED_QUEUE_RDPTR(qid), ssn); + + /* Set scheduler window size and frame limit. */ + iwn_mem_write(sc, sc->sched_base + IWN5000_SCHED_QUEUE_OFFSET(qid) + 4, + IWN_SCHED_LIMIT << 16 | IWN_SCHED_WINSZ); + + /* Enable interrupts for the queue. */ + iwn_prph_setbits(sc, IWN5000_SCHED_INTR_MASK, 1 << qid); + + /* Mark the queue as active. */ + iwn_prph_write(sc, IWN5000_SCHED_QUEUE_STATUS(qid), + IWN5000_TXQ_STATUS_ACTIVE | iwn_tid2fifo[tid]); +} + +void +iwn5000_ampdu_tx_stop(struct iwn_softc *sc, uint8_t tid, uint16_t ssn) +{ + int qid = 10 + tid; + + /* Stop TX scheduler while we're changing its configuration. */ + iwn_prph_write(sc, IWN5000_SCHED_QUEUE_STATUS(qid), + IWN5000_TXQ_STATUS_CHGACT); + + /* Disable aggregation for the queue. */ + iwn_prph_clrbits(sc, IWN5000_SCHED_AGGR_SEL, 1 << qid); + + /* Set starting sequence number from the ADDBA request. */ + IWN_WRITE(sc, IWN_HBUS_TARG_WRPTR, qid << 8 | (ssn & 0xff)); + iwn_prph_write(sc, IWN5000_SCHED_QUEUE_RDPTR(qid), ssn); + + /* Disable interrupts for the queue. */ + iwn_prph_clrbits(sc, IWN5000_SCHED_INTR_MASK, 1 << qid); + + /* Mark the queue as inactive. */ + iwn_prph_write(sc, IWN5000_SCHED_QUEUE_STATUS(qid), + IWN5000_TXQ_STATUS_INACTIVE | iwn_tid2fifo[tid]); +} +#endif + /* * Query calibration tables from the initialization firmware. We do this * only once at first boot. Called from a process context. @@ -4621,7 +5170,9 @@ iwn5000_query_calibration(struct iwn_softc *sc) return error; /* Wait at most two seconds for calibration to complete. */ - return msleep(sc, &sc->sc_mtx, PCATCH, "iwninit", 2 * hz); + if (!(sc->sc_flags & IWN_FLAG_CALIB_DONE)) + error = msleep(sc, &sc->sc_mtx, PCATCH, "iwninit", 2 * hz); + return error; } /* @@ -4651,6 +5202,33 @@ iwn5000_send_calibration(struct iwn_softc *sc) return 0; } +int +iwn5000_send_wimax_coex(struct iwn_softc *sc) +{ + struct iwn5000_wimax_coex wimax; + +#ifdef notyet + if (sc->hw_type == IWN_HW_REV_TYPE_6050) { + /* Enable WiMAX coexistence for combo adapters. */ + wimax.flags = + IWN_WIMAX_COEX_ASSOC_WA_UNMASK | + IWN_WIMAX_COEX_UNASSOC_WA_UNMASK | + IWN_WIMAX_COEX_STA_TABLE_VALID | + IWN_WIMAX_COEX_ENABLE; + memcpy(wimax.events, iwn6050_wimax_events, + sizeof iwn6050_wimax_events); + } else +#endif + { + /* Disable WiMAX coexistence. */ + wimax.flags = 0; + memset(wimax.events, 0, sizeof wimax.events); + } + DPRINTF(sc, IWN_DEBUG_RESET, "%s: Configuring WiMAX coexistence\n", + __func__); + return iwn_cmd(sc, IWN5000_CMD_WIMAX_COEX, &wimax, sizeof wimax, 0); +} + /* * This function is called after the runtime firmware notifies us of its * readiness (called in a process context.) @@ -4663,10 +5241,10 @@ iwn4965_post_alive(struct iwn_softc *sc) if ((error = iwn_nic_lock(sc)) != 0) return error; - /* Clear TX scheduler's state in SRAM. */ + /* Clear TX scheduler state in SRAM. */ sc->sched_base = iwn_prph_read(sc, IWN_SCHED_SRAM_ADDR); iwn_mem_set_region_4(sc, sc->sched_base + IWN4965_SCHED_CTX_OFF, 0, - IWN4965_SCHED_CTX_LEN); + IWN4965_SCHED_CTX_LEN / sizeof (uint32_t)); /* Set physical address of TX scheduler rings (1KB aligned.) */ iwn_prph_write(sc, IWN4965_SCHED_DRAM_ADDR, sc->sched_dma.paddr >> 10); @@ -4711,24 +5289,27 @@ iwn4965_post_alive(struct iwn_softc *sc) int iwn5000_post_alive(struct iwn_softc *sc) { - struct iwn5000_wimax_coex wimax; int error, qid; - if ((error = iwn_nic_lock(sc)) != 0) + /* Switch to using ICT interrupt mode. */ + iwn5000_ict_reset(sc); + + error = iwn_nic_lock(sc); + if (error != 0) return error; - /* Clear TX scheduler's state in SRAM. */ + /* Clear TX scheduler state in SRAM. */ sc->sched_base = iwn_prph_read(sc, IWN_SCHED_SRAM_ADDR); iwn_mem_set_region_4(sc, sc->sched_base + IWN5000_SCHED_CTX_OFF, 0, - IWN5000_SCHED_CTX_LEN); + IWN5000_SCHED_CTX_LEN / sizeof (uint32_t)); /* Set physical address of TX scheduler rings (1KB aligned.) */ iwn_prph_write(sc, IWN5000_SCHED_DRAM_ADDR, sc->sched_dma.paddr >> 10); IWN_SETBITS(sc, IWN_FH_TX_CHICKEN, IWN_FH_TX_CHICKEN_SCHED_RETRY); - /* Enable chain mode for all our 20 queues. */ - iwn_prph_write(sc, IWN5000_SCHED_QCHAIN_SEL, 0xfffff); + /* Enable chain mode for all queues, except command queue. */ + iwn_prph_write(sc, IWN5000_SCHED_QCHAIN_SEL, 0xfffef); iwn_prph_write(sc, IWN5000_SCHED_AGGR_SEL, 0); for (qid = 0; qid < IWN5000_NTXQUEUES; qid++) { @@ -4756,18 +5337,14 @@ iwn5000_post_alive(struct iwn_softc *sc) } iwn_nic_unlock(sc); - /* Configure WiMAX (IEEE 802.16e) coexistence. */ - memset(&wimax, 0, sizeof wimax); - DPRINTF(sc, IWN_DEBUG_RESET, "%s: Configuring WiMAX coexistence\n", - __func__); - error = iwn_cmd(sc, IWN5000_CMD_WIMAX_COEX, &wimax, sizeof wimax, 0); + /* Configure WiMAX coexistence for combo adapters. */ + error = iwn5000_send_wimax_coex(sc); if (error != 0) { device_printf(sc->sc_dev, "%s: could not configure WiMAX coexistence, error %d\n", __func__, error); return error; } - if (sc->hw_type != IWN_HW_REV_TYPE_5150) { struct iwn5000_phy_calib_crystal cmd; @@ -4789,21 +5366,19 @@ iwn5000_post_alive(struct iwn_softc *sc) return error; } } - if (sc->sc_flags & IWN_FLAG_FIRST_BOOT) { + if (!(sc->sc_flags & IWN_FLAG_CALIB_DONE)) { /* Query calibration from the initialization firmware. */ - if ((error = iwn5000_query_calibration(sc)) != 0) { + error = iwn5000_query_calibration(sc); + if (error != 0) { device_printf(sc->sc_dev, "%s: could not query calibration, error %d\n", __func__, error); return error; } /* - * We have the calibration results now so we can skip - * loading the initialization firmware next time. + * We have the calibration results now, reboot with the + * runtime firmware (call ourselves recursively!) */ - sc->sc_flags &= ~IWN_FLAG_FIRST_BOOT; - - /* Reboot (call ourselves recursively!) */ iwn_hw_stop(sc); error = iwn_hw_init(sc); } else { @@ -4976,8 +5551,8 @@ iwn5000_load_firmware(struct iwn_softc *sc) int error; /* Load the initialization firmware on first boot only. */ - fw = (sc->sc_flags & IWN_FLAG_FIRST_BOOT) ? - &sc->fw.init : &sc->fw.main; + fw = (sc->sc_flags & IWN_FLAG_CALIB_DONE) ? + &sc->fw.main : &sc->fw.init; error = iwn5000_load_firmware_section(sc, IWN_FW_TEXT_BASE, fw->text, fw->textsz); @@ -5005,8 +5580,9 @@ int iwn_read_firmware(struct iwn_softc *sc) { const struct iwn_hal *hal = sc->sc_hal; - const struct iwn_firmware_hdr *hdr; struct iwn_fw_info *fw = &sc->fw; + const uint32_t *ptr; + uint32_t rev; size_t size; IWN_UNLOCK(sc); @@ -5023,21 +5599,33 @@ iwn_read_firmware(struct iwn_softc *sc) IWN_LOCK(sc); size = sc->fw_fp->datasize; - if (size < sizeof (*hdr)) { + if (size < 28) { device_printf(sc->sc_dev, "%s: truncated firmware header: %zu bytes\n", __func__, size); return EINVAL; } - /* Extract firmware header information. */ - hdr = (const struct iwn_firmware_hdr *)sc->fw_fp->data; - fw->main.textsz = le32toh(hdr->main_textsz); - fw->main.datasz = le32toh(hdr->main_datasz); - fw->init.textsz = le32toh(hdr->init_textsz); - fw->init.datasz = le32toh(hdr->init_datasz); - fw->boot.textsz = le32toh(hdr->boot_textsz); - fw->boot.datasz = 0; + /* Process firmware header. */ + ptr = (const uint32_t *)sc->fw_fp->data; + rev = le32toh(*ptr++); + /* Check firmware API version. */ + if (IWN_FW_API(rev) <= 1) { + device_printf(sc->sc_dev, + "%s: bad firmware, need API version >=2\n", __func__); + return EINVAL; + } + if (IWN_FW_API(rev) >= 3) { + /* Skip build number (version 2 header). */ + size -= 4; + ptr++; + } + fw->main.textsz = le32toh(*ptr++); + fw->main.datasz = le32toh(*ptr++); + fw->init.textsz = le32toh(*ptr++); + fw->init.datasz = le32toh(*ptr++); + fw->boot.textsz = le32toh(*ptr++); + size -= 24; /* Sanity-check firmware header. */ if (fw->main.textsz > hal->fw_text_maxsz || @@ -5052,8 +5640,8 @@ iwn_read_firmware(struct iwn_softc *sc) } /* Check that all firmware sections fit. */ - if (size < sizeof (*hdr) + fw->main.textsz + fw->main.datasz + - fw->init.textsz + fw->init.datasz + fw->boot.textsz) { + if (fw->main.textsz + fw->main.datasz + fw->init.textsz + + fw->init.datasz + fw->boot.textsz > size) { device_printf(sc->sc_dev, "%s: firmware file too short: %zu bytes\n", __func__, size); @@ -5061,7 +5649,7 @@ iwn_read_firmware(struct iwn_softc *sc) } /* Get pointers to firmware sections. */ - fw->main.text = (const uint8_t *)(hdr + 1); + fw->main.text = (const uint8_t *)ptr; fw->main.data = fw->main.text + fw->main.textsz; fw->init.text = fw->main.data + fw->main.datasz; fw->init.data = fw->init.text + fw->init.textsz; @@ -5070,15 +5658,6 @@ iwn_read_firmware(struct iwn_softc *sc) return 0; } -void -iwn_unload_firmware(struct iwn_softc *sc) -{ - if (sc->fw_fp != NULL) { - firmware_put(sc->fw_fp, FIRMWARE_UNLOAD); - sc->fw_fp = NULL; - } -} - int iwn_clock_wait(struct iwn_softc *sc) { @@ -5088,10 +5667,10 @@ iwn_clock_wait(struct iwn_softc *sc) IWN_SETBITS(sc, IWN_GP_CNTRL, IWN_GP_CNTRL_INIT_DONE); /* Wait for clock stabilization. */ - for (ntries = 0; ntries < 25000; ntries++) { + for (ntries = 0; ntries < 2500; ntries++) { if (IWN_READ(sc, IWN_GP_CNTRL) & IWN_GP_CNTRL_MAC_CLOCK_READY) return 0; - DELAY(100); + DELAY(10); } device_printf(sc->sc_dev, "%s: timeout waiting for clock stabilization\n", __func__); @@ -5099,53 +5678,36 @@ iwn_clock_wait(struct iwn_softc *sc) } int -iwn4965_apm_init(struct iwn_softc *sc) -{ - int error; - - /* Disable L0s. */ - IWN_SETBITS(sc, IWN_GIO_CHICKEN, IWN_GIO_CHICKEN_DIS_L0S_TIMER); - IWN_SETBITS(sc, IWN_GIO_CHICKEN, IWN_GIO_CHICKEN_L1A_NO_L0S_RX); - - error = iwn_clock_wait(sc); - if (error != 0) - return error; - - error = iwn_nic_lock(sc); - if (error != 0) - return error; - - /* Enable DMA. */ - iwn_prph_write(sc, IWN_APMG_CLK_CTRL, - IWN_APMG_CLK_CTRL_DMA_CLK_RQT | IWN_APMG_CLK_CTRL_BSM_CLK_RQT); - DELAY(20); - - /* Disable L1. */ - iwn_prph_setbits(sc, IWN_APMG_PCI_STT, IWN_APMG_PCI_STT_L1A_DIS); - iwn_nic_unlock(sc); - - return 0; -} - -int -iwn5000_apm_init(struct iwn_softc *sc) +iwn_apm_init(struct iwn_softc *sc) { + uint32_t tmp; int error; - /* Disable L0s. */ + /* Disable L0s exit timer (NMI bug workaround.) */ IWN_SETBITS(sc, IWN_GIO_CHICKEN, IWN_GIO_CHICKEN_DIS_L0S_TIMER); + /* Don't wait for ICH L0s (ICH bug workaround.) */ IWN_SETBITS(sc, IWN_GIO_CHICKEN, IWN_GIO_CHICKEN_L1A_NO_L0S_RX); - /* Set Flow Handler wait threshold to the maximum. */ + /* Set FH wait threshold to max (HW bug under stress workaround.) */ IWN_SETBITS(sc, IWN_DBG_HPET_MEM, 0xffff0000); - /* Enable HAP to move adapter from L1a to L0s. */ + /* Enable HAP INTA to move adapter from L1a to L0s. */ IWN_SETBITS(sc, IWN_HW_IF_CONFIG, IWN_HW_IF_CONFIG_HAP_WAKE_L1A); - if (sc->hw_type != IWN_HW_REV_TYPE_6000 && + /* Retrieve PCIe Active State Power Management (ASPM). */ + tmp = pci_read_config(sc->sc_dev, sc->sc_cap_off + 0x10, 1); + /* Workaround for HW instability in PCIe L0->L0s->L1 transition. */ + if (tmp & 0x02) /* L1 Entry enabled. */ + IWN_SETBITS(sc, IWN_GIO, IWN_GIO_L0S_ENA); + else + IWN_CLRBITS(sc, IWN_GIO, IWN_GIO_L0S_ENA); + + if (sc->hw_type != IWN_HW_REV_TYPE_4965 && + sc->hw_type != IWN_HW_REV_TYPE_6000 && sc->hw_type != IWN_HW_REV_TYPE_6050) IWN_SETBITS(sc, IWN_ANA_PLL, IWN_ANA_PLL_INIT); + /* Wait for clock stabilization before accessing prph. */ error = iwn_clock_wait(sc); if (error != 0) return error; @@ -5154,11 +5716,19 @@ iwn5000_apm_init(struct iwn_softc *sc) if (error != 0) return error; - /* Enable DMA. */ - iwn_prph_write(sc, IWN_APMG_CLK_CTRL, IWN_APMG_CLK_CTRL_DMA_CLK_RQT); + if (sc->hw_type == IWN_HW_REV_TYPE_4965) { + /* Enable DMA and BSM (Bootstrap State Machine.) */ + iwn_prph_write(sc, IWN_APMG_CLK_EN, + IWN_APMG_CLK_CTRL_DMA_CLK_RQT | + IWN_APMG_CLK_CTRL_BSM_CLK_RQT); + } else { + /* Enable DMA. */ + iwn_prph_write(sc, IWN_APMG_CLK_EN, + IWN_APMG_CLK_CTRL_DMA_CLK_RQT); + } DELAY(20); - /* Disable L1. */ + /* Disable L1-Active. */ iwn_prph_setbits(sc, IWN_APMG_PCI_STT, IWN_APMG_PCI_STT_L1A_DIS); iwn_nic_unlock(sc); @@ -5170,6 +5740,7 @@ iwn_apm_stop_master(struct iwn_softc *sc) { int ntries; + /* Stop busmaster DMA activity. */ IWN_SETBITS(sc, IWN_RESET, IWN_RESET_STOP_MASTER); for (ntries = 0; ntries < 100; ntries++) { if (IWN_READ(sc, IWN_RESET) & IWN_RESET_MASTER_DISABLED) @@ -5185,6 +5756,7 @@ iwn_apm_stop(struct iwn_softc *sc) { iwn_apm_stop_master(sc); + /* Reset the entire device. */ IWN_SETBITS(sc, IWN_RESET, IWN_RESET_SW); DELAY(10); /* Clear "initialization complete" bit. */ @@ -5194,15 +5766,6 @@ iwn_apm_stop(struct iwn_softc *sc) int iwn4965_nic_config(struct iwn_softc *sc) { - uint32_t tmp; - - /* Retrieve PCIe Active State Power Management (ASPM). */ - tmp = pci_read_config(sc->sc_dev, sc->sc_cap_off + 0x10, 1); - if (tmp & 0x02) /* L1 Entry enabled. */ - IWN_SETBITS(sc, IWN_GIO, IWN_GIO_L0S_ENA); - else - IWN_CLRBITS(sc, IWN_GIO, IWN_GIO_L0S_ENA); - if (IWN_RFCFG_TYPE(sc->rfcfg) == 1) { /* * I don't believe this to be correct but this is what the @@ -5225,13 +5788,6 @@ iwn5000_nic_config(struct iwn_softc *sc) uint32_t tmp; int error; - /* Retrieve PCIe Active State Power Management (ASPM). */ - tmp = pci_read_config(sc->sc_dev, sc->sc_cap_off + 0x10, 1); - if (tmp & 0x02) /* L1 Entry enabled. */ - IWN_SETBITS(sc, IWN_GIO, IWN_GIO_L0S_ENA); - else - IWN_CLRBITS(sc, IWN_GIO, IWN_GIO_L0S_ENA); - if (IWN_RFCFG_TYPE(sc->rfcfg) < 3) { IWN_SETBITS(sc, IWN_HW_IF_CONFIG, IWN_RFCFG_TYPE(sc->rfcfg) | @@ -5245,7 +5801,24 @@ iwn5000_nic_config(struct iwn_softc *sc) if (error != 0) return error; iwn_prph_setbits(sc, IWN_APMG_PS, IWN_APMG_PS_EARLY_PWROFF_DIS); + + if (sc->hw_type == IWN_HW_REV_TYPE_1000) { + /* + * Select first Switching Voltage Regulator (1.32V) to + * solve a stability issue related to noisy DC2DC line + * in the silicon of 1000 Series. + */ + tmp = iwn_prph_read(sc, IWN_APMG_DIGITAL_SVR); + tmp &= ~IWN_APMG_DIGITAL_SVR_VOLTAGE_MASK; + tmp |= IWN_APMG_DIGITAL_SVR_VOLTAGE_1_32; + iwn_prph_write(sc, IWN_APMG_DIGITAL_SVR, tmp); + } iwn_nic_unlock(sc); + + if (sc->sc_flags & IWN_FLAG_INTERNAL_PA) { + /* Use internal power amplifier only. */ + IWN_WRITE(sc, IWN_GP_DRIVER, IWN_GP_DRIVER_RADIO_2X2_IPA); + } return 0; } @@ -5257,6 +5830,16 @@ iwn_hw_prepare(struct iwn_softc *sc) { int ntries; + /* Check if hardware is ready. */ + IWN_SETBITS(sc, IWN_HW_IF_CONFIG, IWN_HW_IF_CONFIG_NIC_READY); + for (ntries = 0; ntries < 5; ntries++) { + if (IWN_READ(sc, IWN_HW_IF_CONFIG) & + IWN_HW_IF_CONFIG_NIC_READY) + return 0; + DELAY(10); + } + + /* Hardware not ready, force into ready state. */ IWN_SETBITS(sc, IWN_HW_IF_CONFIG, IWN_HW_IF_CONFIG_PREPARE); for (ntries = 0; ntries < 15000; ntries++) { if (!(IWN_READ(sc, IWN_HW_IF_CONFIG) & @@ -5267,6 +5850,7 @@ iwn_hw_prepare(struct iwn_softc *sc) if (ntries == 15000) return ETIMEDOUT; + /* Hardware should be ready now. */ IWN_SETBITS(sc, IWN_HW_IF_CONFIG, IWN_HW_IF_CONFIG_NIC_READY); for (ntries = 0; ntries < 5; ntries++) { if (IWN_READ(sc, IWN_HW_IF_CONFIG) & @@ -5286,7 +5870,7 @@ iwn_hw_init(struct iwn_softc *sc) /* Clear pending interrupts. */ IWN_WRITE(sc, IWN_INT, 0xffffffff); - error = hal->apm_init(sc); + error = iwn_apm_init(sc); if (error != 0) { device_printf(sc->sc_dev, "%s: could not power ON adapter, error %d\n", @@ -5363,7 +5947,7 @@ iwn_hw_init(struct iwn_softc *sc) /* Enable interrupt coalescing. */ IWN_WRITE(sc, IWN_INT_COALESCING, 512 / 8); /* Enable interrupts. */ - IWN_WRITE(sc, IWN_MASK, IWN_INT_MASK); + IWN_WRITE(sc, IWN_INT_MASK, sc->int_mask); /* _Really_ make sure "radio off" bit is cleared! */ IWN_WRITE(sc, IWN_UCODE_GP1_CLR, IWN_UCODE_GP1_RFKILL); @@ -5398,9 +5982,10 @@ iwn_hw_stop(struct iwn_softc *sc) IWN_WRITE(sc, IWN_RESET, IWN_RESET_NEVO); /* Disable interrupts. */ - IWN_WRITE(sc, IWN_MASK, 0); + IWN_WRITE(sc, IWN_INT_MASK, 0); IWN_WRITE(sc, IWN_INT, 0xffffffff); IWN_WRITE(sc, IWN_FH_INT, 0xffffffff); + sc->sc_flags &= ~IWN_FLAG_USE_ICT; /* Make sure we no longer hold the NIC lock. */ iwn_nic_unlock(sc); @@ -5431,7 +6016,8 @@ iwn_hw_stop(struct iwn_softc *sc) iwn_reset_tx_ring(sc, &sc->txq[qid]); if (iwn_nic_lock(sc) == 0) { - iwn_prph_write(sc, IWN_APMG_CLK_DIS, IWN_APMG_CLK_DMA_RQT); + iwn_prph_write(sc, IWN_APMG_CLK_DIS, + IWN_APMG_CLK_CTRL_DMA_CLK_RQT); iwn_nic_unlock(sc); } DELAY(5); @@ -5448,8 +6034,6 @@ iwn_init_locked(struct iwn_softc *sc) IWN_LOCK_ASSERT(sc); - iwn_stop_locked(sc); - error = iwn_hw_prepare(sc); if (error != 0) { device_printf(sc->sc_dev, "%s: hardware not ready, eror %d\n", @@ -5457,13 +6041,19 @@ iwn_init_locked(struct iwn_softc *sc) goto fail; } + /* Initialize interrupt mask to default value. */ + sc->int_mask = IWN_INT_MASK_DEF; + sc->sc_flags &= ~IWN_FLAG_USE_ICT; + /* Check that the radio is not disabled by hardware switch. */ if (!(IWN_READ(sc, IWN_GP_CNTRL) & IWN_GP_CNTRL_RFKILL)) { device_printf(sc->sc_dev, - "%s: radio is disabled by hardware switch\n", - __func__); - error = EPERM; /* :-) */ - goto fail; + "radio is disabled by hardware switch\n"); + + /* Enable interrupts to get RF toggle notifications. */ + IWN_WRITE(sc, IWN_INT, 0xffffffff); + IWN_WRITE(sc, IWN_INT_MASK, sc->int_mask); + return; } /* Read firmware images from the filesystem. */ @@ -5477,6 +6067,8 @@ iwn_init_locked(struct iwn_softc *sc) /* Initialize hardware and upload firmware. */ error = iwn_hw_init(sc); + firmware_put(sc->fw_fp, FIRMWARE_UNLOAD); + sc->fw_fp = NULL; if (error != 0) { device_printf(sc->sc_dev, "%s: could not initialize hardware, error %d\n", @@ -5524,8 +6116,6 @@ iwn_stop_locked(struct iwn_softc *sc) IWN_LOCK_ASSERT(sc); - IWN_WRITE(sc, IWN_RESET, IWN_RESET_NEVO); - sc->sc_tx_timer = 0; callout_stop(&sc->sc_timer_to); ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE); @@ -5534,7 +6124,6 @@ iwn_stop_locked(struct iwn_softc *sc) iwn_hw_stop(sc); } - void iwn_stop(struct iwn_softc *sc) { @@ -5564,7 +6153,16 @@ iwn_scan_start(struct ieee80211com *ic) static void iwn_scan_end(struct ieee80211com *ic) { - /* ignore */ + struct ifnet *ifp = ic->ic_ifp; + struct iwn_softc *sc = ifp->if_softc; + struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); + + IWN_LOCK(sc); + if (vap->iv_state == IEEE80211_S_RUN) { + /* Set link LED to ON status if we are associated */ + iwn_set_led(sc, IWN_LED_LINK, 0, 1); + } + IWN_UNLOCK(sc); } /* @@ -5576,27 +6174,12 @@ iwn_set_channel(struct ieee80211com *ic) const struct ieee80211_channel *c = ic->ic_curchan; struct ifnet *ifp = ic->ic_ifp; struct iwn_softc *sc = ifp->if_softc; - struct ieee80211vap *vap; - 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); - } - } + sc->sc_rxtap.wr_chan_freq = htole16(c->ic_freq); + sc->sc_rxtap.wr_chan_flags = htole16(c->ic_flags); + sc->sc_txtap.wt_chan_freq = htole16(c->ic_freq); + sc->sc_txtap.wt_chan_flags = htole16(c->ic_flags); IWN_UNLOCK(sc); } @@ -5628,6 +6211,48 @@ iwn_scan_mindwell(struct ieee80211_scan_state *ss) /* NB: don't try to abort scan; wait for firmware to finish */ } +static struct iwn_eeprom_chan * +iwn_find_eeprom_channel(struct iwn_softc *sc, struct ieee80211_channel *c) +{ + int i, j; + + for (j = 0; j < 7; j++) { + for (i = 0; i < iwn_bands[j].nchan; i++) { + if (iwn_bands[j].chan[i] == c->ic_ieee) + return &sc->eeprom_channels[j][i]; + } + } + + return NULL; +} + +/* + * Enforce flags read from EEPROM. + */ +static int +iwn_setregdomain(struct ieee80211com *ic, struct ieee80211_regdomain *rd, + int nchan, struct ieee80211_channel chans[]) +{ + struct iwn_softc *sc = ic->ic_ifp->if_softc; + int i; + + for (i = 0; i < nchan; i++) { + struct ieee80211_channel *c = &chans[i]; + struct iwn_eeprom_chan *channel; + + channel = iwn_find_eeprom_channel(sc, c); + if (channel == NULL) { + if_printf(ic->ic_ifp, + "%s: invalid channel %u freq %u/0x%x\n", + __func__, c->ic_ieee, c->ic_freq, c->ic_flags); + return EINVAL; + } + c->ic_flags |= iwn_eeprom_channel_flags(channel); + } + + return 0; +} + static void iwn_hw_reset(void *arg0, int pending) { @@ -5635,6 +6260,7 @@ iwn_hw_reset(void *arg0, int pending) struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; + iwn_stop(sc); iwn_init(sc); ieee80211_notify_radio(ic, 1); } @@ -5643,8 +6269,14 @@ static void iwn_radio_on(void *arg0, int pending) { struct iwn_softc *sc = arg0; + struct ifnet *ifp = sc->sc_ifp; + struct ieee80211com *ic = ifp->if_l2com; + struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); - iwn_init(sc); + if (vap != NULL) { + iwn_init(sc); + ieee80211_init(vap); + } } static void @@ -5653,10 +6285,16 @@ iwn_radio_off(void *arg0, int pending) struct iwn_softc *sc = arg0; struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; + struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); + iwn_stop(sc); + if (vap != NULL) + ieee80211_stop(vap); + + /* Enable interrupts to get RF toggle notification. */ IWN_LOCK(sc); - ieee80211_notify_radio(ic, 0); - iwn_stop_locked(sc); + IWN_WRITE(sc, IWN_INT, 0xffffffff); + IWN_WRITE(sc, IWN_INT_MASK, sc->int_mask); IWN_UNLOCK(sc); } @@ -5686,8 +6324,13 @@ static int iwn_suspend(device_t dev) { struct iwn_softc *sc = device_get_softc(dev); + struct ifnet *ifp = sc->sc_ifp; + struct ieee80211com *ic = ifp->if_l2com; + struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); iwn_stop(sc); + if (vap != NULL) + ieee80211_stop(vap); return 0; } @@ -5696,11 +6339,19 @@ iwn_resume(device_t dev) { struct iwn_softc *sc = device_get_softc(dev); struct ifnet *ifp = sc->sc_ifp; + struct ieee80211com *ic = ifp->if_l2com; + struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); + /* Clear device-specific "PCI retry timeout" register (41h). */ pci_write_config(dev, 0x41, 0, 1); - if (ifp->if_flags & IFF_UP) + if (ifp->if_flags & IFF_UP) { iwn_init(sc); + if (vap != NULL) + ieee80211_init(vap); + if (ifp->if_drv_flags & IFF_DRV_RUNNING) + iwn_start(ifp); + } return 0; } @@ -5724,8 +6375,8 @@ iwn_intr_str(uint8_t cmd) 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_RXON: return "IWN_CMD_RXON"; + case IWN_CMD_RXON_ASSOC: return "IWN_CMD_RXON_ASSOC"; case IWN_CMD_EDCA_PARAMS: return "IWN_CMD_EDCA_PARAMS"; case IWN_CMD_TIMING: return "IWN_CMD_TIMING"; case IWN_CMD_LINK_QUALITY: return "IWN_CMD_LINK_QUALITY"; diff --git a/sys/dev/iwn/if_iwnreg.h b/sys/dev/iwn/if_iwnreg.h index ed514af..08ef36c 100644 --- a/sys/dev/iwn/if_iwnreg.h +++ b/sys/dev/iwn/if_iwnreg.h @@ -1,5 +1,5 @@ /* $FreeBSD$ */ -/* $OpenBSD: if_iwnreg.h,v 1.26 2009/05/29 08:25:45 damien Exp $ */ +/* $OpenBSD: if_iwnreg.h,v 1.34 2009/11/08 11:54:48 damien Exp $ */ /*- * Copyright (c) 2007, 2008 @@ -18,8 +18,6 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#define EDCA_NUM_AC 4 - #define IWN_TX_RING_COUNT 256 #define IWN_TX_RING_LOMARK 192 #define IWN_TX_RING_HIMARK 224 @@ -34,6 +32,9 @@ #define IWN_SRVC_DMACHNL 9 +#define IWN_ICT_SIZE 4096 +#define IWN_ICT_COUNT (IWN_ICT_SIZE / sizeof (uint32_t)) + /* Maximum number of DMA segments for TX. */ #define IWN_MAX_SCATTER 20 @@ -57,8 +58,9 @@ */ #define IWN_HW_IF_CONFIG 0x000 #define IWN_INT_COALESCING 0x004 +#define IWN_INT_PERIODIC 0x005 /* use IWN_WRITE_1 */ #define IWN_INT 0x008 -#define IWN_MASK 0x00c +#define IWN_INT_MASK 0x00c #define IWN_FH_INT 0x010 #define IWN_RESET 0x020 #define IWN_GP_CNTRL 0x024 @@ -67,19 +69,23 @@ #define IWN_EEPROM_GP 0x030 #define IWN_OTP_GP 0x034 #define IWN_GIO 0x03c +#define IWN_GP_DRIVER 0x050 #define IWN_UCODE_GP1_CLR 0x05c #define IWN_LED 0x094 +#define IWN_DRAM_INT_TBL 0x0a0 #define IWN_GIO_CHICKEN 0x100 #define IWN_ANA_PLL 0x20c +#define IWN_HW_REV_WA 0x22c #define IWN_DBG_HPET_MEM 0x240 +#define IWN_DBG_LINK_PWR_MGMT 0x250 #define IWN_MEM_RADDR 0x40c #define IWN_MEM_WADDR 0x410 #define IWN_MEM_WDATA 0x418 #define IWN_MEM_RDATA 0x41c -#define IWN_PRPH_WADDR 0x444 -#define IWN_PRPH_RADDR 0x448 -#define IWN_PRPH_WDATA 0x44c -#define IWN_PRPH_RDATA 0x450 +#define IWN_PRPH_WADDR 0x444 +#define IWN_PRPH_RADDR 0x448 +#define IWN_PRPH_WDATA 0x44c +#define IWN_PRPH_RDATA 0x450 #define IWN_HBUS_TARG_WRPTR 0x460 /* @@ -134,10 +140,12 @@ /* * NIC internal memory offsets. */ -#define IWN_CLOCK_CTL 0x3000 -#define IWN_APMG_CLK_CTRL 0x3004 +#define IWN_APMG_CLK_CTRL 0x3000 +#define IWN_APMG_CLK_EN 0x3004 #define IWN_APMG_CLK_DIS 0x3008 #define IWN_APMG_PS 0x300c +#define IWN_APMG_DIGITAL_SVR 0x3058 +#define IWN_APMG_ANALOG_SVR 0x306c #define IWN_APMG_PCI_STT 0x3010 #define IWN_BSM_WR_CTRL 0x3400 #define IWN_BSM_WR_MEM_SRC 0x3404 @@ -149,9 +157,6 @@ #define IWN_BSM_DRAM_DATA_SIZE 0x349c #define IWN_BSM_SRAM_BASE 0x3800 -/* Possible values for IWN_APMG_CLK_DIS. */ -#define IWN_APMG_CLK_DMA_RQT (1 << 9) - /* Possible flags for register IWN_HW_IF_CONFIG. */ #define IWN_HW_IF_CONFIG_4965_R (1 << 4) #define IWN_HW_IF_CONFIG_MAC_SI (1 << 8) @@ -162,6 +167,10 @@ #define IWN_HW_IF_CONFIG_PREPARE_DONE (1 << 25) #define IWN_HW_IF_CONFIG_PREPARE (1 << 27) +/* Possible values for register IWN_INT_PERIODIC. */ +#define IWN_INT_PERIODIC_DIS 0x00 +#define IWN_INT_PERIODIC_ENA 0xff + /* Possible flags for registers IWN_PRPH_RADDR/IWN_PRPH_WADDR. */ #define IWN_PRPH_DWORD ((sizeof (uint32_t) - 1) << 24) @@ -174,6 +183,7 @@ #define IWN_RESET_SW (1 << 7) #define IWN_RESET_MASTER_DISABLED (1 << 8) #define IWN_RESET_STOP_MASTER (1 << 9) +#define IWN_RESET_LINK_PWR_MGMT_DIS (1 << 31) /* Possible flags for register IWN_GP_CNTRL. */ #define IWN_GP_CNTRL_MAC_ACCESS_ENA (1 << 0) @@ -202,6 +212,11 @@ /* Possible flags for register IWN_GIO. */ #define IWN_GIO_L0S_ENA (1 << 1) +/* Possible flags for register IWN_GP_DRIVER. */ +#define IWN_GP_DRIVER_RADIO_3X3_HYB (0 << 0) +#define IWN_GP_DRIVER_RADIO_2X2_HYB (1 << 0) +#define IWN_GP_DRIVER_RADIO_2X2_IPA (2 << 0) + /* Possible flags for register IWN_UCODE_GP1_CLR. */ #define IWN_UCODE_GP1_RFKILL (1 << 1) #define IWN_UCODE_GP1_CMD_BLOCKED (1 << 2) @@ -212,6 +227,10 @@ #define IWN_LED_OFF 0x00000038 #define IWN_LED_ON 0x00000078 +/* Possible flags for register IWN_DRAM_INT_TBL. */ +#define IWN_DRAM_INT_TBL_WRAP_CHECK (1 << 27) +#define IWN_DRAM_INT_TBL_ENABLE (1 << 31) + /* Possible values for register IWN_ANA_PLL. */ #define IWN_ANA_PLL_INIT 0x00880300 @@ -229,12 +248,14 @@ #define IWN_INT_CT_REACHED (1 << 6) #define IWN_INT_RF_TOGGLED (1 << 7) #define IWN_INT_SW_ERR (1 << 25) +#define IWN_INT_SCHED (1 << 26) #define IWN_INT_FH_TX (1 << 27) +#define IWN_INT_RX_PERIODIC (1 << 28) #define IWN_INT_HW_ERR (1 << 29) #define IWN_INT_FH_RX (1 << 31) /* Shortcut. */ -#define IWN_INT_MASK \ +#define IWN_INT_MASK_DEF \ (IWN_INT_SW_ERR | IWN_INT_HW_ERR | IWN_INT_FH_TX | \ IWN_INT_FH_RX | IWN_INT_ALIVE | IWN_INT_WAKEUP | \ IWN_INT_SW_RX | IWN_INT_CT_REACHED | IWN_INT_RF_TOGGLED) @@ -301,7 +322,7 @@ #define IWN5000_TXQ_STATUS_INACTIVE 0x00ff0010 #define IWN5000_TXQ_STATUS_CHGACT (1 << 19) -/* Possible flags for register IWN_APMG_CLK_CTRL. */ +/* Possible flags for registers IWN_APMG_CLK_*. */ #define IWN_APMG_CLK_CTRL_DMA_CLK_RQT (1 << 9) #define IWN_APMG_CLK_CTRL_BSM_CLK_RQT (1 << 11) @@ -313,6 +334,13 @@ #define IWN_APMG_PS_PWR_SRC_MASK IWN_APMG_PS_PWR_SRC(3) #define IWN_APMG_PS_RESET_REQ (1 << 26) +/* Possible flags for register IWN_APMG_DIGITAL_SVR. */ +#define IWN_APMG_DIGITAL_SVR_VOLTAGE(x) (((x) & 0xf) << 5) +#define IWN_APMG_DIGITAL_SVR_VOLTAGE_MASK \ + IWN_APMG_DIGITAL_SVR_VOLTAGE(0xf) +#define IWN_APMG_DIGITAL_SVR_VOLTAGE_1_32 \ + IWN_APMG_DIGITAL_SVR_VOLTAGE(3) + /* Possible flags for IWN_APMG_PCI_STT. */ #define IWN_APMG_PCI_STT_L1A_DIS (1 << 11) @@ -362,6 +390,7 @@ struct iwn_rx_desc { #define IWN_RX_PHY 192 #define IWN_MPDU_RX_DONE 193 #define IWN_RX_DONE 195 +#define IWN_RX_COMPRESSED_BA 197 uint8_t flags; uint8_t idx; @@ -382,8 +411,8 @@ struct iwn_rx_desc { struct iwn_tx_cmd { uint8_t code; -#define IWN_CMD_CONFIGURE 16 -#define IWN_CMD_ASSOCIATE 17 +#define IWN_CMD_RXON 16 +#define IWN_CMD_RXON_ASSOC 17 #define IWN_CMD_EDCA_PARAMS 19 #define IWN_CMD_TIMING 20 #define IWN_CMD_ADD_NODE 24 @@ -394,8 +423,9 @@ struct iwn_tx_cmd { #define IWN5000_CMD_CALIB_CONFIG 101 #define IWN_CMD_SET_POWER_MODE 119 #define IWN_CMD_SCAN 128 +#define IWN_CMD_TXPOWER_DBM 149 #define IWN_CMD_TXPOWER 151 -#define IWN_CMD_TXPOWER_DBM 152 +#define IWN5000_CMD_TX_ANT_CONFIG 152 #define IWN_CMD_BT_COEX 155 #define IWN_CMD_GET_STATISTICS 156 #define IWN_CMD_SET_CRITICAL_TEMP 164 @@ -412,10 +442,12 @@ struct iwn_tx_cmd { #define IWN_ANT_A (1 << 0) #define IWN_ANT_B (1 << 1) #define IWN_ANT_C (1 << 2) -/* Shortcut. */ +/* Shortcuts. */ +#define IWN_ANT_AB (IWN_ANT_A | IWN_ANT_B) +#define IWN_ANT_BC (IWN_ANT_B | IWN_ANT_C) #define IWN_ANT_ABC (IWN_ANT_A | IWN_ANT_B | IWN_ANT_C) -/* Structure for command IWN_CMD_CONFIGURE. */ +/* Structure for command IWN_CMD_RXON. */ struct iwn_rxon { uint8_t myaddr[IEEE80211_ADDR_LEN]; uint16_t reserved1; @@ -431,10 +463,10 @@ struct iwn_rxon { uint8_t air; uint16_t rxchain; -#define IWN_RXCHAIN_FORCE (1 << 0) -#define IWN_RXCHAIN_VALID(x) ((x) << 1) -#define IWN_RXCHAIN_SEL(x) ((x) << 4) -#define IWN_RXCHAIN_MIMO(x) ((x) << 7) +#define IWN_RXCHAIN_DRIVER_FORCE (1 << 0) +#define IWN_RXCHAIN_VALID(x) (((x) & IWN_ANT_ABC) << 1) +#define IWN_RXCHAIN_FORCE_SEL(x) (((x) & IWN_ANT_ABC) << 4) +#define IWN_RXCHAIN_FORCE_MIMO_SEL(x) (((x) & IWN_ANT_ABC) << 7) #define IWN_RXCHAIN_IDLE_COUNT(x) ((x) << 10) #define IWN_RXCHAIN_MIMO_COUNT(x) ((x) << 12) #define IWN_RXCHAIN_MIMO_FORCE (1 << 14) @@ -443,23 +475,16 @@ struct iwn_rxon { uint8_t cck_mask; uint16_t associd; uint32_t flags; -#define IWN_RXON_24GHZ 0x00000001 /* band */ -#define IWN_RXON_CCK 0x00000002 /* modulation */ -#define IWN_RXON_AUTO 0x00000004 /* 2.4-only auto-detect */ -#define IWN_RXON_HTPROT 0x00000008 /* xmit with HT protection */ -#define IWN_RXON_SHSLOT 0x00000010 /* short slot time */ -#define IWN_RXON_SHPREAMBLE 0x00000020 /* short premable */ -#define IWN_RXON_NODIVERSITY 0x00000080 /* disable antenna diversity */ -#define IWN_RXON_ANTENNA_A 0x00000100 -#define IWN_RXON_ANTENNA_B 0x00000200 -#define IWN_RXON_RADAR 0x00001000 /* enable radar detect */ -#define IWN_RXON_NARROW 0x00002000 /* MKK narrow band select */ -#define IWN_RXON_TSF 0x00008000 -#define IWN_RXON_HT 0x06400000 -#define IWN_RXON_HT20 0x02000000 -#define IWN_RXON_HT40U 0x04000000 -#define IWN_RXON_HT40D 0x04400000 -#define IWN_RXON_CTS_TO_SELF 0x40000000 +#define IWN_RXON_24GHZ (1 << 0) +#define IWN_RXON_CCK (1 << 1) +#define IWN_RXON_AUTO (1 << 2) +#define IWN_RXON_SHSLOT (1 << 4) +#define IWN_RXON_SHPREAMBLE (1 << 5) +#define IWN_RXON_NODIVERSITY (1 << 7) +#define IWN_RXON_ANTENNA_A (1 << 8) +#define IWN_RXON_ANTENNA_B (1 << 9) +#define IWN_RXON_TSF (1 << 15) +#define IWN_RXON_CTS_TO_SELF (1 << 30) uint32_t filter; #define IWN_FILTER_PROMISC (1 << 0) @@ -473,7 +498,7 @@ struct iwn_rxon { uint8_t reserved4; uint8_t ht_single_mask; uint8_t ht_dual_mask; - /* The following fields are for 5000 Series only. */ + /* The following fields are for >=5000 Series only. */ uint8_t ht_triple_mask; uint8_t reserved5; uint16_t acquisition; @@ -504,7 +529,7 @@ struct iwn_edca_params { uint8_t aifsn; uint8_t reserved; uint16_t txoplimit; - } __packed ac[EDCA_NUM_AC]; + } __packed ac[WME_NUM_AC]; } __packed; /* Structure for command IWN_CMD_TIMING. */ @@ -595,12 +620,7 @@ struct iwn4965_node_info { uint32_t reserved7; } __packed; -#define IWN_RFLAG_HT (1 << 0) /* use HT modulation */ -#define IWN_RFLAG_CCK (1 << 1) /* use CCK modulation */ -#define IWN_RFLAG_HT40 (1 << 3) /* use dual-stream */ -#define IWN_RFLAG_SGI (1 << 5) /* use short GI */ -#define IWN_RFLAG_ANT_A (1 << 6) /* start on antenna port A */ -#define IWN_RFLAG_ANT_B (1 << 7) /* start on antenna port B */ +#define IWN_RFLAG_CCK (1 << 1) #define IWN_RFLAG_ANT(x) ((x) << 6) /* Structure for command IWN_CMD_TX_DATA. */ @@ -660,7 +680,7 @@ struct iwn_cmd_link_quality { uint8_t mimo; uint8_t antmsk_1stream; uint8_t antmsk_2stream; - uint8_t ridx[EDCA_NUM_AC]; + uint8_t ridx[WME_NUM_AC]; uint16_t ampdu_limit; uint8_t ampdu_threshold; uint8_t ampdu_max; @@ -688,7 +708,12 @@ struct iwn_cmd_led { /* Structure for command IWN5000_CMD_WIMAX_COEX. */ struct iwn5000_wimax_coex { uint32_t flags; - struct { +#define IWN_WIMAX_COEX_STA_TABLE_VALID (1 << 0) +#define IWN_WIMAX_COEX_UNASSOC_WA_UNMASK (1 << 2) +#define IWN_WIMAX_COEX_ASSOC_WA_UNMASK (1 << 3) +#define IWN_WIMAX_COEX_ENABLE (1 << 7) + + struct iwn5000_wimax_event { uint8_t request; uint8_t window; uint8_t reserved; @@ -804,11 +829,20 @@ struct iwn5000_cmd_txpower { /* Structure for command IWN_CMD_BLUETOOTH. */ struct iwn_bluetooth { uint8_t flags; - uint8_t lead; - uint8_t kill; +#define IWN_BT_COEX_DISABLE 0 +#define IWN_BT_COEX_MODE_2WIRE 1 +#define IWN_BT_COEX_MODE_3WIRE 2 +#define IWN_BT_COEX_MODE_4WIRE 3 + + uint8_t lead_time; +#define IWN_BT_LEAD_TIME_DEF 30 + + uint8_t max_kill; +#define IWN_BT_MAX_KILL_DEF 5 + uint8_t reserved; - uint32_t ack; - uint32_t cts; + uint32_t kill_ack; + uint32_t kill_cts; } __packed; /* Structure for command IWN_CMD_SET_CRITICAL_TEMP. */ @@ -850,7 +884,7 @@ struct iwn_phy_calib { #define IWN5000_PHY_CALIB_TX_IQ 11 #define IWN5000_PHY_CALIB_CRYSTAL 15 #define IWN5000_PHY_CALIB_BASE_BAND 16 -#define IWN5000_PHY_CALIB_TX_IQ_PERD 17 +#define IWN5000_PHY_CALIB_TX_IQ_PERIODIC 17 #define IWN5000_PHY_CALIB_RESET_NOISE_GAIN 18 #define IWN5000_PHY_CALIB_NOISE_GAIN 19 @@ -946,9 +980,9 @@ struct iwn_ucode_info { struct iwn4965_tx_stat { uint8_t nframes; - uint8_t killcnt; - uint8_t rtscnt; - uint8_t retrycnt; + uint8_t btkillcnt; + uint8_t rtsfailcnt; + uint8_t ackfailcnt; uint8_t rate; uint8_t rflags; uint16_t xrflags; @@ -960,9 +994,9 @@ struct iwn4965_tx_stat { struct iwn5000_tx_stat { uint8_t nframes; - uint8_t killcnt; - uint8_t rtscnt; - uint8_t retrycnt; + uint8_t btkillcnt; + uint8_t rtsfailcnt; + uint8_t ackfailcnt; uint8_t rate; uint8_t rflags; uint16_t xrflags; @@ -972,7 +1006,9 @@ struct iwn5000_tx_stat { uint32_t info; uint16_t seq; uint16_t len; - uint32_t tlc; + uint8_t tlc; + uint8_t ratid; + uint8_t fc[2]; uint16_t status; uint16_t sequence; } __packed; @@ -1027,6 +1063,18 @@ struct iwn_rx_stat { #define IWN_RSSI_TO_DBM 44 +/* Structure for IWN_RX_COMPRESSED_BA notification. */ +struct iwn_compressed_ba { + uint8_t macaddr[IEEE80211_ADDR_LEN]; + uint16_t reserved; + uint8_t id; + uint8_t tid; + uint16_t seq; + uint64_t bitmap; + uint16_t qid; + uint16_t ssn; +} __packed; + /* Structure for IWN_START_SCAN notification. */ struct iwn_start_scan { uint64_t tstamp; @@ -1078,7 +1126,7 @@ struct iwn_spectrum_notif { #define IWN_MEASUREMENT_FAILED 8 } __packed; -/* Structure for IWN_{RX,BEACON}_STATISTICS notification. */ +/* Structures for IWN_{RX,BEACON}_STATISTICS notification. */ struct iwn_rx_phy_stats { uint32_t ina; uint32_t fina; @@ -1208,16 +1256,6 @@ struct iwn_fw_dump { uint32_t time[2]; } __packed; -/* Firmware image file header. */ -struct iwn_firmware_hdr { - uint32_t version; - uint32_t main_textsz; - uint32_t main_datasz; - uint32_t init_textsz; - uint32_t init_datasz; - uint32_t boot_textsz; -} __packed; - #define IWN4965_FW_TEXT_MAXSZ ( 96 * 1024) #define IWN4965_FW_DATA_MAXSZ ( 40 * 1024) #define IWN5000_FW_TEXT_MAXSZ (256 * 1024) @@ -1226,6 +1264,8 @@ struct iwn_firmware_hdr { #define IWN4965_FWSZ (IWN4965_FW_TEXT_MAXSZ + IWN4965_FW_DATA_MAXSZ) #define IWN5000_FWSZ IWN5000_FW_TEXT_MAXSZ +#define IWN_FW_API(x) (((x) >> 8) & 0xff) + /* * Offsets into EEPROM. */ @@ -1253,6 +1293,7 @@ struct iwn_firmware_hdr { #define IWN5000_EEPROM_BAND5 0x03a #define IWN5000_EEPROM_BAND6 0x041 #define IWN5000_EEPROM_BAND7 0x049 +#define IWN6000_EEPROM_ENHINFO 0x054 #define IWN5000_EEPROM_CRYSTAL 0x128 #define IWN5000_EEPROM_TEMP 0x12a #define IWN5000_EEPROM_VOLT 0x12b @@ -1270,12 +1311,18 @@ struct iwn_eeprom_chan { #define IWN_EEPROM_CHAN_IBSS (1 << 1) #define IWN_EEPROM_CHAN_ACTIVE (1 << 3) #define IWN_EEPROM_CHAN_RADAR (1 << 4) -#define IWN_EEPROM_CHAN_WIDE (1 << 5) /* HT40 */ -#define IWN_EEPROM_CHAN_NARROW (1 << 6) /* HT20 */ int8_t maxpwr; } __packed; +struct iwn_eeprom_enhinfo { + uint16_t chan; + int8_t chain[3]; /* max power in half-dBm */ + uint8_t reserved; + int8_t mimo2; /* max power in half-dBm */ + int8_t mimo3; /* max power in half-dBm */ +} __packed; + #define IWN_NSAMPLES 3 struct iwn4965_eeprom_chan_samples { uint8_t num; @@ -1336,7 +1383,9 @@ static const struct iwn_chan_band { { 11, { 36, 44, 52, 60, 100, 108, 116, 124, 132, 149, 157 } } }; -#define IWN_RIDX_MCS 0x08 /* or'd to indicate MCS */ +#define IWN1000_OTP_NBLOCKS 3 +#define IWN6000_OTP_NBLOCKS 4 +#define IWN6050_OTP_NBLOCKS 7 /* HW rate indices. */ #define IWN_RIDX_CCK1 0 @@ -1489,7 +1538,7 @@ struct iwn_sensitivity_limits { */ static const struct iwn_sensitivity_limits iwn4965_sensitivity_limits = { 105, 140, - 170, 210, + 220, 270, 85, 120, 170, 210, 125, 200, @@ -1511,11 +1560,55 @@ static const struct iwn_sensitivity_limits iwn5000_sensitivity_limits = { 95 }; +static const struct iwn_sensitivity_limits iwn5150_sensitivity_limits = { + 105, 105, /* min = max for performance bug in DSP. */ + 220, 220, /* min = max for performance bug in DSP. */ + 90, 120, + 170, 210, + 125, 200, + 170, 400, + 95, + 95, + 95 +}; + +static const struct iwn_sensitivity_limits iwn6000_sensitivity_limits = { + 105, 145, + 192, 232, + 80, 145, + 128, 232, + 125, 175, + 160, 310, + 97, + 97, + 100 +}; + /* Map TID to TX scheduler's FIFO. */ static const uint8_t iwn_tid2fifo[] = { 1, 0, 0, 1, 2, 2, 3, 3, 7, 7, 7, 7, 7, 7, 7, 7, 3 }; +/* WiFi/WiMAX coexist event priority table for 6050. */ +static const struct iwn5000_wimax_event iwn6050_wimax_events[] = { + { 0x04, 0x03, 0x00, 0x00 }, + { 0x04, 0x03, 0x00, 0x03 }, + { 0x04, 0x03, 0x00, 0x03 }, + { 0x04, 0x03, 0x00, 0x03 }, + { 0x04, 0x03, 0x00, 0x00 }, + { 0x04, 0x03, 0x00, 0x07 }, + { 0x04, 0x03, 0x00, 0x00 }, + { 0x04, 0x03, 0x00, 0x03 }, + { 0x04, 0x03, 0x00, 0x03 }, + { 0x04, 0x03, 0x00, 0x00 }, + { 0x06, 0x03, 0x00, 0x07 }, + { 0x04, 0x03, 0x00, 0x00 }, + { 0x06, 0x06, 0x00, 0x03 }, + { 0x04, 0x03, 0x00, 0x07 }, + { 0x04, 0x03, 0x00, 0x00 }, + { 0x04, 0x03, 0x00, 0x00 } +}; + /* Firmware errors. */ static const char * const iwn_fw_errmsg[] = { "OK", @@ -1558,8 +1651,19 @@ static const char * const iwn_fw_errmsg[] = { #define IWN_WRITE(sc, reg, val) \ bus_space_write_4((sc)->sc_st, (sc)->sc_sh, (reg), (val)) +#define IWN_WRITE_1(sc, reg, val) \ + bus_space_write_1((sc)->sc_st, (sc)->sc_sh, (reg), (val)) + #define IWN_SETBITS(sc, reg, mask) \ IWN_WRITE(sc, reg, IWN_READ(sc, reg) | (mask)) #define IWN_CLRBITS(sc, reg, mask) \ IWN_WRITE(sc, reg, IWN_READ(sc, reg) & ~(mask)) + +#define IWN_BARRIER_WRITE(sc) \ + bus_space_barrier((sc)->sc_st, (sc)->sc_sh, 0, (sc)->sc_sz, \ + BUS_SPACE_BARRIER_WRITE) + +#define IWN_BARRIER_READ_WRITE(sc) \ + bus_space_barrier((sc)->sc_st, (sc)->sc_sh, 0, (sc)->sc_sz, \ + BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE) diff --git a/sys/dev/iwn/if_iwnvar.h b/sys/dev/iwn/if_iwnvar.h index a5fb31f..432ece6 100644 --- a/sys/dev/iwn/if_iwnvar.h +++ b/sys/dev/iwn/if_iwnvar.h @@ -1,5 +1,5 @@ /* $FreeBSD$ */ -/* $OpenBSD: if_iwnvar.h,v 1.12 2009/05/29 08:25:45 damien Exp $ */ +/* $OpenBSD: if_iwnvar.h,v 1.16 2009/11/04 17:46:52 damien Exp $ */ /*- * Copyright (c) 2007, 2008 @@ -74,6 +74,7 @@ struct iwn_tx_ring { struct iwn_tx_desc *desc; struct iwn_tx_cmd *cmd; struct iwn_tx_data data[IWN_TX_RING_COUNT]; + bus_dma_tag_t data_dmat; int qid; int queued; int cur; @@ -92,13 +93,12 @@ struct iwn_rx_ring { uint32_t *desc; struct iwn_rx_status *stat; struct iwn_rx_data data[IWN_RX_RING_COUNT]; + bus_dma_tag_t data_dmat; int cur; }; struct iwn_node { struct ieee80211_node ni; /* must be the first */ -#define IWN_NODE(_ni) ((struct iwn_node *)(_ni)) - struct ieee80211_amrr_node amn; uint16_t disable_tid; uint8_t id; @@ -161,21 +161,24 @@ struct iwn_hal { int (*load_firmware)(struct iwn_softc *); void (*read_eeprom)(struct iwn_softc *); int (*post_alive)(struct iwn_softc *); - int (*apm_init)(struct iwn_softc *); int (*nic_config)(struct iwn_softc *); void (*update_sched)(struct iwn_softc *, int, int, uint8_t, uint16_t); int (*get_temperature)(struct iwn_softc *); int (*get_rssi)(struct iwn_softc *, struct iwn_rx_stat *); - int (*set_txpower)(struct iwn_softc *, - struct ieee80211_channel *, int); + int (*set_txpower)(struct iwn_softc *, int); int (*init_gains)(struct iwn_softc *); int (*set_gains)(struct iwn_softc *); int (*add_node)(struct iwn_softc *, struct iwn_node_info *, int); void (*tx_done)(struct iwn_softc *, struct iwn_rx_desc *, struct iwn_rx_data *); - const struct iwn_sensitivity_limits *limits; +#if 0 /* HT */ + void (*ampdu_tx_start)(struct iwn_softc *, + struct ieee80211_node *, uint8_t, uint16_t); + void (*ampdu_tx_stop)(struct iwn_softc *, uint8_t, + uint16_t); +#endif int ntxqs; int ndmachnls; uint8_t broadcast_id; @@ -202,7 +205,7 @@ struct iwn_softc { struct ifnet *sc_ifp; int sc_debug; - /* locks */ + /* Locks */ struct mtx sc_mtx; /* Bus */ @@ -215,11 +218,15 @@ struct iwn_softc { u_int sc_flags; #define IWN_FLAG_HAS_5GHZ (1 << 0) #define IWN_FLAG_HAS_OTPROM (1 << 1) -#define IWN_FLAG_FIRST_BOOT (1 << 2) +#define IWN_FLAG_CALIB_DONE (1 << 2) +#define IWN_FLAG_USE_ICT (1 << 3) +#define IWN_FLAG_INTERNAL_PA (1 << 4) uint8_t hw_type; const struct iwn_hal *sc_hal; const char *fwname; + const struct iwn_sensitivity_limits + *limits; /* TX scheduler rings. */ struct iwn_dma_info sched_dma; @@ -235,6 +242,11 @@ struct iwn_softc { /* Firmware DMA transfer. */ struct iwn_dma_info fw_dma; + /* ICT table. */ + struct iwn_dma_info ict_dma; + uint32_t *ict; + int ict_cur; + /* TX/RX rings. */ struct iwn_tx_ring txq[IWN5000_NTXQUEUES]; struct iwn_rx_ring rxq; @@ -249,7 +261,7 @@ struct iwn_softc { struct task sc_reinit_task; struct task sc_radioon_task; struct task sc_radiooff_task; - + int calib_cnt; struct iwn_calib_state calib; @@ -266,8 +278,10 @@ struct iwn_softc { int noise; uint32_t qfullmsk; + uint32_t prom_base; struct iwn4965_eeprom_band bands[IWN_NBANDS]; + struct iwn_eeprom_chan eeprom_channels[IWN_NBANDS][IWN_MAX_CHAN_PER_BAND]; uint16_t rfcfg; char eeprom_domain[4]; uint32_t eeprom_crystal; @@ -275,20 +289,21 @@ struct iwn_softc { int8_t maxpwr2GHz; int8_t maxpwr5GHz; int8_t maxpwr[IEEE80211_CHAN_MAX]; + int8_t enh_maxpwr[35]; - uint32_t critical_temp; + int32_t temp_off; + uint32_t int_mask; uint8_t ntxchains; uint8_t nrxchains; - uint8_t txantmsk; - uint8_t rxantmsk; - uint8_t antmsk; + uint8_t txchainmask; + uint8_t rxchainmask; + uint8_t chainmask; struct callout sc_timer_to; int sc_tx_timer; struct iwn_rx_radiotap_header sc_rxtap; struct iwn_tx_radiotap_header sc_txtap; - const struct ieee80211_channel *sc_curchan; }; #define IWN_LOCK_INIT(_sc) \ |