diff options
Diffstat (limited to 'sys/dev/iwn/if_iwn.c')
-rw-r--r-- | sys/dev/iwn/if_iwn.c | 1879 |
1 files changed, 1265 insertions, 614 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"; |