diff options
author | Damien Bergamini <damien@cvs.openbsd.org> | 2008-10-13 16:37:11 +0000 |
---|---|---|
committer | Damien Bergamini <damien@cvs.openbsd.org> | 2008-10-13 16:37:11 +0000 |
commit | 2e00c710ae1bbf327ef6802bff993cdde1213b5c (patch) | |
tree | df95b6e6b44fd75b53341e63aac665dac7aab5a2 /sys/dev/pci | |
parent | 87f3d4865508c4ef3923fe163c5bb15467715ee8 (diff) |
Almost complete rewrite of iwn(4).
Add support for Intel WiFi Link 5000 Series adapters (5100/5150/5300/5350).
Quite frankly, Intel made things unnecessarily difficult by gratuitously
changing firmware commands (adding new fields in the middle of a struct)
and some register offsets for the 5000 Series and by defining yet another
way of loading a firmware.
I had to write a hardware abstraction layer to manage those differences.
Committed over a 5300 adapter (3T3R).
Require an upgrade of the iwn-firmware package, even for 4965AGN users.
Not tested on 5100, 5150 and 5350 (test reports are more than welcomed.)
There should be no regression on the 4965AGN.
Diffstat (limited to 'sys/dev/pci')
-rw-r--r-- | sys/dev/pci/if_iwn.c | 4069 | ||||
-rw-r--r-- | sys/dev/pci/if_iwnreg.h | 954 | ||||
-rw-r--r-- | sys/dev/pci/if_iwnvar.h | 108 |
3 files changed, 3369 insertions, 1762 deletions
diff --git a/sys/dev/pci/if_iwn.c b/sys/dev/pci/if_iwn.c index 52a7f664970..4f269abc64a 100644 --- a/sys/dev/pci/if_iwn.c +++ b/sys/dev/pci/if_iwn.c @@ -1,7 +1,7 @@ -/* $OpenBSD: if_iwn.c,v 1.23 2008/08/27 09:05:03 damien Exp $ */ +/* $OpenBSD: if_iwn.c,v 1.24 2008/10/13 16:37:10 damien Exp $ */ /*- - * Copyright (c) 2007,2008 + * Copyright (c) 2007, 2008 * Damien Bergamini <damien.bergamini@free.fr> * * Permission to use, copy, modify, and distribute this software for any @@ -18,7 +18,8 @@ */ /* - * Driver for Intel Wireless WiFi Link 4965AGN 802.11 network adapters. + * Driver for Intel Wireless WiFi Link 4965 and Intel WiFi Link 5000 Series + * 802.11 network adapters. */ #include "bpfilter.h" @@ -67,21 +68,37 @@ static const struct pci_matchid iwn_devices[] = { { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_PRO_WL_4965AGN_1 }, - { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_PRO_WL_4965AGN_2 } + { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_PRO_WL_4965AGN_2 }, + { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_PRO_WL_5100AGN_1 }, + { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_PRO_WL_5100AGN_2 }, + { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_PRO_WL_5300AGN_1 }, + { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_PRO_WL_5300AGN_2 }, + { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_PRO_WL_5350AGN_1 }, + { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_PRO_WL_5350AGN_2 } }; int iwn_match(struct device *, void *, void *); void iwn_attach(struct device *, struct device *, void *); +const struct iwn_hal *iwn_hal_attach(struct iwn_softc *); #ifndef SMALL_KERNEL void iwn_sensor_attach(struct iwn_softc *); #endif +#if NBPFILTER > 0 void iwn_radiotap_attach(struct iwn_softc *); +#endif void iwn_power(int, void *); +int iwn_nic_lock(struct iwn_softc *); +void iwn_prph_write_region_4(struct iwn_softc *, uint32_t, + const uint32_t *, int); +void iwn_mem_set_region_4(struct iwn_softc *, uint32_t, uint32_t, + int); +int iwn_eeprom_lock(struct iwn_softc *); +int iwn_read_prom_data(struct iwn_softc *, uint32_t, void *, int); int iwn_dma_contig_alloc(bus_dma_tag_t, struct iwn_dma_info *, void **, bus_size_t, bus_size_t, int); void iwn_dma_contig_free(struct iwn_dma_info *); -int iwn_alloc_shared(struct iwn_softc *); -void iwn_free_shared(struct iwn_softc *); +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_fwmem(struct iwn_softc *); @@ -97,76 +114,107 @@ 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 *); +int iwn_read_eeprom(struct iwn_softc *); +void iwn4965_read_eeprom(struct iwn_softc *); +void iwn4965_print_power_group(struct iwn_softc *, int); +void iwn5000_read_eeprom(struct iwn_softc *); +void iwn_read_eeprom_channels(struct iwn_softc *, int, uint32_t); struct ieee80211_node *iwn_node_alloc(struct ieee80211com *); void iwn_newassoc(struct ieee80211com *, struct ieee80211_node *, int); int iwn_media_change(struct ifnet *); int iwn_newstate(struct ieee80211com *, enum ieee80211_state, int); -void iwn_mem_lock(struct iwn_softc *); -void iwn_mem_unlock(struct iwn_softc *); -uint32_t iwn_mem_read(struct iwn_softc *, uint32_t); -void iwn_mem_write(struct iwn_softc *, uint32_t, uint32_t); -void iwn_mem_write_region_4(struct iwn_softc *, uint32_t, - const uint32_t *, int); -int iwn_eeprom_lock(struct iwn_softc *); -void iwn_eeprom_unlock(struct iwn_softc *); -int iwn_read_prom_data(struct iwn_softc *, uint32_t, void *, int); -int iwn_load_microcode(struct iwn_softc *, const uint8_t *, int); -int iwn_load_firmware(struct iwn_softc *); -void iwn_calib_timeout(void *); void iwn_iter_func(void *, struct ieee80211_node *); +void iwn_calib_timeout(void *); void iwn_ampdu_rx_start(struct iwn_softc *, struct iwn_rx_desc *); -void iwn_rx_intr(struct iwn_softc *, struct iwn_rx_desc *, +void iwn_rx_done(struct iwn_softc *, struct iwn_rx_desc *, struct iwn_rx_data *); +void iwn5000_rx_calib_results(struct iwn_softc *, + struct iwn_rx_desc *); void iwn_rx_statistics(struct iwn_softc *, struct iwn_rx_desc *); -void iwn_tx_intr(struct iwn_softc *, struct iwn_rx_desc *); -void iwn_cmd_intr(struct iwn_softc *, struct iwn_rx_desc *); +void iwn4965_tx_done(struct iwn_softc *, struct iwn_rx_desc *); +void iwn5000_tx_done(struct iwn_softc *, struct iwn_rx_desc *); +void iwn_tx_done(struct iwn_softc *, struct iwn_rx_desc *, int, + uint32_t); +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 *); int iwn_intr(void *); -void iwn_read_eeprom(struct iwn_softc *); -void iwn_read_eeprom_channels(struct iwn_softc *, int); -void iwn_print_power_group(struct iwn_softc *, int); +void iwn4965_update_sched(struct iwn_softc *, int, int, uint8_t, + uint16_t); +void iwn5000_update_sched(struct iwn_softc *, int, int, uint8_t, + uint16_t); +void iwn5000_reset_sched(struct iwn_softc *, int, int); uint8_t iwn_plcp_signal(int); int iwn_tx_data(struct iwn_softc *, struct mbuf *, - struct ieee80211_node *, int); + struct ieee80211_node *); void iwn_start(struct ifnet *); void iwn_watchdog(struct ifnet *); int iwn_ioctl(struct ifnet *, u_long, caddr_t); int iwn_cmd(struct iwn_softc *, int, const void *, int, int); +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_setup_node_mrr(struct iwn_softc *, const struct ieee80211_node *, uint8_t); int iwn_set_fixed_rate(struct iwn_softc *, uint8_t, uint8_t, int); -int iwn_set_key(struct ieee80211com *, struct ieee80211_node *, - struct ieee80211_key *); void iwn_updateedca(struct ieee80211com *); void iwn_set_led(struct iwn_softc *, uint8_t, uint8_t, uint8_t); int iwn_set_critical_temp(struct iwn_softc *); -void iwn_enable_tsf(struct iwn_softc *, struct ieee80211_node *); -void iwn_power_calibration(struct iwn_softc *, int); -int iwn_set_txpower(struct iwn_softc *, - struct ieee80211_channel *, int); -int iwn_get_rssi(const struct iwn_rx_stat *); +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 *, int); +int iwn5000_set_txpower(struct iwn_softc *, int); +int iwn4965_get_rssi(const struct iwn_rx_stat *); +int iwn5000_get_rssi(const struct iwn_rx_stat *); int iwn_get_noise(const struct iwn_rx_general_stats *); -int iwn_get_temperature(struct iwn_softc *); +int iwn4965_get_temperature(struct iwn_softc *); +int iwn5000_get_temperature(struct iwn_softc *); int iwn_init_sensitivity(struct iwn_softc *); -void iwn_compute_differential_gain(struct iwn_softc *, +void iwn_collect_noise(struct iwn_softc *, const struct iwn_rx_general_stats *); +int iwn4965_init_gains(struct iwn_softc *); +int iwn5000_init_gains(struct iwn_softc *); +int iwn4965_set_gains(struct iwn_softc *); +int iwn5000_set_gains(struct iwn_softc *); void iwn_tune_sensitivity(struct iwn_softc *, const struct iwn_rx_stats *); int iwn_send_sensitivity(struct iwn_softc *); +int iwn_set_pslevel(struct iwn_softc *, int, int, int); +int iwn_config(struct iwn_softc *); +int iwn_scan(struct iwn_softc *, uint16_t); int iwn_auth(struct iwn_softc *); int iwn_run(struct iwn_softc *); -int iwn_scan(struct iwn_softc *, uint16_t); -int iwn_config(struct iwn_softc *); -void iwn_post_alive(struct iwn_softc *); -void iwn_stop_master(struct iwn_softc *); -int iwn_reset(struct iwn_softc *); -void iwn_hw_config(struct iwn_softc *); +int iwn4965_addba(struct iwn_softc *, struct ieee80211_node *, + int, int, int); +int iwn4965_delba(struct iwn_softc *, int, int, int); +int iwn5000_addba(struct iwn_softc *, struct ieee80211_node *, + int, int, int); +int iwn5000_delba(struct iwn_softc *, int, int, int); +int iwn5000_query_calibration(struct iwn_softc *); +int iwn5000_send_calibration(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 *, + int); +int iwn4965_load_firmware(struct iwn_softc *); +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 *); +int iwn_clock_wait(struct iwn_softc *); +int iwn4965_apm_init(struct iwn_softc *); +int iwn5000_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_init(struct iwn_softc *); +void iwn_hw_stop(struct iwn_softc *); int iwn_init(struct ifnet *); void iwn_stop(struct ifnet *, int); -#define IWN_DEBUG - #ifdef IWN_DEBUG #define DPRINTF(x) do { if (iwn_debug > 0) printf x; } while (0) #define DPRINTFN(n, x) do { if (iwn_debug >= (n)) printf x; } while (0) @@ -176,6 +224,62 @@ int iwn_debug = 0; #define DPRINTFN(n, x) #endif +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, + iwn4965_get_rssi, + iwn4965_set_txpower, + iwn4965_init_gains, + iwn4965_set_gains, + iwn4965_add_node, + iwn4965_tx_done, + "iwn-4965", + &iwn4965_sensitivity_limits, + IWN4965_NTXQUEUES, + IWN4965_ID_BROADCAST, + IWN4965_RXONSZ, + IWN4965_SCHEDSZ, + IWN4965_FW_TEXT_MAXSZ, + IWN4965_FW_DATA_MAXSZ, + IWN4965_FWSZ, + 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, + iwn5000_get_rssi, + iwn5000_set_txpower, + iwn5000_init_gains, + iwn5000_set_gains, + iwn5000_add_node, + iwn5000_tx_done, + "iwn-5000", + &iwn5000_sensitivity_limits, + IWN5000_NTXQUEUES, + IWN5000_ID_BROADCAST, + IWN5000_RXONSZ, + IWN5000_SCHEDSZ, + IWN5000_FW_TEXT_MAXSZ, + IWN5000_FW_DATA_MAXSZ, + IWN5000_FWSZ, + IWN5000_SCHED_TXFACT +}; + +struct cfdriver iwn_cd = { + NULL, "iwn", DV_IFNET +}; + struct cfattach iwn_ca = { sizeof (struct iwn_softc), iwn_match, iwn_attach }; @@ -184,12 +288,9 @@ int iwn_match(struct device *parent, void *match, void *aux) { return pci_matchbyid((struct pci_attach_args *)aux, iwn_devices, - sizeof (iwn_devices) / sizeof (iwn_devices[0])); + sizeof iwn_devices / sizeof iwn_devices[0]); } -/* Base Address Register */ -#define IWN_PCI_BAR0 0x10 - void iwn_attach(struct device *parent, struct device *self, void *aux) { @@ -197,19 +298,31 @@ iwn_attach(struct device *parent, struct device *self, void *aux) struct ieee80211com *ic = &sc->sc_ic; struct ifnet *ifp = &ic->ic_if; struct pci_attach_args *pa = aux; + const struct iwn_hal *hal; const char *intrstr; pci_intr_handle_t ih; - pcireg_t memtype, data; + pcireg_t memtype, reg; int i, error; sc->sc_pct = pa->pa_pc; sc->sc_pcitag = pa->pa_tag; sc->sc_dmat = pa->pa_dmat; - /* clear device specific PCI configuration register 0x41 */ - data = pci_conf_read(sc->sc_pct, sc->sc_pcitag, 0x40); - data &= ~0x0000ff00; - pci_conf_write(sc->sc_pct, sc->sc_pcitag, 0x40, data); + /* + * Get the offset of the PCI Express Capability Structure in PCI + * Configuration Space (the vendor driver hard-codes it as E0h.) + */ + error = pci_get_capability(sc->sc_pct, sc->sc_pcitag, + PCI_CAP_PCIEXPRESS, &sc->sc_cap_off, NULL); + if (error == 0) { + printf(": PCIe capability structure not found!\n"); + return; + } + + /* Clear device-specific "PCI retry timeout" register (41H). */ + reg = pci_conf_read(sc->sc_pct, sc->sc_pcitag, 0x40); + reg &= ~0xff00; + pci_conf_write(sc->sc_pct, sc->sc_pcitag, 0x40, reg); memtype = pci_mapreg_type(pa->pa_pc, pa->pa_tag, IWN_PCI_BAR0); error = pci_mapreg_map(pa, IWN_PCI_BAR0, memtype, 0, &sc->sc_st, @@ -219,11 +332,11 @@ iwn_attach(struct device *parent, struct device *self, void *aux) return; } + /* Install interrupt handler. */ if (pci_intr_map(pa, &ih) != 0) { printf(": could not map interrupt\n"); return; } - intrstr = pci_intr_string(sc->sc_pct, ih); sc->sc_ih = pci_intr_establish(sc->sc_pct, ih, IPL_NET, iwn_intr, sc, sc->sc_dev.dv_xname); @@ -236,80 +349,87 @@ iwn_attach(struct device *parent, struct device *self, void *aux) } printf(": %s", intrstr); - /* - * Put adapter into a known state. - */ - if ((error = iwn_reset(sc)) != 0) { - printf(": could not reset adapter\n"); + /* Attach Hardware Abstraction Layer. */ + if ((hal = iwn_hal_attach(sc)) == NULL) + return; + + /* Power ON adapter. */ + if ((error = hal->apm_init(sc)) != 0) { + printf(": could not power ON adapter\n"); return; } - /* - * Allocate DMA memory for firmware transfers. - */ + /* Read MAC address, channels, etc from EEPROM. */ + if ((error = iwn_read_eeprom(sc)) != 0) { + printf(": could not read EEPROM\n"); + return; + } + + /* Allocate DMA memory for firmware transfers. */ if ((error = iwn_alloc_fwmem(sc)) != 0) { - printf(": could not allocate firmware memory\n"); + printf(": could not allocate memory for firmware\n"); return; } - /* - * Allocate a "keep warm" page. - */ + /* Allocate "Keep Warm" page. */ if ((error = iwn_alloc_kw(sc)) != 0) { printf(": could not allocate keep warm page\n"); goto fail1; } - /* - * Allocate shared area (communication area). - */ - if ((error = iwn_alloc_shared(sc)) != 0) { - printf(": could not allocate shared area\n"); + /* Allocate TX scheduler "rings". */ + if ((error = iwn_alloc_sched(sc)) != 0) { + printf(": could not allocate TX scheduler rings\n"); goto fail2; } - /* - * Allocate Rx buffers and Tx/Rx rings. - */ + /* Allocate RX buffers. */ if ((error = iwn_alloc_rpool(sc)) != 0) { - printf(": could not allocate Rx buffers\n"); + printf(": could not allocate RX buffers\n"); goto fail3; } - for (i = 0; i < IWN_NTXQUEUES; i++) { + /* Allocate TX rings (16 on 4965AGN, 20 on 5000.) */ + for (i = 0; i < hal->ntxqs; i++) { if ((error = iwn_alloc_tx_ring(sc, &sc->txq[i], i)) != 0) { - printf(": could not allocate Tx ring %d\n", i); + printf(": could not allocate TX ring %d\n", i); goto fail4; } } + /* Allocate RX ring. */ if ((error = iwn_alloc_rx_ring(sc, &sc->rxq)) != 0) { - printf(": could not allocate Rx ring\n"); + printf(": could not allocate RX ring\n"); goto fail4; } + /* Power OFF adapter. */ + iwn_apm_stop(sc); + + printf(", %dT%dR, %.4s, address %s\n", sc->ntxchains, sc->nrxchains, + sc->eeprom_domain, ether_sprintf(ic->ic_myaddr)); + + /* Initialization firmware has not been loaded yet. */ + sc->sc_flags |= IWN_FLAG_FIRST_BOOT; + ic->ic_phytype = IEEE80211_T_OFDM; /* not only, but not used */ ic->ic_opmode = IEEE80211_M_STA; /* default to BSS mode */ ic->ic_state = IEEE80211_S_INIT; - /* set device capabilities */ + /* Set device capabilities. */ ic->ic_caps = - IEEE80211_C_WEP | /* s/w WEP */ + IEEE80211_C_WEP | /* WEP */ IEEE80211_C_RSN | /* WPA/RSN */ IEEE80211_C_MONITOR | /* monitor mode supported */ - IEEE80211_C_TXPMGT | /* tx power management */ IEEE80211_C_SHSLOT | /* short slot time supported */ IEEE80211_C_SHPREAMBLE; /* short preamble supported */ - /* read supported channels and MAC address from EEPROM */ - iwn_read_eeprom(sc); - - /* set supported .11a, .11b and .11g rates */ + /* Set supported rates. */ ic->ic_sup_rates[IEEE80211_MODE_11A] = ieee80211_std_rateset_11a; ic->ic_sup_rates[IEEE80211_MODE_11B] = ieee80211_std_rateset_11b; ic->ic_sup_rates[IEEE80211_MODE_11G] = ieee80211_std_rateset_11g; - /* IBSS channel undefined for now */ + /* IBSS channel undefined for now. */ ic->ic_ibss_chan = &ic->ic_channels[0]; ifp->if_softc = sc; @@ -325,12 +445,9 @@ iwn_attach(struct device *parent, struct device *self, void *aux) ieee80211_ifattach(ifp); ic->ic_node_alloc = iwn_node_alloc; ic->ic_newassoc = iwn_newassoc; -#ifdef notyet - ic->ic_set_key = iwn_set_key; -#endif ic->ic_updateedca = iwn_updateedca; - /* override state transition machine */ + /* Override 802.11 state transition machine. */ sc->sc_newstate = ic->ic_newstate; ic->ic_newstate = iwn_newstate; ieee80211_media_init(ifp, iwn_media_change, ieee80211_media_status); @@ -341,23 +458,62 @@ iwn_attach(struct device *parent, struct device *self, void *aux) #ifndef SMALL_KERNEL iwn_sensor_attach(sc); #endif +#if NBPFILTER > 0 iwn_radiotap_attach(sc); - +#endif timeout_set(&sc->calib_to, iwn_calib_timeout, sc); sc->powerhook = powerhook_establish(iwn_power, sc); return; - /* free allocated memory if something failed during attachment */ + /* Free allocated memory if something failed during attachment. */ fail4: while (--i >= 0) iwn_free_tx_ring(sc, &sc->txq[i]); iwn_free_rpool(sc); -fail3: iwn_free_shared(sc); +fail3: iwn_free_sched(sc); fail2: iwn_free_kw(sc); fail1: iwn_free_fwmem(sc); } +const struct iwn_hal * +iwn_hal_attach(struct iwn_softc *sc) +{ + sc->hw_type = (IWN_READ(sc, IWN_HW_REV) >> 4) & 0xf; + + switch (sc->hw_type) { + case IWN_HW_REV_TYPE_4965: + sc->sc_hal = &iwn4965_hal; + sc->ntxchains = 2; + sc->nrxchains = 2; /* XXX should be 3? */ + sc->critical_temp = IWN_CTOK(110); + break; + case IWN_HW_REV_TYPE_5100: + sc->sc_hal = &iwn5000_hal; + sc->ntxchains = 1; + sc->nrxchains = 2; + sc->critical_temp = 110; + break; + case IWN_HW_REV_TYPE_5150: + sc->sc_hal = &iwn5000_hal; + sc->ntxchains = 1; + sc->nrxchains = 2; + sc->critical_temp = IWN_CTOK(110); + break; + case IWN_HW_REV_TYPE_5300: + case IWN_HW_REV_TYPE_5350: + sc->sc_hal = &iwn5000_hal; + sc->ntxchains = 3; + sc->nrxchains = 3; + sc->critical_temp = 110; + break; + default: + printf(": adapter type %d not supported\n", sc->hw_type); + return NULL; + } + return sc->sc_hal; +} + #ifndef SMALL_KERNEL /* * Attach the adapter's on-board thermal sensor to the sensors framework. @@ -368,7 +524,7 @@ iwn_sensor_attach(struct iwn_softc *sc) strlcpy(sc->sensordev.xname, sc->sc_dev.dv_xname, sizeof sc->sensordev.xname); sc->sensor.type = SENSOR_TEMP; - /* temperature invalid until interface is up */ + /* Temperature is not valid unless interface is up. */ sc->sensor.value = 0; sc->sensor.flags = SENSOR_FINVALID; sensor_attach(&sc->sensordev, &sc->sensor); @@ -376,13 +532,13 @@ iwn_sensor_attach(struct iwn_softc *sc) } #endif +#if NBPFILTER > 0 /* * Attach the interface to 802.11 radiotap. */ void iwn_radiotap_attach(struct iwn_softc *sc) { -#if NBPFILTER > 0 bpfattach(&sc->sc_drvbpf, &sc->sc_ic.ic_if, DLT_IEEE802_11_RADIO, sizeof (struct ieee80211_frame) + IEEE80211_RADIOTAP_HDRLEN); @@ -393,24 +549,24 @@ iwn_radiotap_attach(struct iwn_softc *sc) sc->sc_txtap_len = sizeof sc->sc_txtapu; sc->sc_txtap.wt_ihdr.it_len = htole16(sc->sc_txtap_len); sc->sc_txtap.wt_ihdr.it_present = htole32(IWN_TX_RADIOTAP_PRESENT); -#endif } +#endif void iwn_power(int why, void *arg) { struct iwn_softc *sc = arg; struct ifnet *ifp; - pcireg_t data; + pcireg_t reg; int s; if (why != PWR_RESUME) return; - /* clear device specific PCI configuration register 0x41 */ - data = pci_conf_read(sc->sc_pct, sc->sc_pcitag, 0x40); - data &= ~0x0000ff00; - pci_conf_write(sc->sc_pct, sc->sc_pcitag, 0x40, data); + /* Clear device-specific "PCI retry timeout" register (41H). */ + reg = pci_conf_read(sc->sc_pct, sc->sc_pcitag, 0x40); + reg &= ~0xff00; + pci_conf_write(sc->sc_pct, sc->sc_pcitag, 0x40, reg); s = splnet(); ifp = &sc->sc_ic.ic_if; @@ -423,6 +579,144 @@ iwn_power(int why, void *arg) } int +iwn_nic_lock(struct iwn_softc *sc) +{ + int ntries; + + /* Request exclusive access to NIC. */ + IWN_SETBITS(sc, IWN_GP_CNTRL, IWN_GP_CNTRL_MAC_ACCESS_REQ); + + /* Spin until we actually get the lock. */ + for (ntries = 0; ntries < 1000; ntries++) { + if ((IWN_READ(sc, IWN_GP_CNTRL) & + (IWN_GP_CNTRL_MAC_ACCESS_ENA | IWN_GP_CNTRL_SLEEP)) == + IWN_GP_CNTRL_MAC_ACCESS_ENA) + return 0; + DELAY(10); + } + return ETIMEDOUT; +} + +static __inline void +iwn_nic_unlock(struct iwn_softc *sc) +{ + IWN_CLRBITS(sc, IWN_GP_CNTRL, IWN_GP_CNTRL_MAC_ACCESS_REQ); +} + +static __inline uint32_t +iwn_prph_read(struct iwn_softc *sc, uint32_t addr) +{ + IWN_WRITE(sc, IWN_PRPH_RADDR, IWN_PRPH_DWORD | addr); + return IWN_READ(sc, IWN_PRPH_RDATA); +} + +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_WRITE(sc, IWN_PRPH_WDATA, data); +} + +void +iwn_prph_write_region_4(struct iwn_softc *sc, uint32_t addr, + const uint32_t *data, int count) +{ + for (; count > 0; count--, data++, addr += 4) + iwn_prph_write(sc, addr, *data); +} + +static __inline uint32_t +iwn_mem_read(struct iwn_softc *sc, uint32_t addr) +{ + IWN_WRITE(sc, IWN_MEM_RADDR, addr); + return IWN_READ(sc, IWN_MEM_RDATA); +} + +static __inline void +iwn_mem_write(struct iwn_softc *sc, uint32_t addr, uint32_t data) +{ + IWN_WRITE(sc, IWN_MEM_WADDR, addr); + IWN_WRITE(sc, IWN_MEM_WDATA, data); +} + +static __inline void +iwn_mem_write_2(struct iwn_softc *sc, uint32_t addr, uint16_t data) +{ + uint32_t tmp; + + tmp = iwn_mem_read(sc, addr & ~3); + if (addr & 3) + tmp = (tmp & 0x0000ffff) | data << 16; + else + tmp = (tmp & 0xffff0000) | data; + iwn_mem_write(sc, addr & ~3, tmp); +} + +void +iwn_mem_set_region_4(struct iwn_softc *sc, uint32_t addr, uint32_t val, + int count) +{ + for (; count > 0; count--, addr += 4) + iwn_mem_write(sc, addr, val); +} + +int +iwn_eeprom_lock(struct iwn_softc *sc) +{ + int i, ntries; + + for (i = 0; i < 100; i++) { + /* Request exclusive access to EEPROM. */ + IWN_SETBITS(sc, IWN_HW_IF_CONFIG, + IWN_HW_IF_CONFIG_EEPROM_LOCKED); + + /* Spin until we actually get the lock. */ + for (ntries = 0; ntries < 100; ntries++) { + if (IWN_READ(sc, IWN_HW_IF_CONFIG) & + IWN_HW_IF_CONFIG_EEPROM_LOCKED) + return 0; + DELAY(10); + } + } + return ETIMEDOUT; +} + +static __inline void +iwn_eeprom_unlock(struct iwn_softc *sc) +{ + IWN_CLRBITS(sc, IWN_HW_IF_CONFIG, IWN_HW_IF_CONFIG_EEPROM_LOCKED); +} + +int +iwn_read_prom_data(struct iwn_softc *sc, uint32_t addr, void *data, int count) +{ + uint8_t *out = data; + uint32_t val; + int ntries; + + for (; count > 0; count -= 2, addr++) { + IWN_WRITE(sc, IWN_EEPROM, addr << 2); + IWN_CLRBITS(sc, IWN_EEPROM, IWN_EEPROM_CMD); + + for (ntries = 0; ntries < 10; ntries++) { + val = IWN_READ(sc, IWN_EEPROM); + if (val & IWN_EEPROM_READ_VALID) + break; + DELAY(5); + } + if (ntries == 10) { + printf("%s: could not read EEPROM\n", + sc->sc_dev.dv_xname); + return ETIMEDOUT; + } + *out++ = val >> 16; + if (count > 1) + *out++ = val >> 24; + } + return 0; +} + +int iwn_dma_contig_alloc(bus_dma_tag_t tag, struct iwn_dma_info *dma, void **kvap, bus_size_t size, bus_size_t alignment, int flags) { @@ -476,26 +770,25 @@ iwn_dma_contig_free(struct iwn_dma_info *dma) } int -iwn_alloc_shared(struct iwn_softc *sc) +iwn_alloc_sched(struct iwn_softc *sc) { - /* must be aligned on a 1KB boundary */ - return iwn_dma_contig_alloc(sc->sc_dmat, &sc->shared_dma, - (void **)&sc->shared, sizeof (struct iwn_shared), 1024, - BUS_DMA_NOWAIT); + /* TX scheduler rings must be aligned on a 1KB boundary. */ + return iwn_dma_contig_alloc(sc->sc_dmat, &sc->sched_dma, + (void **)&sc->sched, sc->sc_hal->schedsz, 1024, BUS_DMA_NOWAIT); } void -iwn_free_shared(struct iwn_softc *sc) +iwn_free_sched(struct iwn_softc *sc) { - iwn_dma_contig_free(&sc->shared_dma); + iwn_dma_contig_free(&sc->sched_dma); } int iwn_alloc_kw(struct iwn_softc *sc) { - /* must be aligned on a 4KB boundary */ - return iwn_dma_contig_alloc(sc->sc_dmat, &sc->kw_dma, NULL, - PAGE_SIZE, PAGE_SIZE, BUS_DMA_NOWAIT); + /* "Keep Warm" page must be aligned on a 4KB boundary. */ + return iwn_dma_contig_alloc(sc->sc_dmat, &sc->kw_dma, NULL, 4096, + 4096, BUS_DMA_NOWAIT); } void @@ -507,10 +800,9 @@ iwn_free_kw(struct iwn_softc *sc) int iwn_alloc_fwmem(struct iwn_softc *sc) { - /* allocate enough contiguous space to store text and data */ + /* Must be aligned on a 16-byte boundary. */ return iwn_dma_contig_alloc(sc->sc_dmat, &sc->fw_dma, NULL, - IWN_FW_MAIN_TEXT_MAXSZ + IWN_FW_MAIN_DATA_MAXSZ, 16, - BUS_DMA_NOWAIT); + sc->sc_hal->fwsz, 16, BUS_DMA_NOWAIT); } void @@ -533,7 +825,7 @@ iwn_alloc_rbuf(struct iwn_softc *sc) /* * This is called automatically by the network stack when the mbuf to which - * our Rx buffer is attached is freed. + * our RX buffer is attached is freed. */ void iwn_free_rbuf(caddr_t buf, u_int size, void *arg) @@ -541,7 +833,7 @@ iwn_free_rbuf(caddr_t buf, u_int size, void *arg) struct iwn_rbuf *rbuf = arg; struct iwn_softc *sc = rbuf->sc; - /* put the buffer back in the free list */ + /* Put the RX buffer back in the free list. */ SLIST_INSERT_HEAD(&sc->rxq.freelist, rbuf, next); } @@ -551,21 +843,20 @@ iwn_alloc_rpool(struct iwn_softc *sc) struct iwn_rx_ring *ring = &sc->rxq; int i, error; - /* allocate a big chunk of DMA'able memory.. */ + /* Allocate a big chunk of DMA'able memory... */ error = iwn_dma_contig_alloc(sc->sc_dmat, &ring->buf_dma, NULL, - IWN_RBUF_COUNT * IWN_RBUF_SIZE, PAGE_SIZE, BUS_DMA_NOWAIT); + IWN_RBUF_COUNT * IWN_RBUF_SIZE, 4096, BUS_DMA_NOWAIT); if (error != 0) { - printf("%s: could not allocate Rx buffers DMA memory\n", + printf("%s: could not allocate RX buffers DMA memory\n", sc->sc_dev.dv_xname); return error; } - - /* ..and split it into chunks of "rbufsz" bytes */ + /* ...and split it into chunks of IWN_RBUF_SIZE bytes. */ SLIST_INIT(&ring->freelist); for (i = 0; i < IWN_RBUF_COUNT; i++) { struct iwn_rbuf *rbuf = &ring->rbuf[i]; - rbuf->sc = sc; /* backpointer for callbacks */ + rbuf->sc = sc; /* Backpointer for callbacks. */ rbuf->vaddr = ring->buf_dma.vaddr + i * IWN_RBUF_SIZE; rbuf->paddr = ring->buf_dma.paddr + i * IWN_RBUF_SIZE; @@ -583,21 +874,33 @@ iwn_free_rpool(struct iwn_softc *sc) int iwn_alloc_rx_ring(struct iwn_softc *sc, struct iwn_rx_ring *ring) { + bus_size_t size; int i, error; ring->cur = 0; + /* Allocate RX descriptors (256-byte aligned.) */ + size = IWN_RX_RING_COUNT * sizeof (uint32_t); error = iwn_dma_contig_alloc(sc->sc_dmat, &ring->desc_dma, - (void **)&ring->desc, IWN_RX_RING_COUNT * sizeof (uint32_t), - IWN_RING_DMA_ALIGN, BUS_DMA_NOWAIT); + (void **)&ring->desc, size, 256, BUS_DMA_NOWAIT); + if (error != 0) { + printf("%s: could not allocate RX ring DMA memory\n", + sc->sc_dev.dv_xname); + goto fail; + } + + /* Allocate RX status area (16-byte aligned.) */ + error = iwn_dma_contig_alloc(sc->sc_dmat, &ring->stat_dma, + (void **)&ring->stat, sizeof (struct iwn_rx_status), 16, + BUS_DMA_NOWAIT); if (error != 0) { - printf("%s: could not allocate rx ring DMA memory\n", + printf("%s: could not allocate RX status DMA memory\n", sc->sc_dev.dv_xname); goto fail; } /* - * Setup Rx buffers. + * Allocate RX buffers. */ for (i = 0; i < IWN_RX_RING_COUNT; i++) { struct iwn_rx_data *data = &ring->data[i]; @@ -605,7 +908,7 @@ iwn_alloc_rx_ring(struct iwn_softc *sc, struct iwn_rx_ring *ring) MGETHDR(data->m, M_DONTWAIT, MT_DATA); if (data->m == NULL) { - printf("%s: could not allocate rx mbuf\n", + printf("%s: could not allocate RX mbuf\n", sc->sc_dev.dv_xname); error = ENOMEM; goto fail; @@ -613,19 +916,18 @@ iwn_alloc_rx_ring(struct iwn_softc *sc, struct iwn_rx_ring *ring) if ((rbuf = iwn_alloc_rbuf(sc)) == NULL) { m_freem(data->m); data->m = NULL; - printf("%s: could not allocate rx buffer\n", + printf("%s: could not allocate RX buffer\n", sc->sc_dev.dv_xname); error = ENOMEM; goto fail; } - /* attach Rx buffer to mbuf */ + /* Attach RX buffer to mbuf header. */ MEXTADD(data->m, rbuf->vaddr, IWN_RBUF_SIZE, 0, iwn_free_rbuf, rbuf); - /* Rx buffers are aligned on a 256-byte boundary */ + /* Set physical address of RX buffer (256-byte aligned.) */ ring->desc[i] = htole32(rbuf->paddr >> 8); } - return 0; fail: iwn_free_rx_ring(sc, ring); @@ -637,21 +939,18 @@ iwn_reset_rx_ring(struct iwn_softc *sc, struct iwn_rx_ring *ring) { int ntries; - iwn_mem_lock(sc); - - IWN_WRITE(sc, IWN_RX_CONFIG, 0); - for (ntries = 0; ntries < 100; ntries++) { - if (IWN_READ(sc, IWN_RX_STATUS) & IWN_RX_IDLE) - break; - DELAY(10); + if (iwn_nic_lock(sc) == 0) { + IWN_WRITE(sc, IWN_FH_RX_CONFIG, 0); + for (ntries = 0; ntries < 1000; ntries++) { + if (IWN_READ(sc, IWN_FH_RX_STATUS) & + IWN_FH_RX_STATUS_IDLE) + break; + DELAY(10); + } + iwn_nic_unlock(sc); } -#ifdef IWN_DEBUG - if (ntries == 100 && iwn_debug > 0) - printf("%s: timeout resetting Rx ring\n", sc->sc_dev.dv_xname); -#endif - iwn_mem_unlock(sc); - ring->cur = 0; + sc->last_rx_valid = 0; } void @@ -660,6 +959,7 @@ iwn_free_rx_ring(struct iwn_softc *sc, struct iwn_rx_ring *ring) int i; iwn_dma_contig_free(&ring->desc_dma); + iwn_dma_contig_free(&ring->stat_dma); for (i = 0; i < IWN_RX_RING_COUNT; i++) { if (ring->data[i].m != NULL) @@ -670,6 +970,7 @@ iwn_free_rx_ring(struct iwn_softc *sc, struct iwn_rx_ring *ring) int iwn_alloc_tx_ring(struct iwn_softc *sc, struct iwn_tx_ring *ring, int qid) { + bus_addr_t paddr; bus_size_t size; int i, error; @@ -677,37 +978,49 @@ iwn_alloc_tx_ring(struct iwn_softc *sc, struct iwn_tx_ring *ring, int qid) ring->queued = 0; ring->cur = 0; + /* Allocate TX descriptors (256-byte aligned.) */ size = IWN_TX_RING_COUNT * sizeof (struct iwn_tx_desc); error = iwn_dma_contig_alloc(sc->sc_dmat, &ring->desc_dma, - (void **)&ring->desc, size, IWN_RING_DMA_ALIGN, BUS_DMA_NOWAIT); + (void **)&ring->desc, size, 256, BUS_DMA_NOWAIT); if (error != 0) { - printf("%s: could not allocate tx ring DMA memory\n", + printf("%s: could not allocate TX ring DMA memory\n", sc->sc_dev.dv_xname); goto fail; } + /* + * We only use rings 0 through 4 (4 EDCA + cmd) so there is no need + * to allocate commands space for other rings. + * XXX Do we really need to allocate descriptors for other rings? + */ + if (qid > 4) + return 0; size = IWN_TX_RING_COUNT * sizeof (struct iwn_tx_cmd); error = iwn_dma_contig_alloc(sc->sc_dmat, &ring->cmd_dma, (void **)&ring->cmd, size, 4, BUS_DMA_NOWAIT); if (error != 0) { - printf("%s: could not allocate tx cmd DMA memory\n", + printf("%s: could not allocate TX cmd DMA memory\n", sc->sc_dev.dv_xname); goto fail; } + paddr = ring->cmd_dma.paddr; for (i = 0; i < IWN_TX_RING_COUNT; i++) { struct iwn_tx_data *data = &ring->data[i]; + data->cmd_paddr = paddr; + data->scratch_paddr = paddr + 12; + paddr += sizeof (struct iwn_tx_cmd); + error = bus_dmamap_create(sc->sc_dmat, MCLBYTES, IWN_MAX_SCATTER - 1, MCLBYTES, 0, BUS_DMA_NOWAIT, &data->map); if (error != 0) { - printf("%s: could not create tx buf DMA map\n", + printf("%s: could not create TX buf DMA map\n", sc->sc_dev.dv_xname); goto fail; } } - return 0; fail: iwn_free_tx_ring(sc, ring); @@ -720,23 +1033,17 @@ iwn_reset_tx_ring(struct iwn_softc *sc, struct iwn_tx_ring *ring) uint32_t tmp; int i, ntries; - iwn_mem_lock(sc); - - IWN_WRITE(sc, IWN_TX_CONFIG(ring->qid), 0); - for (ntries = 0; ntries < 100; ntries++) { - tmp = IWN_READ(sc, IWN_TX_STATUS); - if ((tmp & IWN_TX_IDLE(ring->qid)) == IWN_TX_IDLE(ring->qid)) - break; - DELAY(10); - } -#ifdef IWN_DEBUG - if (ntries == 100 && iwn_debug > 1) { - printf("%s: timeout resetting Tx ring %d\n", - sc->sc_dev.dv_xname, ring->qid); + if (iwn_nic_lock(sc) == 0) { + IWN_WRITE(sc, IWN_FH_TX_CONFIG(ring->qid), 0); + for (ntries = 0; ntries < 200; ntries++) { + tmp = IWN_READ(sc, IWN_FH_TX_STATUS); + if ((tmp & IWN_FH_TX_STATUS_IDLE(ring->qid)) == + IWN_FH_TX_STATUS_IDLE(ring->qid)) + break; + DELAY(10); + } + iwn_nic_unlock(sc); } -#endif - iwn_mem_unlock(sc); - for (i = 0; i < IWN_TX_RING_COUNT; i++) { struct iwn_tx_data *data = &ring->data[i]; @@ -746,7 +1053,9 @@ iwn_reset_tx_ring(struct iwn_softc *sc, struct iwn_tx_ring *ring) data->m = NULL; } } - + /* Clear TX descriptors. */ + memset(ring->desc, 0, ring->desc_dma.size); + sc->qfullmsk &= ~(1 << ring->qid); ring->queued = 0; ring->cur = 0; } @@ -766,6 +1075,203 @@ iwn_free_tx_ring(struct iwn_softc *sc, struct iwn_tx_ring *ring) bus_dmamap_unload(sc->sc_dmat, data->map); m_freem(data->m); } + if (data->map != NULL) + bus_dmamap_destroy(sc->sc_dmat, data->map); + } +} + +int +iwn_read_eeprom(struct iwn_softc *sc) +{ + const struct iwn_hal *hal = sc->sc_hal; + struct ieee80211com *ic = &sc->sc_ic; + uint16_t val; + int error; + + if ((IWN_READ(sc, IWN_EEPROM_GP) & 0x6) == 0) { + printf("%s: bad EEPROM signature\n", sc->sc_dev.dv_xname); + return EIO; + } + if ((error = iwn_eeprom_lock(sc)) != 0) { + printf("%s: could not lock EEPROM (error=%d)\n", + sc->sc_dev.dv_xname, error); + return error; + } + + iwn_read_prom_data(sc, IWN_EEPROM_RFCFG, &val, 2); + sc->rfcfg = letoh16(val); + DPRINTF(("radio config=0x%04x\n", sc->rfcfg)); + + /* Read MAC address. */ + iwn_read_prom_data(sc, IWN_EEPROM_MAC, ic->ic_myaddr, 6); + + /* Read adapter-specific information from EEPROM. */ + hal->read_eeprom(sc); + + iwn_eeprom_unlock(sc); + return 0; +} + +void +iwn4965_read_eeprom(struct iwn_softc *sc) +{ + uint32_t addr; + uint16_t val; + int i; + + /* Read regulatory domain (4 ASCII characters.) */ + iwn_read_prom_data(sc, IWN4965_EEPROM_DOMAIN, sc->eeprom_domain, 4); + + /* 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); + sc->maxpwr2GHz = val & 0xff; + sc->maxpwr5GHz = val >> 8; + /* Check that EEPROM values are within valid range. */ + if (sc->maxpwr5GHz < 20 || sc->maxpwr5GHz > 50) + sc->maxpwr5GHz = 38; + if (sc->maxpwr2GHz < 20 || sc->maxpwr2GHz > 50) + sc->maxpwr2GHz = 38; + DPRINTF(("maxpwr 2GHz=%d 5GHz=%d\n", sc->maxpwr2GHz, sc->maxpwr5GHz)); + + /* Read samples for each TX power group. */ + iwn_read_prom_data(sc, IWN4965_EEPROM_BANDS, sc->bands, + sizeof sc->bands); + + /* Read voltage at which samples were taken. */ + iwn_read_prom_data(sc, IWN4965_EEPROM_VOLTAGE, &val, 2); + sc->eeprom_voltage = (int16_t)letoh16(val); + DPRINTF(("voltage=%d (in 0.3V)\n", sc->eeprom_voltage)); + +#ifdef IWN_DEBUG + /* Print samples. */ + if (iwn_debug > 0) { + for (i = 0; i < IWN_NBANDS; i++) + iwn4965_print_power_group(sc, i); + } +#endif +} + +#ifdef IWN_DEBUG +void +iwn4965_print_power_group(struct iwn_softc *sc, int i) +{ + struct iwn4965_eeprom_band *band = &sc->bands[i]; + struct iwn4965_eeprom_chan_samples *chans = band->chans; + int j, c; + + printf("===band %d===\n", i); + printf("chan lo=%d, chan hi=%d\n", band->lo, band->hi); + printf("chan1 num=%d\n", chans[0].num); + for (c = 0; c < 2; c++) { + for (j = 0; j < IWN_NSAMPLES; j++) { + printf("chain %d, sample %d: temp=%d gain=%d " + "power=%d pa_det=%d\n", c, j, + chans[0].samples[c][j].temp, + chans[0].samples[c][j].gain, + chans[0].samples[c][j].power, + chans[0].samples[c][j].pa_det); + } + } + printf("chan2 num=%d\n", chans[1].num); + for (c = 0; c < 2; c++) { + for (j = 0; j < IWN_NSAMPLES; j++) { + printf("chain %d, sample %d: temp=%d gain=%d " + "power=%d pa_det=%d\n", c, j, + chans[1].samples[c][j].temp, + chans[1].samples[c][j].gain, + chans[1].samples[c][j].power, + chans[1].samples[c][j].pa_det); + } + } +} +#endif + +void +iwn5000_read_eeprom(struct iwn_softc *sc) +{ + uint32_t base, addr; + uint16_t val; + int i; + + /* Read regulatory domain (4 ASCII characters.) */ + iwn_read_prom_data(sc, IWN5000_EEPROM_REG, &val, sizeof val); + base = letoh16(val); + iwn_read_prom_data(sc, base + IWN5000_EEPROM_DOMAIN, + sc->eeprom_domain, 4); + + /* 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, i, addr); + } + + /* Read crystal calibration. */ + iwn_read_prom_data(sc, IWN5000_EEPROM_CAL, &val, sizeof val); + base = letoh16(val); + iwn_read_prom_data(sc, base + IWN5000_EEPROM_CRYSTAL, + &sc->eeprom_crystal, sizeof (uint32_t)); + DPRINTF(("crystal calibration 0x%08x\n", sc->eeprom_crystal)); +} + +void +iwn_read_eeprom_channels(struct iwn_softc *sc, int n, uint32_t addr) +{ + struct ieee80211com *ic = &sc->sc_ic; + const struct iwn_chan_band *band = &iwn_bands[n]; + struct iwn_eeprom_chan channels[IWN_MAX_CHAN_PER_BAND]; + uint8_t chan; + 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)) + continue; + + chan = band->chan[i]; + + if (n == 0) { /* 2GHz band */ + ic->ic_channels[chan].ic_freq = + ieee80211_ieee2mhz(chan, IEEE80211_CHAN_2GHZ); + ic->ic_channels[chan].ic_flags = + IEEE80211_CHAN_CCK | IEEE80211_CHAN_OFDM | + IEEE80211_CHAN_DYN | IEEE80211_CHAN_2GHZ; + + } else { /* 5GHz band */ + /* + * Some adapters support channels 7, 8, 11 and 12 + * both in the 2GHz *and* 5GHz bands. + * Because of limitations in our net80211 stack, + * we can't support these channels in 5GHz band. + */ + if (chan <= 14) + continue; + + ic->ic_channels[chan].ic_freq = + ieee80211_ieee2mhz(chan, IEEE80211_CHAN_5GHZ); + ic->ic_channels[chan].ic_flags = IEEE80211_CHAN_A; + /* We have at least one valid 5GHZ channel. */ + sc->sc_flags |= IWN_FLAG_HAS_5GHZ; + } + + /* Is active scan allowed on this channel? */ + if (!(channels[i].flags & IWN_EEPROM_CHAN_ACTIVE)) { + ic->ic_channels[chan].ic_flags |= + IEEE80211_CHAN_PASSIVE; + } + + /* Save maximum allowed TX power for this channel. */ + sc->maxpwr[chan] = channels[i].maxpwr; + + DPRINTF(("adding chan %d flags=0x%x maxpwr=%d\n", + chan, channels[i].flags, sc->maxpwr[chan])); } } @@ -783,7 +1289,7 @@ iwn_newassoc(struct ieee80211com *ic, struct ieee80211_node *ni, int isnew) ieee80211_amrr_node_init(&sc->amrr, &((struct iwn_node *)ni)->amn); - /* set rate to some reasonable initial value */ + /* Set rate to some reasonable initial value. */ for (i = ni->ni_rates.rs_nrates - 1; i > 0 && (ni->ni_rates.rs_rates[i] & IEEE80211_RATE_VAL) > 72; i--); @@ -802,7 +1308,7 @@ iwn_media_change(struct ifnet *ifp) if ((ifp->if_flags & (IFF_UP | IFF_RUNNING)) == (IFF_UP | IFF_RUNNING)) { iwn_stop(ifp, 0); - iwn_init(ifp); + (void)iwn_init(ifp); } return 0; } @@ -818,10 +1324,10 @@ iwn_newstate(struct ieee80211com *ic, enum ieee80211_state nstate, int arg) switch (nstate) { case IEEE80211_S_SCAN: - /* make the link LED blink while we're scanning */ + /* Make the link LED blink while we're scanning. */ iwn_set_led(sc, IWN_LED_LINK, 20, 2); - if ((error = iwn_scan(sc, IEEE80211_CHAN_G)) != 0) { + if ((error = iwn_scan(sc, IEEE80211_CHAN_2GHZ)) != 0) { printf("%s: could not initiate scan\n", sc->sc_dev.dv_xname); return error; @@ -834,9 +1340,9 @@ iwn_newstate(struct ieee80211com *ic, enum ieee80211_state nstate, int arg) break; /* FALLTHROUGH */ case IEEE80211_S_AUTH: - /* reset state to handle reassociations correctly */ - sc->config.associd = 0; - sc->config.filter &= ~htole32(IWN_FILTER_BSS); + /* Reset state to handle reassociations correctly. */ + sc->rxon.associd = 0; + sc->rxon.filter &= ~htole32(IWN_FILTER_BSS); sc->calib.state = IWN_CALIB_STATE_INIT; if ((error = iwn_auth(sc)) != 0) { @@ -862,281 +1368,13 @@ iwn_newstate(struct ieee80211com *ic, enum ieee80211_state nstate, int arg) return sc->sc_newstate(ic, nstate, arg); } -/* - * Grab exclusive access to NIC memory. - */ void -iwn_mem_lock(struct iwn_softc *sc) -{ - uint32_t tmp; - int ntries; - - tmp = IWN_READ(sc, IWN_GPIO_CTL); - IWN_WRITE(sc, IWN_GPIO_CTL, tmp | IWN_GPIO_MAC); - - /* spin until we actually get the lock */ - for (ntries = 0; ntries < 1000; ntries++) { - if ((IWN_READ(sc, IWN_GPIO_CTL) & - (IWN_GPIO_CLOCK | IWN_GPIO_SLEEP)) == IWN_GPIO_CLOCK) - break; - DELAY(10); - } - if (ntries == 1000) - printf("%s: could not lock memory\n", sc->sc_dev.dv_xname); -} - -/* - * Release lock on NIC memory. - */ -void -iwn_mem_unlock(struct iwn_softc *sc) -{ - uint32_t tmp = IWN_READ(sc, IWN_GPIO_CTL); - IWN_WRITE(sc, IWN_GPIO_CTL, tmp & ~IWN_GPIO_MAC); -} - -uint32_t -iwn_mem_read(struct iwn_softc *sc, uint32_t addr) -{ - IWN_WRITE(sc, IWN_READ_MEM_ADDR, IWN_MEM_4 | addr); - return IWN_READ(sc, IWN_READ_MEM_DATA); -} - -void -iwn_mem_write(struct iwn_softc *sc, uint32_t addr, uint32_t data) -{ - IWN_WRITE(sc, IWN_WRITE_MEM_ADDR, IWN_MEM_4 | addr); - IWN_WRITE(sc, IWN_WRITE_MEM_DATA, data); -} - -void -iwn_mem_write_region_4(struct iwn_softc *sc, uint32_t addr, - const uint32_t *data, int wlen) -{ - for (; wlen > 0; wlen--, data++, addr += 4) - iwn_mem_write(sc, addr, *data); -} - -int -iwn_eeprom_lock(struct iwn_softc *sc) -{ - uint32_t tmp; - int ntries; - - tmp = IWN_READ(sc, IWN_HWCONFIG); - IWN_WRITE(sc, IWN_HWCONFIG, tmp | IWN_HW_EEPROM_LOCKED); - - /* spin until we actually get the lock */ - for (ntries = 0; ntries < 100; ntries++) { - if (IWN_READ(sc, IWN_HWCONFIG) & IWN_HW_EEPROM_LOCKED) - return 0; - DELAY(10); - } - return ETIMEDOUT; -} - -void -iwn_eeprom_unlock(struct iwn_softc *sc) -{ - uint32_t tmp = IWN_READ(sc, IWN_HWCONFIG); - IWN_WRITE(sc, IWN_HWCONFIG, tmp & ~IWN_HW_EEPROM_LOCKED); -} - -/* - * Read `len' bytes from the EEPROM. We access the EEPROM through the MAC - * instead of using the traditional bit-bang method. - */ -int -iwn_read_prom_data(struct iwn_softc *sc, uint32_t addr, void *data, int len) -{ - uint8_t *out = data; - uint32_t val; - int ntries; - - iwn_mem_lock(sc); - for (; len > 0; len -= 2, addr++) { - IWN_WRITE(sc, IWN_EEPROM_CTL, addr << 2); - IWN_WRITE(sc, IWN_EEPROM_CTL, - IWN_READ(sc, IWN_EEPROM_CTL) & ~IWN_EEPROM_CMD); - - for (ntries = 0; ntries < 10; ntries++) { - if ((val = IWN_READ(sc, IWN_EEPROM_CTL)) & - IWN_EEPROM_READY) - break; - DELAY(5); - } - if (ntries == 10) { - printf("%s: could not read EEPROM\n", - sc->sc_dev.dv_xname); - return ETIMEDOUT; - } - *out++ = val >> 16; - if (len > 1) - *out++ = val >> 24; - } - iwn_mem_unlock(sc); - - return 0; -} - -/* - * The firmware boot code is small and is intended to be copied directly into - * the NIC internal memory. - */ -int -iwn_load_microcode(struct iwn_softc *sc, const uint8_t *ucode, int size) -{ - int ntries; - - size /= sizeof (uint32_t); - - iwn_mem_lock(sc); - - /* copy microcode image into NIC memory */ - iwn_mem_write_region_4(sc, IWN_MEM_UCODE_BASE, - (const uint32_t *)ucode, size); - - iwn_mem_write(sc, IWN_MEM_UCODE_SRC, 0); - iwn_mem_write(sc, IWN_MEM_UCODE_DST, IWN_FW_TEXT); - iwn_mem_write(sc, IWN_MEM_UCODE_SIZE, size); - - /* run microcode */ - iwn_mem_write(sc, IWN_MEM_UCODE_CTL, IWN_UC_RUN); - - /* wait for transfer to complete */ - for (ntries = 0; ntries < 1000; ntries++) { - if (!(iwn_mem_read(sc, IWN_MEM_UCODE_CTL) & IWN_UC_RUN)) - break; - DELAY(10); - } - if (ntries == 1000) { - iwn_mem_unlock(sc); - printf("%s: could not load boot firmware\n", - sc->sc_dev.dv_xname); - return ETIMEDOUT; - } - iwn_mem_write(sc, IWN_MEM_UCODE_CTL, IWN_UC_ENABLE); - - iwn_mem_unlock(sc); - - return 0; -} - -int -iwn_load_firmware(struct iwn_softc *sc) +iwn_iter_func(void *arg, struct ieee80211_node *ni) { - struct iwn_dma_info *dma = &sc->fw_dma; - const struct iwn_firmware_hdr *hdr; - const uint8_t *init_text, *init_data, *main_text, *main_data; - const uint8_t *boot_text; - uint32_t init_textsz, init_datasz, main_textsz, main_datasz; - uint32_t boot_textsz; - u_char *fw; - size_t size; - int error; - - /* load firmware image from disk */ - if ((error = loadfirmware("iwn-4965agn", &fw, &size)) != 0) { - printf("%s: error, %d, could not read firmware %s\n", - sc->sc_dev.dv_xname, error, "iwn-4965agn"); - goto fail1; - } - - /* extract firmware header information */ - if (size < sizeof (struct iwn_firmware_hdr)) { - printf("%s: truncated firmware header: %d bytes\n", - sc->sc_dev.dv_xname, size); - error = EINVAL; - goto fail2; - } - hdr = (const struct iwn_firmware_hdr *)fw; - main_textsz = letoh32(hdr->main_textsz); - main_datasz = letoh32(hdr->main_datasz); - init_textsz = letoh32(hdr->init_textsz); - init_datasz = letoh32(hdr->init_datasz); - boot_textsz = letoh32(hdr->boot_textsz); - - /* sanity-check firmware segments sizes */ - if (main_textsz > IWN_FW_MAIN_TEXT_MAXSZ || - main_datasz > IWN_FW_MAIN_DATA_MAXSZ || - init_textsz > IWN_FW_INIT_TEXT_MAXSZ || - init_datasz > IWN_FW_INIT_DATA_MAXSZ || - boot_textsz > IWN_FW_BOOT_TEXT_MAXSZ || - (boot_textsz & 3) != 0) { - printf("%s: invalid firmware header\n", sc->sc_dev.dv_xname); - error = EINVAL; - goto fail2; - } - - /* check that all firmware segments are present */ - if (size < sizeof (struct iwn_firmware_hdr) + main_textsz + - main_datasz + init_textsz + init_datasz + boot_textsz) { - printf("%s: firmware file too short: %d bytes\n", - sc->sc_dev.dv_xname, size); - error = EINVAL; - goto fail2; - } - - /* get pointers to firmware segments */ - main_text = (const uint8_t *)(hdr + 1); - main_data = main_text + main_textsz; - init_text = main_data + main_datasz; - init_data = init_text + init_textsz; - boot_text = init_data + init_datasz; - - /* copy initialization images into pre-allocated DMA-safe memory */ - memcpy(dma->vaddr, init_data, init_datasz); - memcpy(dma->vaddr + IWN_FW_INIT_DATA_MAXSZ, init_text, init_textsz); - - /* tell adapter where to find initialization images */ - iwn_mem_lock(sc); - iwn_mem_write(sc, IWN_MEM_DATA_BASE, dma->paddr >> 4); - iwn_mem_write(sc, IWN_MEM_DATA_SIZE, init_datasz); - iwn_mem_write(sc, IWN_MEM_TEXT_BASE, - (dma->paddr + IWN_FW_INIT_DATA_MAXSZ) >> 4); - iwn_mem_write(sc, IWN_MEM_TEXT_SIZE, init_textsz); - iwn_mem_unlock(sc); - - /* load firmware boot code */ - if ((error = iwn_load_microcode(sc, boot_text, boot_textsz)) != 0) { - printf("%s: could not load boot firmware\n", - sc->sc_dev.dv_xname); - goto fail2; - } - - /* now press "execute" ;-) */ - IWN_WRITE(sc, IWN_RESET, 0); - - /* wait at most one second for first alive notification */ - if ((error = tsleep(sc, PCATCH, "iwninit", hz)) != 0) { - /* this isn't what was supposed to happen.. */ - printf("%s: timeout waiting for adapter to initialize\n", - sc->sc_dev.dv_xname); - goto fail2; - } - - /* copy runtime images into pre-allocated DMA-safe memory */ - memcpy(dma->vaddr, main_data, main_datasz); - memcpy(dma->vaddr + IWN_FW_MAIN_DATA_MAXSZ, main_text, main_textsz); - - /* tell adapter where to find runtime images */ - iwn_mem_lock(sc); - iwn_mem_write(sc, IWN_MEM_DATA_BASE, dma->paddr >> 4); - iwn_mem_write(sc, IWN_MEM_DATA_SIZE, main_datasz); - iwn_mem_write(sc, IWN_MEM_TEXT_BASE, - (dma->paddr + IWN_FW_MAIN_DATA_MAXSZ) >> 4); - iwn_mem_write(sc, IWN_MEM_TEXT_SIZE, IWN_FW_UPDATED | main_textsz); - iwn_mem_unlock(sc); - - /* wait at most one second for second alive notification */ - if ((error = tsleep(sc, PCATCH, "iwninit", hz)) != 0) { - /* this isn't what was supposed to happen.. */ - printf("%s: timeout waiting for adapter to initialize\n", - sc->sc_dev.dv_xname); - } + struct iwn_softc *sc = arg; + struct iwn_node *wn = (struct iwn_node *)ni; -fail2: free(fw, M_DEVBUF); -fail1: return error; + ieee80211_amrr_choose(&sc->amrr, ni, &wn->amn); } void @@ -1146,7 +1384,6 @@ iwn_calib_timeout(void *arg) struct ieee80211com *ic = &sc->sc_ic; int s; - /* automatic rate control triggered every 500ms */ if (ic->ic_fixed_rate == -1) { s = splnet(); if (ic->ic_opmode == IEEE80211_M_STA) @@ -1155,42 +1392,44 @@ iwn_calib_timeout(void *arg) ieee80211_iterate_nodes(ic, iwn_iter_func, sc); splx(s); } - - /* automatic calibration every 60s */ + /* Force automatic TX power calibration every 60 secs. */ if (++sc->calib_cnt >= 120) { + uint32_t flags = 0; + DPRINTF(("sending request for statistics\n")); - (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 = 0; } - + /* Automatic rate control triggered every 500ms. */ timeout_add(&sc->calib_to, hz / 2); } -void -iwn_iter_func(void *arg, struct ieee80211_node *ni) -{ - struct iwn_softc *sc = arg; - struct iwn_node *wn = (struct iwn_node *)ni; - - ieee80211_amrr_choose(&sc->amrr, ni, &wn->amn); -} - +/* + * Process an AMPDU_RX_START firmware notification. This is usually + * immediately followed by an AMPDU_RX_DONE notification. + */ void iwn_ampdu_rx_start(struct iwn_softc *sc, struct iwn_rx_desc *desc) { struct iwn_rx_stat *stat; DPRINTFN(2, ("received AMPDU stats\n")); - /* save Rx statistics, they will be used on IWN_AMPDU_RX_DONE */ + /* Save RX statistics, they will be used on AMPDU_RX_DONE. */ stat = (struct iwn_rx_stat *)(desc + 1); memcpy(&sc->last_rx_stat, stat, sizeof (*stat)); sc->last_rx_valid = 1; } +/* + * Process an RX_DONE (4965AGN only) or AMPDU_RX_DONE firmware notification. + * Each AMPDU_RX_DONE notification must be preceded by an AMPDU_RX_START. + */ void -iwn_rx_intr(struct iwn_softc *sc, struct iwn_rx_desc *desc, +iwn_rx_done(struct iwn_softc *sc, struct iwn_rx_desc *desc, struct iwn_rx_data *data) { + const struct iwn_hal *hal = sc->sc_hal; struct ieee80211com *ic = &sc->sc_ic; struct ifnet *ifp = &ic->ic_if; struct iwn_rx_ring *ring = &sc->rxq; @@ -1205,7 +1444,7 @@ iwn_rx_intr(struct iwn_softc *sc, struct iwn_rx_desc *desc, int len, rssi; if (desc->type == IWN_AMPDU_RX_DONE) { - /* check for prior AMPDU_RX_START */ + /* Check for prior AMPDU_RX_START. */ if (!sc->last_rx_valid) { DPRINTF(("missing AMPDU_RX_START\n")); ifp->if_ierrors++; @@ -1217,7 +1456,7 @@ iwn_rx_intr(struct iwn_softc *sc, struct iwn_rx_desc *desc, stat = (struct iwn_rx_stat *)(desc + 1); if (stat->cfg_phy_len > IWN_STAT_MAXLEN) { - printf("%s: invalid rx statistic header\n", + printf("%s: invalid RX statistic header\n", sc->sc_dev.dv_xname); ifp->if_ierrors++; return; @@ -1232,14 +1471,14 @@ iwn_rx_intr(struct iwn_softc *sc, struct iwn_rx_desc *desc, len = letoh16(stat->len); } - /* discard Rx frames with bad CRC early */ + /* Discard frames with a bad FCS early. */ tail = (uint32_t *)(head + len); if ((letoh32(*tail) & IWN_RX_NOERROR) != IWN_RX_NOERROR) { - DPRINTFN(2, ("rx flags error %x\n", letoh32(*tail))); + DPRINTFN(2, ("RX flags error %x\n", letoh32(*tail))); ifp->if_ierrors++; return; } - /* XXX for ieee80211_find_rxnode() */ + /* Discard frames that are too short. */ if (len < sizeof (struct ieee80211_frame)) { DPRINTF(("frame too short: %d\n", len)); ic->ic_stats.is_rx_tooshort++; @@ -1249,7 +1488,7 @@ iwn_rx_intr(struct iwn_softc *sc, struct iwn_rx_desc *desc, m = data->m; - /* finalize mbuf */ + /* Finalize mbuf. */ m->m_pkthdr.rcvif = ifp; m->m_data = head; m->m_pkthdr.len = m->m_len = len; @@ -1261,28 +1500,27 @@ iwn_rx_intr(struct iwn_softc *sc, struct iwn_rx_desc *desc, ifp->if_ierrors++; return; } - - /* attach Rx buffer to mbuf */ + /* Attach RX buffer to mbuf header. */ MEXTADD(mnew, rbuf->vaddr, IWN_RBUF_SIZE, 0, iwn_free_rbuf, rbuf); SLIST_REMOVE_HEAD(&sc->rxq.freelist, next); data->m = mnew; - /* update Rx descriptor */ + /* Update RX descriptor. */ ring->desc[ring->cur] = htole32(rbuf->paddr >> 8); } else { - /* no free rbufs, copy frame */ + /* No free rbufs, copy frame into an mbuf. */ m = m_copym2(m, 0, M_COPYALL, M_DONTWAIT); if (m == NULL) { - /* no free mbufs either, drop frame */ + /* No free mbufs either, drop frame. */ ic->ic_stats.is_rx_nombuf++; ifp->if_ierrors++; return; } } - rssi = iwn_get_rssi(stat); + rssi = hal->get_rssi(stat); #if NBPFILTER > 0 if (sc->sc_drvbpf != NULL) { @@ -1298,12 +1536,12 @@ iwn_rx_intr(struct iwn_softc *sc, struct iwn_rx_desc *desc, tap->wr_dbm_antnoise = (int8_t)sc->noise; tap->wr_tsft = stat->tstamp; switch (stat->rate) { - /* CCK rates */ + /* 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 */ + /* OFDM rates. */ case 0xd: tap->wr_rate = 12; break; case 0xf: tap->wr_rate = 18; break; case 0x5: tap->wr_rate = 24; break; @@ -1312,7 +1550,7 @@ iwn_rx_intr(struct iwn_softc *sc, struct iwn_rx_desc *desc, 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 */ + /* Unknown rate: should not happen. */ default: tap->wr_rate = 0; } @@ -1326,132 +1564,209 @@ iwn_rx_intr(struct iwn_softc *sc, struct iwn_rx_desc *desc, } #endif - /* grab a reference to the source node */ + /* Grab a reference to the source node. */ wh = mtod(m, struct ieee80211_frame *); ni = ieee80211_find_rxnode(ic, wh); - /* send the frame to the 802.11 layer */ + /* Send the frame to the 802.11 layer. */ rxi.rxi_flags = 0; rxi.rxi_rssi = rssi; rxi.rxi_tstamp = 0; /* unused */ ieee80211_input(ifp, m, ni, &rxi); - /* node is no longer needed */ + /* Node is no longer needed. */ ieee80211_release_node(ic, ni); } +/* + * Process a CALIBRATION_RESULT notification sent by the initialization + * firmware on response to a CMD_CALIB_CONFIG command (5000 only.) + */ +void +iwn5000_rx_calib_results(struct iwn_softc *sc, struct iwn_rx_desc *desc) +{ + struct iwn5000_calib *calib = (struct iwn5000_calib *)(desc + 1); + u_int idx, len; + + /* No initial calibration required for 5150! */ + if (sc->hw_type == IWN_HW_REV_TYPE_5150) + return; + + /* Runtime firmware should not send such a notification. */ + if (!(sc->sc_flags & IWN_FLAG_FIRST_BOOT)) + return; + + switch (calib->code) { + case IWN5000_PHY_CALIB_LO: + idx = 0; + break; + case IWN5000_PHY_CALIB_LO_TX_IQ: + idx = 1; + break; + case IWN5000_PHY_CALIB_LO_TX_IQ_PERD: + idx = 2; + break; + default: + /* Ignore other results. */ + return; + } + /* Save calibration result. */ + if (sc->calibcmd[idx].buf != NULL) + free(sc->calibcmd[idx].buf, M_DEVBUF); + len = (letoh32(desc->len) & 0x3fff) - 4; + sc->calibcmd[idx].buf = malloc(len, M_DEVBUF, M_NOWAIT); + if (sc->calibcmd[idx].buf == NULL) { + DPRINTF(("not enough memory for calibration result %d\n", + calib->code)); + return; + } + DPRINTF(("saving calibration result code=%d len=%d\n", + calib->code, len)); + sc->calibcmd[idx].len = len; + memcpy(sc->calibcmd[idx].buf, calib, len); +} + +/* + * Process an RX_STATISTICS or BEACON_STATISTICS firmware notification. + * The latter is sent by the firmware after each received beacon. + */ void iwn_rx_statistics(struct iwn_softc *sc, struct iwn_rx_desc *desc) { + const struct iwn_hal *hal = sc->sc_hal; struct ieee80211com *ic = &sc->sc_ic; struct iwn_calib_state *calib = &sc->calib; struct iwn_stats *stats = (struct iwn_stats *)(desc + 1); + int temp; - /* ignore beacon statistics received during a scan */ + /* Ignore statistics received during a scan. */ if (ic->ic_state != IEEE80211_S_RUN) return; DPRINTFN(3, ("received statistics (cmd=%d)\n", desc->type)); - sc->calib_cnt = 0; /* reset timeout */ + sc->calib_cnt = 0; /* Reset TX power calibration timeout. */ - /* test if temperature has changed */ + /* Test if temperature has changed. */ if (stats->general.temp != sc->rawtemp) { - int temp; - + /* Convert "raw" temperature to degC. */ sc->rawtemp = stats->general.temp; - temp = iwn_get_temperature(sc); - DPRINTFN(2, ("temperature=%d\n", temp)); + temp = hal->get_temperature(sc); + DPRINTFN(2, ("temperature=%dC\n", temp)); - /* update temperature sensor */ + /* Update temperature sensor. */ sc->sensor.value = IWN_CTOMUK(temp); + sc->sensor.flags &= ~SENSOR_FINVALID; - /* update Tx power if need be */ - iwn_power_calibration(sc, temp); + /* Update TX power if need be (4965AGN only.) */ + if (sc->hw_type == IWN_HW_REV_TYPE_4965) + iwn4965_power_calibration(sc, temp); } if (desc->type != IWN_BEACON_STATISTICS) - return; /* reply to a statistics request */ + return; /* Reply to a statistics request. */ sc->noise = iwn_get_noise(&stats->rx.general); - DPRINTFN(3, ("noise=%d\n", sc->noise)); - /* test that RSSI and noise are present in stats report */ + /* Test that RSSI and noise are present in stats report. */ if (letoh32(stats->rx.general.flags) != 1) { DPRINTF(("received statistics without RSSI\n")); return; } if (calib->state == IWN_CALIB_STATE_ASSOC) - iwn_compute_differential_gain(sc, &stats->rx.general); + iwn_collect_noise(sc, &stats->rx.general); else if (calib->state == IWN_CALIB_STATE_RUN) iwn_tune_sensitivity(sc, &stats->rx); } +/* + * Process a TX_DONE firmware notification. Unfortunately, the 4965AGN + * and 5000 adapters have different incompatible TX status formats. + */ void -iwn_tx_intr(struct iwn_softc *sc, struct iwn_rx_desc *desc) +iwn4965_tx_done(struct iwn_softc *sc, struct iwn_rx_desc *desc) +{ + struct iwn4965_tx_stat *stat = (struct iwn4965_tx_stat *)(desc + 1); + + iwn_tx_done(sc, desc, stat->retrycnt, letoh32(stat->status)); +} + +void +iwn5000_tx_done(struct iwn_softc *sc, struct iwn_rx_desc *desc) +{ + struct iwn5000_tx_stat *stat = (struct iwn5000_tx_stat *)(desc + 1); + + /* Reset TX scheduler slot. */ + iwn5000_reset_sched(sc, desc->qid & 0xf, desc->idx); + iwn_tx_done(sc, desc, stat->retrycnt, letoh16(stat->status)); +} + +/* + * Adapter-independent backend for TX_DONE firmware notifications. + */ +void +iwn_tx_done(struct iwn_softc *sc, struct iwn_rx_desc *desc, int retrycnt, + uint32_t status) { struct ieee80211com *ic = &sc->sc_ic; struct ifnet *ifp = &ic->ic_if; struct iwn_tx_ring *ring = &sc->txq[desc->qid & 0xf]; struct iwn_tx_data *data = &ring->data[desc->idx]; - struct iwn_tx_stat *stat = (struct iwn_tx_stat *)(desc + 1); struct iwn_node *wn = (struct iwn_node *)data->ni; - uint32_t status; - DPRINTFN(4, ("tx done: qid=%d idx=%d retries=%d nkill=%d rate=%x " - "duration=%d status=%x\n", desc->qid, desc->idx, stat->ntries, - stat->nkill, stat->rate, letoh16(stat->duration), - letoh32(stat->status))); - - /* - * Update rate control statistics for the node. - */ + /* Update rate control statistics. */ wn->amn.amn_txcnt++; - if (stat->ntries > 0) { - DPRINTFN(3, ("tx intr ntries %d\n", stat->ntries)); + if (retrycnt > 0) wn->amn.amn_retrycnt++; - } - status = letoh32(stat->status) & 0xff; if (status != 1 && status != 2) ifp->if_oerrors++; else ifp->if_opackets++; + /* Unmap and free mbuf. */ bus_dmamap_unload(sc->sc_dmat, data->map); m_freem(data->m); data->m = NULL; ieee80211_release_node(ic, data->ni); data->ni = NULL; - ring->queued--; - sc->sc_tx_timer = 0; - ifp->if_flags &= ~IFF_OACTIVE; - (*ifp->if_start)(ifp); + if (--ring->queued < IWN_TX_RING_LOMARK) + sc->qfullmsk &= ~(1 << ring->qid); + if (sc->qfullmsk == 0) { + ifp->if_flags &= ~IFF_OACTIVE; + (*ifp->if_start)(ifp); + } } +/* + * Process a "command done" firmware notification. This is where we wakeup + * processes waiting for a synchronous command completion. + */ void -iwn_cmd_intr(struct iwn_softc *sc, struct iwn_rx_desc *desc) +iwn_cmd_done(struct iwn_softc *sc, struct iwn_rx_desc *desc) { struct iwn_tx_ring *ring = &sc->txq[4]; struct iwn_tx_data *data; if ((desc->qid & 0xf) != 4) - return; /* not a command ack */ + return; /* Not a command ack. */ data = &ring->data[desc->idx]; - /* if the command was mapped in a mbuf, free it */ + /* If the command was mapped in an mbuf, free it. */ if (data->m != NULL) { bus_dmamap_unload(sc->sc_dmat, data->map); m_freem(data->m); data->m = NULL; } - - wakeup(&ring->cmd[desc->idx]); + wakeup(&ring->desc[desc->idx]); } +/* + * Process an INT_FH_RX or INT_SW_RX interrupt. + */ void iwn_notif_intr(struct iwn_softc *sc) { @@ -1459,31 +1774,31 @@ iwn_notif_intr(struct iwn_softc *sc) struct ifnet *ifp = &ic->ic_if; uint16_t hw; - hw = letoh16(sc->shared->closed_count); + hw = letoh16(sc->rxq.stat->closed_count) & 0xfff; while (sc->rxq.cur != hw) { struct iwn_rx_data *data = &sc->rxq.data[sc->rxq.cur]; struct iwn_rx_desc *desc = (void *)data->m->m_ext.ext_buf; - DPRINTFN(4,("rx notification qid=%x idx=%d flags=%x type=%d " - "len=%d\n", desc->qid, desc->idx, desc->flags, desc->type, - letoh16(desc->len))); + DPRINTFN(4, ("notification qid=%d idx=%d flags=%x type=%d\n", + desc->qid & 0xf, desc->idx, desc->flags, desc->type)); - if (!(desc->qid & 0x80)) /* reply to a command */ - iwn_cmd_intr(sc, desc); + if (!(desc->qid & 0x80)) /* Reply to a command. */ + iwn_cmd_done(sc, desc); switch (desc->type) { - case IWN_RX_DONE: - case IWN_AMPDU_RX_DONE: - iwn_rx_intr(sc, desc, data); - break; - case IWN_AMPDU_RX_START: iwn_ampdu_rx_start(sc, desc); break; + case IWN_RX_DONE: /* 4965AGN only. */ + case IWN_AMPDU_RX_DONE: + /* An 802.11 frame has been received. */ + iwn_rx_done(sc, desc, data); + break; + case IWN_TX_DONE: - /* a 802.11 frame has been transmitted */ - iwn_tx_intr(sc, desc); + /* An 802.11 frame has been transmitted. */ + sc->sc_hal->tx_done(sc, desc); break; case IWN_RX_STATISTICS: @@ -1499,7 +1814,7 @@ iwn_notif_intr(struct iwn_softc *sc) * If more than 5 consecutive beacons are missed, * reinitialize the sensitivity state machine. */ - DPRINTFN(2, ("beacons missed %d/%d\n", + DPRINTF(("beacons missed %d/%d\n", letoh32(miss->consecutive), letoh32(miss->total))); if (ic->ic_state == IEEE80211_S_RUN && letoh32(miss->consecutive) > 5) @@ -1511,7 +1826,7 @@ iwn_notif_intr(struct iwn_softc *sc) struct iwn_ucode_info *uc = (struct iwn_ucode_info *)(desc + 1); - /* the microcontroller is ready */ + /* The microcontroller is ready. */ DPRINTF(("microcode alive notification version=%d.%d " "subtype=%x alive=%x\n", uc->major, uc->minor, uc->subtype, letoh32(uc->valid))); @@ -1522,7 +1837,7 @@ iwn_notif_intr(struct iwn_softc *sc) break; } if (uc->subtype == IWN_UCODE_INIT) { - /* save microcontroller's report */ + /* Save microcontroller's report. */ memcpy(&sc->ucode_info, uc, sizeof (*uc)); } break; @@ -1531,17 +1846,17 @@ iwn_notif_intr(struct iwn_softc *sc) { uint32_t *status = (uint32_t *)(desc + 1); - /* enabled/disabled notification */ + /* Enabled/disabled notification. */ DPRINTF(("state changed to %x\n", letoh32(*status))); if (letoh32(*status) & 1) { - /* the radio button has to be pushed */ + /* The radio button has to be pushed. */ printf("%s: Radio transmitter is off\n", sc->sc_dev.dv_xname); - /* turn the interface down */ + /* Turn the interface down. */ ifp->if_flags &= ~IFF_UP; iwn_stop(ifp, 1); - return; /* no further processing */ + return; /* No further processing. */ } break; } @@ -1553,7 +1868,7 @@ iwn_notif_intr(struct iwn_softc *sc) DPRINTFN(2, ("scanning channel %d status %x\n", scan->chan, letoh32(scan->status))); - /* fix current channel */ + /* Fix current channel. */ ic->ic_bss->ni_chan = &ic->ic_channels[scan->chan]; break; } @@ -1565,25 +1880,52 @@ iwn_notif_intr(struct iwn_softc *sc) DPRINTF(("scan finished nchan=%d status=%d chan=%d\n", scan->nchan, scan->status, scan->chan)); - if (scan->status == 1 && scan->chan <= 14) { + if (scan->status == 1 && scan->chan <= 14 && + (sc->sc_flags & IWN_FLAG_HAS_5GHZ)) { /* - * We just finished scanning 802.11g channels, - * start scanning 802.11a ones. + * We just finished scanning 2GHz channels, + * start scanning 5GHz ones. */ - if (iwn_scan(sc, IEEE80211_CHAN_A) == 0) + if (iwn_scan(sc, IEEE80211_CHAN_5GHZ) == 0) break; } ieee80211_end_scan(ifp); break; } + case IWN5000_CALIBRATION_RESULT: + iwn5000_rx_calib_results(sc, desc); + break; + + case IWN5000_CALIBRATION_DONE: + wakeup(sc); + break; } sc->rxq.cur = (sc->rxq.cur + 1) % IWN_RX_RING_COUNT; } - /* tell the firmware what we have processed */ + /* Tell the firmware what we have processed. */ hw = (hw == 0) ? IWN_RX_RING_COUNT - 1 : hw - 1; - IWN_WRITE(sc, IWN_RX_WIDX, hw & ~7); + IWN_WRITE(sc, IWN_FH_RX_WPTR, hw & ~7); +} + +/* + * Process an INT_WAKEUP interrupt raised when the microcontroller wakes up + * from power-down sleep mode. + */ +void +iwn_wakeup_intr(struct iwn_softc *sc) +{ + int qid; + + DPRINTF(("ucode wakeup from power-down sleep\n")); + + /* Wakeup RX and TX rings. */ + IWN_WRITE(sc, IWN_FH_RX_WPTR, sc->rxq.cur & ~7); + for (qid = 0; qid < 6; qid++) { + struct iwn_tx_ring *ring = &sc->txq[qid]; + IWN_WRITE(sc, IWN_HBUS_TARG_WRPTR, qid << 8 | ring->cur); + } } int @@ -1593,67 +1935,107 @@ iwn_intr(void *arg) struct ifnet *ifp = &sc->sc_ic.ic_if; uint32_t r1, r2; - /* disable interrupts */ + /* Disable interrupts. */ IWN_WRITE(sc, IWN_MASK, 0); - r1 = IWN_READ(sc, IWN_INTR); - r2 = IWN_READ(sc, IWN_INTR_STATUS); + r1 = IWN_READ(sc, IWN_INT); + r2 = IWN_READ(sc, IWN_FH_INT); if (r1 == 0 && r2 == 0) { if (ifp->if_flags & IFF_UP) - IWN_WRITE(sc, IWN_MASK, IWN_INTR_MASK); - return 0; /* not for us */ + IWN_WRITE(sc, IWN_MASK, IWN_INT_MASK); + return 0; /* Interrupt not for us. */ } + if (r1 == 0xffffffff || (r1 & 0xfffffff0) == 0xa5a5a5a0) + return 0; /* Hardware gone! */ - if (r1 == 0xffffffff) - return 0; /* hardware gone */ - - /* ack interrupts */ - IWN_WRITE(sc, IWN_INTR, r1); - IWN_WRITE(sc, IWN_INTR_STATUS, r2); + /* Acknowledge interrupts. */ + IWN_WRITE(sc, IWN_INT, r1); + IWN_WRITE(sc, IWN_FH_INT, r2); - DPRINTFN(6, ("interrupt reg1=%x reg2=%x\n", r1, r2)); - - if (r1 & IWN_RF_TOGGLED) { - uint32_t tmp = IWN_READ(sc, IWN_GPIO_CTL); + if (r1 & IWN_INT_RF_TOGGLED) { + uint32_t tmp = IWN_READ(sc, IWN_GP_CNTRL); printf("%s: RF switch: radio %s\n", sc->sc_dev.dv_xname, - (tmp & IWN_GPIO_RF_ENABLED) ? "enabled" : "disabled"); + (tmp & IWN_GP_CNTRL_RFKILL) ? "enabled" : "disabled"); } - if (r1 & IWN_CT_REACHED) { + if (r1 & IWN_INT_CT_REACHED) { printf("%s: critical temperature reached!\n", sc->sc_dev.dv_xname); + /* XXX reduce TX power? */ } - if (r1 & (IWN_SW_ERROR | IWN_HW_ERROR)) { + if (r1 & (IWN_INT_SW_ERR | IWN_INT_HW_ERR)) { printf("%s: fatal firmware error\n", sc->sc_dev.dv_xname); ifp->if_flags &= ~IFF_UP; iwn_stop(ifp, 1); return 1; } - if ((r1 & (IWN_RX_INTR | IWN_SW_RX_INTR)) || - (r2 & IWN_RX_STATUS_INTR)) + if ((r1 & (IWN_INT_FH_RX | IWN_INT_SW_RX)) || + (r2 & IWN_FH_INT_RX)) iwn_notif_intr(sc); - if (r1 & IWN_ALIVE_INTR) - wakeup(sc); + if ((r1 & IWN_INT_FH_TX) || (r2 & IWN_FH_INT_TX)) + wakeup(sc); /* FH DMA transfer completed. */ + + if (r1 & IWN_INT_ALIVE) + wakeup(sc); /* Firmware is alive. */ - /* re-enable interrupts */ + if (r1 & IWN_INT_WAKEUP) + iwn_wakeup_intr(sc); + + /* Re-enable interrupts. */ if (ifp->if_flags & IFF_UP) - IWN_WRITE(sc, IWN_MASK, IWN_INTR_MASK); + IWN_WRITE(sc, IWN_MASK, IWN_INT_MASK); return 1; } +/* + * Update TX scheduler ring when transmitting an 802.11 frame (4965AGN and + * 5000 adapters use a slightly different format.) + */ +void +iwn4965_update_sched(struct iwn_softc *sc, int qid, int idx, uint8_t id, + uint16_t len) +{ + uint16_t *w = &sc->sched[qid * IWN4965_SCHED_COUNT + idx]; + + *w = htole16(len + 8); + if (idx < IWN_SCHED_WINSZ) + *(w + IWN_TX_RING_COUNT) = *w; +} + +void +iwn5000_update_sched(struct iwn_softc *sc, int qid, int idx, uint8_t id, + uint16_t len) +{ + uint16_t *w = &sc->sched[qid * IWN5000_SCHED_COUNT + idx]; + + *w = htole16(id << 12 | (len + 8)); + if (idx < IWN_SCHED_WINSZ) + *(w + IWN_TX_RING_COUNT) = *w; +} + +void +iwn5000_reset_sched(struct iwn_softc *sc, int qid, int idx) +{ + uint16_t *w = &sc->sched[qid * IWN5000_SCHED_COUNT + idx]; + + *w = (*w & htole16(0xf000)) | htole16(1); + if (idx < IWN_SCHED_WINSZ) + *(w + IWN_TX_RING_COUNT) = *w; +} + uint8_t iwn_plcp_signal(int rate) { switch (rate) { - /* CCK rates (returned values are device-dependent) */ + /* CCK rates (returned values are device-dependent.) */ case 2: return 10; case 4: return 20; case 11: return 55; case 22: return 110; - /* OFDM rates (cf IEEE Std 802.11a-1999, pp. 14 Table 80) */ + /* OFDM rates (cf IEEE Std 802.11a-1999, pp. 14 Table 80.) */ /* R1-R4, (u)ral is R4-R1 */ case 12: return 0xd; case 18: return 0xf; @@ -1665,43 +2047,54 @@ iwn_plcp_signal(int rate) case 108: return 0x3; case 120: return 0x3; } - /* unknown rate (should not get there) */ + /* Unknown rate (should not get there.) */ return 0; } -/* determine if a given rate is CCK or OFDM */ +/* Determine if a given rate is CCK or OFDM. */ #define IWN_RATE_IS_OFDM(rate) ((rate) >= 12 && (rate) != 22) int -iwn_tx_data(struct iwn_softc *sc, struct mbuf *m0, struct ieee80211_node *ni, - int ac) +iwn_tx_data(struct iwn_softc *sc, struct mbuf *m0, struct ieee80211_node *ni) { + const struct iwn_hal *hal = sc->sc_hal; struct ieee80211com *ic = &sc->sc_ic; - struct iwn_tx_ring *ring = &sc->txq[ac]; + struct iwn_tx_ring *ring; struct iwn_tx_desc *desc; struct iwn_tx_data *data; struct iwn_tx_cmd *cmd; struct iwn_cmd_data *tx; struct ieee80211_frame *wh; struct ieee80211_key *k; + enum ieee80211_edca_ac ac; struct mbuf *mnew; - bus_addr_t paddr; uint32_t flags; - uint8_t type; + uint16_t qos; + uint8_t tid, type; u_int hdrlen; - int i, rate, error, pad; - - desc = &ring->desc[ring->cur]; - data = &ring->data[ring->cur]; + int i, hasqos, rate, error, pad; wh = mtod(m0, struct ieee80211_frame *); hdrlen = ieee80211_get_hdrlen(wh); type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK; - /* pickup a rate */ + /* Select EDCA Access Category and TX ring for this frame. */ + if ((hasqos = ieee80211_has_qos(wh))) { + qos = ieee80211_get_qos(wh); + tid = qos & IEEE80211_QOS_TID; + ac = ieee80211_up_to_ac(ic, tid); + } else { + tid = 0; + ac = EDCA_AC_BE; + } + + ring = &sc->txq[ac]; + desc = &ring->desc[ring->cur]; + data = &ring->data[ring->cur]; + + /* Chose a TX rate. */ if (IEEE80211_IS_MULTICAST(wh->i_addr1) || type != IEEE80211_FC0_TYPE_DATA) { - /* mgmt/multicast frames are sent at the lowest avail. rate */ rate = ni->ni_rates.rs_rates[0]; } else if (ic->ic_fixed_rate != -1) { rate = ic->ic_sup_rates[ic->ic_curmode]. @@ -1720,7 +2113,8 @@ iwn_tx_data(struct iwn_softc *sc, struct mbuf *m0, struct ieee80211_node *ni, tap->wt_chan_flags = htole16(ni->ni_chan->ic_flags); tap->wt_rate = rate; tap->wt_hwqueue = ac; - if (wh->i_fc[1] & IEEE80211_FC1_WEP) + if ((ic->ic_flags & IEEE80211_F_WEPON) && + (wh->i_fc[1] & IEEE80211_FC1_PROTECTED)) tap->wt_flags |= IEEE80211_RADIOTAP_F_WEP; mb.m_data = (caddr_t)tap; @@ -1733,6 +2127,18 @@ iwn_tx_data(struct iwn_softc *sc, struct mbuf *m0, struct ieee80211_node *ni, } #endif + /* Encrypt the frame if need be. */ + if (wh->i_fc[1] & IEEE80211_FC1_PROTECTED) { + /* Retrieve key for TX. */ + k = ieee80211_get_txkey(ic, wh, ni); + /* Do software encryption. */ + if ((m0 = ieee80211_encrypt(ic, m0, k)) == NULL) + return ENOBUFS; + /* 802.11 header may have moved. */ + wh = mtod(m0, struct ieee80211_frame *); + } + + /* Prepare TX firmware command. */ cmd = &ring->cmd[ring->cur]; cmd->code = IWN_CMD_TX_DATA; cmd->flags = 0; @@ -1740,49 +2146,61 @@ iwn_tx_data(struct iwn_softc *sc, struct mbuf *m0, struct ieee80211_node *ni, cmd->idx = ring->cur; tx = (struct iwn_cmd_data *)cmd->data; - /* no need to bzero tx, all fields are reinitialized here */ + /* NB: No need to bzero tx, all fields are reinitialized here. */ - if (wh->i_fc[1] & IEEE80211_FC1_PROTECTED) { - k = ieee80211_get_txkey(ic, wh, ni); - - if ((m0 = ieee80211_encrypt(ic, m0, k)) == NULL) - return ENOBUFS; - - wh = mtod(m0, struct ieee80211_frame *); + flags = 0; + if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) { + /* Unicast frame, check if an ACK is expected. */ + if (!hasqos || (qos & IEEE80211_QOS_ACK_POLICY_MASK) >> + IEEE80211_QOS_ACK_POLICY_SHIFT != + IEEE80211_QOS_ACK_POLICY_NOACK) + flags |= IWN_TX_NEED_ACK; } + if ((wh->i_fc[0] & + (IEEE80211_FC0_TYPE_MASK | IEEE80211_FC0_SUBTYPE_MASK)) == + (IEEE80211_FC0_TYPE_CTL | IEEE80211_FC0_SUBTYPE_BAR)) + flags |= IWN_TX_IMM_BA; /* Cannot happen yet. */ - flags = IWN_TX_AUTO_SEQ; - if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) - flags |= IWN_TX_NEED_ACK; - - if (IEEE80211_IS_MULTICAST(wh->i_addr1) || - type != IEEE80211_FC0_TYPE_DATA) - tx->id = IWN_ID_BROADCAST; - else - tx->id = IWN_ID_BSS; + if (wh->i_fc[1] & IEEE80211_FC1_MORE_FRAG) + flags |= IWN_TX_MORE_FRAG; /* Cannot happen yet. */ - /* check if RTS/CTS or CTS-to-self protection must be used */ + /* Check if frame must be protected using RTS/CTS or CTS-to-self. */ if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) { - /* multicast frames are not sent at OFDM rates in 802.11b/g */ + /* NB: Group frames are sent using CCK in 802.11b/g. */ if (m0->m_pkthdr.len + IEEE80211_CRC_LEN > ic->ic_rtsthreshold) { - flags |= IWN_TX_NEED_RTS | IWN_TX_FULL_TXOP; + flags |= IWN_TX_NEED_RTS; } else if ((ic->ic_flags & IEEE80211_F_USEPROT) && IWN_RATE_IS_OFDM(rate)) { if (ic->ic_protmode == IEEE80211_PROT_CTSONLY) - flags |= IWN_TX_NEED_CTS | IWN_TX_FULL_TXOP; + flags |= IWN_TX_NEED_CTS; else if (ic->ic_protmode == IEEE80211_PROT_RTSCTS) - flags |= IWN_TX_NEED_RTS | IWN_TX_FULL_TXOP; + flags |= IWN_TX_NEED_RTS; + } + if (flags & (IWN_TX_NEED_RTS | IWN_TX_NEED_CTS)) { + if (sc->hw_type != IWN_HW_REV_TYPE_4965) { + /* 5000 autoselect RTS/CTS or CTS-to-self. */ + flags &= ~(IWN_TX_NEED_RTS | IWN_TX_NEED_CTS); + flags |= IWN5000_TX_NEED_RTS_CTS; + } else + flags |= IWN_TX_FULL_TXOP; } } + if (IEEE80211_IS_MULTICAST(wh->i_addr1) || + type != IEEE80211_FC0_TYPE_DATA) + tx->id = hal->broadcast_id; + else + tx->id = IWN_ID_BSS; + if (type == IEEE80211_FC0_TYPE_MGT) { uint8_t subtype = wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK; - /* tell h/w to set timestamp in probe responses */ +#ifndef IEEE80211_STA_ONLY + /* Tell HW to set timestamp in probe responses. */ if (subtype == IEEE80211_FC0_SUBTYPE_PROBE_RESP) flags |= IWN_TX_INSERT_TSTAMP; - +#endif if (subtype == IEEE80211_FC0_SUBTYPE_ASSOC_REQ || subtype == IEEE80211_FC0_SUBTYPE_REASSOC_REQ) tx->timeout = htole16(3); @@ -1792,7 +2210,7 @@ iwn_tx_data(struct iwn_softc *sc, struct mbuf *m0, struct ieee80211_node *ni, tx->timeout = htole16(0); if (hdrlen & 3) { - /* first segment's length must be a multiple of 4 */ + /* First segment's length must be a multiple of 4. */ flags |= IWN_TX_NEED_PADDING; pad = 4 - (hdrlen & 3); } else @@ -1800,24 +2218,28 @@ iwn_tx_data(struct iwn_softc *sc, struct mbuf *m0, struct ieee80211_node *ni, tx->flags = htole32(flags); tx->len = htole16(m0->m_pkthdr.len); - tx->rate = iwn_plcp_signal(rate); + tx->tid = tid; tx->rts_ntries = 60; tx->data_ntries = 15; tx->lifetime = htole32(IWN_LIFETIME_INFINITE); - - /* XXX alternate between Ant A and Ant B ? */ - tx->rflags = IWN_RFLAG_ANT_B; - if (tx->id == IWN_ID_BROADCAST) { + tx->rate = iwn_plcp_signal(rate); + tx->rflags = 0; + if (!IWN_RATE_IS_OFDM(rate)) + tx->rflags |= IWN_RFLAG_CCK; + if (tx->id == hal->broadcast_id) { + /* Group or management frame. */ tx->ridx = IWN_MAX_TX_RETRIES - 1; - if (!IWN_RATE_IS_OFDM(rate)) - tx->rflags |= IWN_RFLAG_CCK; + /* XXX Alternate between antenna A and B? */ + tx->rflags |= IWN_RFLAG_ANT_B; } else { tx->ridx = ni->ni_rates.rs_nrates - ni->ni_txrate - 1; - /* tell adapter to ignore rflags */ + /* Tell adapter to ignore rflags. */ tx->flags |= htole32(IWN_TX_MRR_INDEX); } + /* Set physical address of "scratch area". */ + tx->loaddr = htole32(data->scratch_paddr); - /* copy and trim IEEE802.11 header */ + /* Copy and trim 802.11 header. */ memcpy((uint8_t *)(tx + 1), wh, hdrlen); m_adj(m0, hdrlen); @@ -1830,7 +2252,7 @@ iwn_tx_data(struct iwn_softc *sc, struct mbuf *m0, struct ieee80211_node *ni, return error; } if (error != 0) { - /* too many fragments, linearize */ + /* Too many fragments, linearize mbuf. */ MGETHDR(mnew, M_DONTWAIT, MT_DATA); if (mnew == NULL) { @@ -1846,7 +2268,6 @@ iwn_tx_data(struct iwn_softc *sc, struct mbuf *m0, struct ieee80211_node *ni, return ENOMEM; } } - m_copydata(m0, 0, m0->m_pkthdr.len, mtod(mnew, caddr_t)); m_freem(m0); mnew->m_len = mnew->m_pkthdr.len; @@ -1868,30 +2289,30 @@ iwn_tx_data(struct iwn_softc *sc, struct mbuf *m0, struct ieee80211_node *ni, DPRINTFN(4, ("sending data: qid=%d idx=%d len=%d nsegs=%d\n", ring->qid, ring->cur, m0->m_pkthdr.len, data->map->dm_nsegs)); - paddr = ring->cmd_dma.paddr + ring->cur * sizeof (struct iwn_tx_cmd); - tx->loaddr = htole32(paddr + 4 + - offsetof(struct iwn_cmd_data, ntries)); - tx->hiaddr = 0; /* limit to 32-bit physical addresses */ - - /* first scatter/gather segment is used by the tx data command */ + /* Fill TX descriptor. */ IWN_SET_DESC_NSEGS(desc, 1 + data->map->dm_nsegs); - IWN_SET_DESC_SEG(desc, 0, paddr, 4 + sizeof (*tx) + hdrlen + pad); + /* First DMA segment is used by the TX command. */ + IWN_SET_DESC_SEG(desc, 0, data->cmd_paddr, + 4 + sizeof (*tx) + hdrlen + pad); + /* Other DMA segments are for data payload. */ for (i = 1; i <= data->map->dm_nsegs; i++) { IWN_SET_DESC_SEG(desc, i, data->map->dm_segs[i - 1].ds_addr, data->map->dm_segs[i - 1].ds_len); } - sc->shared->len[ring->qid][ring->cur] = - htole16(hdrlen + m0->m_pkthdr.len + 8); - if (ring->cur < IWN_TX_WINDOW) { - sc->shared->len[ring->qid][ring->cur + IWN_TX_RING_COUNT] = - htole16(hdrlen + m0->m_pkthdr.len + 8); - } - ring->queued++; - /* kick Tx ring */ + /* Update TX scheduler. */ + hal->update_sched(sc, ring->qid, ring->cur, tx->id, + hdrlen + m0->m_pkthdr.len); + + /* Kick TX ring. */ ring->cur = (ring->cur + 1) % IWN_TX_RING_COUNT; - IWN_WRITE(sc, IWN_TX_WIDX, ring->qid << 8 | ring->cur); + IWN_WRITE(sc, IWN_HBUS_TARG_WRPTR, ring->qid << 8 | ring->cur); + /* Mark TX ring as full if we reach a certain threshold. */ + if (++ring->queued > IWN_TX_RING_HIMARK) { + sc->qfullmsk |= 1 << ring->qid; + ic->ic_if.if_flags |= IFF_OACTIVE; + } return 0; } @@ -1913,11 +2334,8 @@ iwn_start(struct ifnet *ifp) for (;;) { IF_POLL(&ic->ic_mgtq, m0); if (m0 != NULL) { - /* management frames go into ring 0 */ - if (sc->txq[0].queued >= IWN_TX_RING_COUNT - 8) { - ifp->if_flags |= IFF_OACTIVE; + if (sc->qfullmsk != 0) break; - } IF_DEQUEUE(&ic->ic_mgtq, m0); ni = (struct ieee80211_node *)m0->m_pkthdr.rcvif; @@ -1926,7 +2344,7 @@ iwn_start(struct ifnet *ifp) if (ic->ic_rawbpf != NULL) bpf_mtap(ic->ic_rawbpf, m0, BPF_DIRECTION_OUT); #endif - if (iwn_tx_data(sc, m0, ni, 0) != 0) + if (iwn_tx_data(sc, m0, ni) != 0) break; } else { @@ -1935,11 +2353,8 @@ iwn_start(struct ifnet *ifp) IFQ_POLL(&ifp->if_snd, m0); if (m0 == NULL) break; - if (sc->txq[0].queued >= IWN_TX_RING_COUNT - 8) { - /* there is no place left in this ring */ - ifp->if_flags |= IFF_OACTIVE; + if (sc->qfullmsk != 0) break; - } IFQ_DEQUEUE(&ifp->if_snd, m0); #if NBPFILTER > 0 if (ifp->if_bpf != NULL) @@ -1952,7 +2367,7 @@ iwn_start(struct ifnet *ifp) if (ic->ic_rawbpf != NULL) bpf_mtap(ic->ic_rawbpf, m0, BPF_DIRECTION_OUT); #endif - if (iwn_tx_data(sc, m0, ni, 0) != 0) { + if (iwn_tx_data(sc, m0, ni) != 0) { if (ni != NULL) ieee80211_release_node(ic, ni); ifp->if_oerrors++; @@ -2009,7 +2424,7 @@ iwn_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) case SIOCSIFFLAGS: if (ifp->if_flags & IFF_UP) { if (!(ifp->if_flags & IFF_RUNNING)) - iwn_init(ifp); + (void)iwn_init(ifp); } else { if (ifp->if_flags & IFF_RUNNING) iwn_stop(ifp, 1); @@ -2032,173 +2447,64 @@ iwn_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) } if (error == ENETRESET) { + error = 0; if ((ifp->if_flags & (IFF_UP | IFF_RUNNING)) == (IFF_UP | IFF_RUNNING)) { iwn_stop(ifp, 0); - iwn_init(ifp); + (void)iwn_init(ifp); } - error = 0; } splx(s); return error; } -void -iwn_read_eeprom(struct iwn_softc *sc) -{ - struct ieee80211com *ic = &sc->sc_ic; - char domain[4]; - uint16_t val; - int i, error; - - if ((error = iwn_eeprom_lock(sc)) != 0) { - printf("%s: could not lock EEPROM (error=%d)\n", - sc->sc_dev.dv_xname, error); - return; - } - /* read and print regulatory domain */ - iwn_read_prom_data(sc, IWN_EEPROM_DOMAIN, domain, 4); - printf(", %.4s", domain); - - /* read and print MAC address */ - iwn_read_prom_data(sc, IWN_EEPROM_MAC, ic->ic_myaddr, 6); - printf(", address %s\n", ether_sprintf(ic->ic_myaddr)); - - /* read the list of authorized channels */ - for (i = 0; i < IWN_CHAN_BANDS_COUNT; i++) - iwn_read_eeprom_channels(sc, i); - - /* read maximum allowed Tx power for 2GHz and 5GHz bands */ - iwn_read_prom_data(sc, IWN_EEPROM_MAXPOW, &val, 2); - sc->maxpwr2GHz = val & 0xff; - sc->maxpwr5GHz = val >> 8; - /* check that EEPROM values are correct */ - if (sc->maxpwr5GHz < 20 || sc->maxpwr5GHz > 50) - sc->maxpwr5GHz = 38; - if (sc->maxpwr2GHz < 20 || sc->maxpwr2GHz > 50) - sc->maxpwr2GHz = 38; - DPRINTF(("maxpwr 2GHz=%d 5GHz=%d\n", sc->maxpwr2GHz, sc->maxpwr5GHz)); - - /* read voltage at which samples were taken */ - iwn_read_prom_data(sc, IWN_EEPROM_VOLTAGE, &val, 2); - sc->eeprom_voltage = (int16_t)letoh16(val); - DPRINTF(("voltage=%d (in 0.3V)\n", sc->eeprom_voltage)); - - /* read power groups */ - iwn_read_prom_data(sc, IWN_EEPROM_BANDS, sc->bands, sizeof sc->bands); -#ifdef IWN_DEBUG - if (iwn_debug > 0) { - for (i = 0; i < IWN_NBANDS; i++) - iwn_print_power_group(sc, i); - } -#endif - iwn_eeprom_unlock(sc); -} - -void -iwn_read_eeprom_channels(struct iwn_softc *sc, int n) -{ - struct ieee80211com *ic = &sc->sc_ic; - const struct iwn_chan_band *band = &iwn_bands[n]; - struct iwn_eeprom_chan channels[IWN_MAX_CHAN_PER_BAND]; - int chan, i; - - iwn_read_prom_data(sc, band->addr, channels, - band->nchan * sizeof (struct iwn_eeprom_chan)); - - for (i = 0; i < band->nchan; i++) { - if (!(channels[i].flags & IWN_EEPROM_CHAN_VALID)) - continue; - - chan = band->chan[i]; - - if (n == 0) { /* 2GHz band */ - ic->ic_channels[chan].ic_freq = - ieee80211_ieee2mhz(chan, IEEE80211_CHAN_2GHZ); - ic->ic_channels[chan].ic_flags = - IEEE80211_CHAN_CCK | IEEE80211_CHAN_OFDM | - IEEE80211_CHAN_DYN | IEEE80211_CHAN_2GHZ; - - } else { /* 5GHz band */ - /* - * Some adapters support channels 7, 8, 11 and 12 - * both in the 2GHz *and* 5GHz bands. - * Because of limitations in our net80211(9) stack, - * we can't support these channels in 5GHz band. - */ - if (chan <= 14) - continue; - - ic->ic_channels[chan].ic_freq = - ieee80211_ieee2mhz(chan, IEEE80211_CHAN_5GHZ); - ic->ic_channels[chan].ic_flags = IEEE80211_CHAN_A; - } - - /* is active scan allowed on this channel? */ - if (!(channels[i].flags & IWN_EEPROM_CHAN_ACTIVE)) { - ic->ic_channels[chan].ic_flags |= - IEEE80211_CHAN_PASSIVE; - } - - /* save maximum allowed power for this channel */ - sc->maxpwr[chan] = channels[i].maxpwr; - - DPRINTF(("adding chan %d flags=0x%x maxpwr=%d\n", - chan, channels[i].flags, sc->maxpwr[chan])); - } -} - -#ifdef IWN_DEBUG -void -iwn_print_power_group(struct iwn_softc *sc, int i) -{ - struct iwn_eeprom_band *band = &sc->bands[i]; - struct iwn_eeprom_chan_samples *chans = band->chans; - int j, c; - - printf("===band %d===\n", i); - printf("chan lo=%d, chan hi=%d\n", band->lo, band->hi); - printf("chan1 num=%d\n", chans[0].num); - for (c = 0; c < IWN_NTXCHAINS; c++) { - for (j = 0; j < IWN_NSAMPLES; j++) { - printf("chain %d, sample %d: temp=%d gain=%d " - "power=%d pa_det=%d\n", c, j, - chans[0].samples[c][j].temp, - chans[0].samples[c][j].gain, - chans[0].samples[c][j].power, - chans[0].samples[c][j].pa_det); - } - } - printf("chan2 num=%d\n", chans[1].num); - for (c = 0; c < IWN_NTXCHAINS; c++) { - for (j = 0; j < IWN_NSAMPLES; j++) { - printf("chain %d, sample %d: temp=%d gain=%d " - "power=%d pa_det=%d\n", c, j, - chans[1].samples[c][j].temp, - chans[1].samples[c][j].gain, - chans[1].samples[c][j].power, - chans[1].samples[c][j].pa_det); - } - } -} -#endif - /* * Send a command to the firmware. */ 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; struct iwn_tx_cmd *cmd; + struct mbuf *m; bus_addr_t paddr; - - KASSERT(size <= sizeof cmd->data); + int totlen, error; desc = &ring->desc[ring->cur]; - cmd = &ring->cmd[ring->cur]; + data = &ring->data[ring->cur]; + totlen = 4 + size; + + if (size > sizeof cmd->data) { + /* Command is too large to fit in a descriptor. */ + if (totlen > MCLBYTES) + return EINVAL; + MGETHDR(m, M_DONTWAIT, MT_DATA); + 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(sc->sc_dmat, data->map, cmd, totlen, + NULL, BUS_DMA_NOWAIT); + if (error != 0) { + m_freem(m); + return error; + } + data->m = m; + paddr = data->map->dm_segs[0].ds_addr; + } else { + cmd = &ring->cmd[ring->cur]; + paddr = data->cmd_paddr; + } cmd->code = code; cmd->flags = 0; @@ -2206,21 +2512,40 @@ iwn_cmd(struct iwn_softc *sc, int code, const void *buf, int size, int async) cmd->idx = ring->cur; memcpy(cmd->data, buf, size); - paddr = ring->cmd_dma.paddr + ring->cur * sizeof (struct iwn_tx_cmd); - IWN_SET_DESC_NSEGS(desc, 1); - IWN_SET_DESC_SEG(desc, 0, paddr, 4 + size); - sc->shared->len[ring->qid][ring->cur] = htole16(8); - if (ring->cur < IWN_TX_WINDOW) { - sc->shared->len[ring->qid][ring->cur + IWN_TX_RING_COUNT] = - htole16(8); - } + IWN_SET_DESC_SEG(desc, 0, paddr, totlen); - /* kick cmd ring */ + /* Update TX scheduler. */ + hal->update_sched(sc, ring->qid, ring->cur, 0, 0); + + /* Kick command ring. */ ring->cur = (ring->cur + 1) % IWN_TX_RING_COUNT; - IWN_WRITE(sc, IWN_TX_WIDX, ring->qid << 8 | ring->cur); + IWN_WRITE(sc, IWN_HBUS_TARG_WRPTR, ring->qid << 8 | ring->cur); + + return async ? 0 : tsleep(desc, PCATCH, "iwncmd", hz); +} + +int +iwn4965_add_node(struct iwn_softc *sc, struct iwn_node_info *node, int async) +{ + struct iwn4965_node_info hnode; + + /* We use the node structure for 5000 internally, convert it. */ + memset(&hnode, 0, sizeof hnode); + hnode.control = node->control; + IEEE80211_ADDR_COPY(hnode.macaddr, node->macaddr); + hnode.id = node->id; + hnode.flags = node->flags; + hnode.htflags = node->htflags; + /* XXX More fields. */ + return iwn_cmd(sc, IWN_CMD_ADD_NODE, &hnode, sizeof hnode, async); +} - return async ? 0 : tsleep(cmd, PCATCH, "iwncmd", hz); +int +iwn5000_add_node(struct iwn_softc *sc, struct iwn_node_info *node, int async) +{ + /* Direct mapping. */ + return iwn_cmd(sc, IWN_CMD_ADD_NODE, node, sizeof (*node), async); } /* @@ -2239,22 +2564,23 @@ iwn_setup_node_mrr(struct iwn_softc *sc, const struct ieee80211_node *ni, mrr.id = id; mrr.ssmask = 2; mrr.dsmask = 3; + mrr.ampdu_max = 64; mrr.ampdu_disable = 3; - mrr.ampdu_limit = 4000; + mrr.ampdu_limit = htole16(4000); r = rs->rs_nrates - 1; for (i = 0; i < IWN_MAX_TX_RETRIES && r >= 0; i++, r--) { rate = rs->rs_rates[r] & IEEE80211_RATE_VAL; - DPRINTF(("retry #%d: rate %d\n", i, rate)); + DPRINTF(("retry %d: rate %d\n", i, rate)); mrr.table[i].rate = iwn_plcp_signal(rate); mrr.table[i].rflags = IWN_RFLAG_ANT_B; if (!IWN_RATE_IS_OFDM(rate)) mrr.table[i].rflags |= IWN_RFLAG_CCK; } - /* pad with the lowest available bit-rate */ + /* Pad with the lowest available bit-rate. */ + rate = rs->rs_rates[0] & IEEE80211_RATE_VAL; for (; i < IWN_MAX_TX_RETRIES; i++) { - rate = rs->rs_rates[0] & IEEE80211_RATE_VAL; - DPRINTF(("retry #%d: rate %d\n", i, rate)); + DPRINTF(("retry %d: rate %d\n", i, rate)); mrr.table[i].rate = iwn_plcp_signal(rate); mrr.table[i].rflags = IWN_RFLAG_ANT_B; if (!IWN_RATE_IS_OFDM(rate)) @@ -2273,10 +2599,11 @@ iwn_set_fixed_rate(struct iwn_softc *sc, uint8_t id, uint8_t rate, int async) mrr.id = id; mrr.ssmask = 2; mrr.dsmask = 3; + mrr.ampdu_max = 64; mrr.ampdu_disable = 3; - mrr.ampdu_limit = 4000; + mrr.ampdu_limit = htole16(4000); - /* to setup a fixed rate, make all retries use the same rate.. */ + /* To set a fixed rate, we make all retries use the same rate. */ mrr.table[0].rate = iwn_plcp_signal(rate); mrr.table[0].rflags = IWN_RFLAG_ANT_B; if (!IWN_RATE_IS_OFDM(rate)) @@ -2288,38 +2615,6 @@ iwn_set_fixed_rate(struct iwn_softc *sc, uint8_t id, uint8_t rate, int async) return iwn_cmd(sc, IWN_CMD_NODE_MRR_SETUP, &mrr, sizeof mrr, async); } -/* - * Install a pairwise key into the hardware. - */ -int -iwn_set_key(struct ieee80211com *ic, struct ieee80211_node *ni, - struct ieee80211_key *k) -{ - struct iwn_softc *sc = ic->ic_softc; - struct iwn_node_info node; - - if (k->k_flags & IEEE80211_KEY_GROUP) - return 0; - - memset(&node, 0, sizeof node); - - switch (k->k_cipher) { - case IEEE80211_CIPHER_CCMP: - node.security = htole16(IWN_CIPHER_CCMP); - memcpy(node.key, k->k_key, k->k_len); - break; - default: - return 0; - } - - node.id = IWN_ID_BSS; - IEEE80211_ADDR_COPY(node.macaddr, ni->ni_macaddr); - node.control = IWN_NODE_UPDATE; - node.flags = IWN_FLAG_SET_KEY; - - return iwn_cmd(sc, IWN_CMD_ADD_NODE, &node, sizeof node, 1); -} - void iwn_updateedca(struct ieee80211com *ic) { @@ -2348,110 +2643,95 @@ iwn_set_led(struct iwn_softc *sc, uint8_t which, uint8_t off, uint8_t on) { struct iwn_cmd_led led; + /* Clear microcode LED ownership. */ + IWN_CLRBITS(sc, IWN_LED, IWN_LED_BSM_CTRL); + led.which = which; - led.unit = htole32(100000); /* on/off in unit of 100ms */ + led.unit = htole32(1000); /* on/off in unit of 100ms */ led.off = off; led.on = on; - (void)iwn_cmd(sc, IWN_CMD_SET_LED, &led, sizeof led, 1); } /* - * Set the critical temperature at which the firmware will automatically stop - * the radio transmitter. + * Set the critical temperature at which the firmware will notify us. */ int iwn_set_critical_temp(struct iwn_softc *sc) { - struct iwn_ucode_info *uc = &sc->ucode_info; struct iwn_critical_temp crit; - uint32_t r1, r2, r3, temp; - - r1 = letoh32(uc->temp[0].chan20MHz); - r2 = letoh32(uc->temp[1].chan20MHz); - r3 = letoh32(uc->temp[2].chan20MHz); - /* inverse function of iwn_get_temperature() */ - temp = r2 + (IWN_CTOK(110) * (r3 - r1)) / 259; - IWN_WRITE(sc, IWN_UCODE_CLR, IWN_CTEMP_STOP_RF); + IWN_WRITE(sc, IWN_UCODE_GP1_CLR, IWN_UCODE_GP1_CTEMP_STOP_RF); memset(&crit, 0, sizeof crit); - crit.tempR = htole32(temp); - DPRINTF(("setting critical temperature to %u\n", temp)); + crit.tempR = htole32(sc->critical_temp); + DPRINTF(("setting critical temperature to %u\n", sc->critical_temp)); return iwn_cmd(sc, IWN_CMD_SET_CRITICAL_TEMP, &crit, sizeof crit, 0); } -void -iwn_enable_tsf(struct iwn_softc *sc, struct ieee80211_node *ni) +int +iwn_set_timing(struct iwn_softc *sc, struct ieee80211_node *ni) { - struct iwn_cmd_tsf tsf; + struct iwn_cmd_timing cmd; uint64_t val, mod; - memset(&tsf, 0, sizeof tsf); - memcpy(&tsf.tstamp, ni->ni_tstamp, sizeof (uint64_t)); - tsf.bintval = htole16(ni->ni_intval); - tsf.lintval = htole16(10); + memset(&cmd, 0, sizeof cmd); + memcpy(&cmd.tstamp, ni->ni_tstamp, sizeof (uint64_t)); + cmd.bintval = htole16(ni->ni_intval); + cmd.lintval = htole16(10); - /* compute remaining time until next beacon */ + /* Compute remaining time until next beacon. */ val = (uint64_t)ni->ni_intval * 1024; /* msecs -> usecs */ - mod = letoh64(tsf.tstamp) % val; - tsf.binitval = htole32((uint32_t)(val - mod)); + mod = letoh64(cmd.tstamp) % val; + cmd.binitval = htole32((uint32_t)(val - mod)); - DPRINTF(("TSF bintval=%u tstamp=%llu, init=%u\n", - ni->ni_intval, letoh64(tsf.tstamp), (uint32_t)(val - mod))); + DPRINTF(("timing bintval=%u, tstamp=%llu, init=%u\n", + ni->ni_intval, letoh64(cmd.tstamp), (uint32_t)(val - mod))); - if (iwn_cmd(sc, IWN_CMD_TSF, &tsf, sizeof tsf, 1) != 0) - printf("%s: could not enable TSF\n", sc->sc_dev.dv_xname); + return iwn_cmd(sc, IWN_CMD_TIMING, &cmd, sizeof cmd, 1); } void -iwn_power_calibration(struct iwn_softc *sc, int temp) +iwn4965_power_calibration(struct iwn_softc *sc, int temp) { - struct ieee80211com *ic = &sc->sc_ic; - + /* Adjust TX power if need be (delta >= 3 degC.) */ DPRINTF(("temperature %d->%d\n", sc->temp, temp)); - - /* adjust Tx power if need be (delta >= 3°C) */ - if (abs(temp - sc->temp) < 3) - return; - - sc->temp = temp; - - DPRINTF(("setting Tx power for channel %d\n", - ieee80211_chan2ieee(ic, ic->ic_bss->ni_chan))); - if (iwn_set_txpower(sc, ic->ic_bss->ni_chan, 1) != 0) { - /* just warn, too bad for the automatic calibration... */ - printf("%s: could not adjust Tx power\n", sc->sc_dev.dv_xname); + if (abs(temp - sc->temp) >= 3) { + sc->temp = temp; + (void)iwn4965_set_txpower(sc, 1); } } /* - * Set Tx power for a given channel (each rate has its own power settings). + * Set TX power for current channel (each rate has its own power settings). * This function takes into account the regulatory information from EEPROM, * the current temperature and the current voltage. */ int -iwn_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 */ +/* Fixed-point arithmetic division using a n-bit fractional part. */ #define fdivround(a, b, n) \ ((((1 << n) * (a)) / (b) + (1 << n) / 2) / (1 << n)) -/* linear interpolation */ +/* Linear interpolation. */ #define interpolate(x, x1, y1, x2, y2, n) \ ((y1) + fdivround(((int)(x) - (x1)) * ((y2) - (y1)), (x2) - (x1), n)) static const int tdiv[IWN_NATTEN_GROUPS] = { 9, 8, 8, 8, 6 }; struct ieee80211com *ic = &sc->sc_ic; struct iwn_ucode_info *uc = &sc->ucode_info; - struct iwn_cmd_txpower cmd; - struct iwn_eeprom_chan_samples *chans; + struct ieee80211_channel *ch; + struct iwn4965_cmd_txpower cmd; + struct iwn4965_eeprom_chan_samples *chans; const uint8_t *rf_gain, *dsp_gain; int32_t vdiff, tdiff; int i, c, grp, maxpwr; - u_int chan; + uint8_t chan; - /* get channel number */ - chan = ieee80211_chan2ieee(ic, ch); + /* Retrieve current channel from last RXON. */ + chan = sc->rxon.chan; + DPRINTF(("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; @@ -2459,15 +2739,15 @@ iwn_set_txpower(struct iwn_softc *sc, struct ieee80211_channel *ch, int async) if (IEEE80211_IS_CHAN_5GHZ(ch)) { maxpwr = sc->maxpwr5GHz; - rf_gain = iwn_rf_gain_5ghz; - dsp_gain = iwn_dsp_gain_5ghz; + rf_gain = iwn4965_rf_gain_5ghz; + dsp_gain = iwn4965_dsp_gain_5ghz; } else { maxpwr = sc->maxpwr2GHz; - rf_gain = iwn_rf_gain_2ghz; - dsp_gain = iwn_dsp_gain_2ghz; + rf_gain = iwn4965_rf_gain_2ghz; + dsp_gain = iwn4965_dsp_gain_2ghz; } - /* compute voltage compensation */ + /* Compute voltage compensation. */ vdiff = ((int32_t)letoh32(uc->volt) - sc->eeprom_voltage) / 7; if (vdiff > 0) vdiff *= 2; @@ -2476,7 +2756,7 @@ iwn_set_txpower(struct iwn_softc *sc, struct ieee80211_channel *ch, int async) DPRINTF(("voltage compensation=%d (UCODE=%d, EEPROM=%d)\n", vdiff, letoh32(uc->volt), sc->eeprom_voltage)); - /* get channel's attenuation group */ + /* Get channel's attenuation group. */ if (chan <= 20) /* 1-20 */ grp = 4; else if (chan <= 43) /* 34-43 */ @@ -2489,7 +2769,7 @@ iwn_set_txpower(struct iwn_softc *sc, struct ieee80211_channel *ch, int async) grp = 3; DPRINTF(("chan %d, attenuation group=%d\n", chan, grp)); - /* get channel's sub-band */ + /* Get channel's 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) @@ -2497,7 +2777,7 @@ iwn_set_txpower(struct iwn_softc *sc, struct ieee80211_channel *ch, int async) chans = sc->bands[i].chans; DPRINTF(("chan %d sub-band=%d\n", chan, i)); - for (c = 0; c < IWN_NTXCHAINS; c++) { + for (c = 0; c < 2; c++) { uint8_t power, gain, temp; int maxchpwr, pwr, ridx, idx; @@ -2510,31 +2790,32 @@ iwn_set_txpower(struct iwn_softc *sc, struct ieee80211_channel *ch, int async) temp = interpolate(chan, chans[0].num, chans[0].samples[c][1].temp, chans[1].num, chans[1].samples[c][1].temp, 1); - DPRINTF(("Tx chain %d: power=%d gain=%d temp=%d\n", + DPRINTF(("TX chain %d: power=%d gain=%d temp=%d\n", c, power, gain, temp)); - /* compute temperature compensation */ + /* Compute temperature compensation. */ tdiff = ((sc->temp - temp) * 2) / tdiv[grp]; DPRINTF(("temperature compensation=%d (current=%d, " "EEPROM=%d)\n", tdiff, sc->temp, temp)); for (ridx = 0; ridx <= IWN_RIDX_MAX; ridx++) { maxchpwr = sc->maxpwr[chan] * 2; - if ((ridx / 8) & 1) { - /* MIMO: decrease Tx power (-3dB) */ - maxchpwr -= 6; - } - - pwr = maxpwr - 10; - - /* decrease power for highest OFDM rates */ - if ((ridx % 8) == 5) /* 48Mbit/s */ - pwr -= 5; - else if ((ridx % 8) == 6) /* 54Mbit/s */ - pwr -= 7; - else if ((ridx % 8) == 7) /* 60Mbit/s */ - pwr -= 10; - + if ((ridx / 8) & 1) + maxchpwr -= 6; /* MIMO 2T: -3dB */ + + pwr = maxpwr; + + /* Adjust TX power based on rate. */ + if ((ridx % 8) == 5) + pwr -= 15; /* OFDM48: -7.5dB */ + else if ((ridx % 8) == 6) + pwr -= 17; /* OFDM54: -8.5dB */ + else if ((ridx % 8) == 7) + pwr -= 20; /* OFDM60: -10dB */ + else + pwr -= 10; /* Others: -5dB */ + + /* Do not exceed channel's max TX power. */ if (pwr > maxchpwr) pwr = maxchpwr; @@ -2547,51 +2828,85 @@ iwn_set_txpower(struct iwn_softc *sc, struct ieee80211_channel *ch, int async) if (ridx == IWN_RIDX_MAX) idx += 5; /* CCK */ - /* make sure idx stays in a valid range */ + /* Make sure idx stays in a valid range. */ if (idx < 0) idx = 0; - else if (idx > IWN_MAX_PWR_INDEX) - idx = IWN_MAX_PWR_INDEX; + else if (idx > IWN4965_MAX_PWR_INDEX) + idx = IWN4965_MAX_PWR_INDEX; - DPRINTF(("Tx chain %d, rate idx %d: power=%d\n", + DPRINTF(("TX chain %d, rate idx %d: power=%d\n", c, ridx, idx)); cmd.power[ridx].rf_gain[c] = rf_gain[idx]; cmd.power[ridx].dsp_gain[c] = dsp_gain[idx]; } } - DPRINTF(("setting tx power for chan %d\n", chan)); + DPRINTF(("setting TX power for chan %d\n", chan)); return iwn_cmd(sc, IWN_CMD_TXPOWER, &cmd, sizeof cmd, async); #undef interpolate #undef fdivround } +int +iwn5000_set_txpower(struct iwn_softc *sc, int async) +{ + struct iwn5000_cmd_txpower cmd; + + /* + * TX power calibration is handled automatically by the firmware + * for 5000 Series. + */ + memset(&cmd, 0, sizeof cmd); + cmd.global_limit = 2 * IWN5000_TX_POWER_MAX_DBM; /* 16 dBm */ + cmd.flags = IWN5000_TX_POWER_NO_CLOSED; + cmd.srv_limit = IWN5000_TX_POWER_AUTO; + DPRINTF(("setting TX power\n")); + return iwn_cmd(sc, IWN_CMD_TXPOWER_DBM, &cmd, sizeof cmd, async); +} + /* - * Get the best (maximum) RSSI among Rx antennas (in dBm). + * Retrieve the maximum RSSI (in dBm) among receivers. */ int -iwn_get_rssi(const struct iwn_rx_stat *stat) +iwn4965_get_rssi(const struct iwn_rx_stat *stat) { + struct iwn4965_rx_phystat *phy = (void *)stat->phybuf; uint8_t mask, agc; int rssi; - mask = (letoh16(stat->antenna) >> 4) & 0x7; - agc = (letoh16(stat->agc) >> 7) & 0x7f; + mask = (letoh16(phy->antenna) >> 4) & 0x7; + agc = (letoh16(phy->agc) >> 7) & 0x7f; rssi = 0; if (mask & (1 << 0)) /* Ant A */ - rssi = MAX(rssi, stat->rssi[0]); + rssi = MAX(rssi, phy->rssi[0]); if (mask & (1 << 1)) /* Ant B */ - rssi = MAX(rssi, stat->rssi[2]); + rssi = MAX(rssi, phy->rssi[2]); if (mask & (1 << 2)) /* Ant C */ - rssi = MAX(rssi, stat->rssi[4]); + rssi = MAX(rssi, phy->rssi[4]); + + return rssi - agc - IWN_RSSI_TO_DBM; +} + +int +iwn5000_get_rssi(const struct iwn_rx_stat *stat) +{ + struct iwn5000_rx_phystat *phy = (void *)stat->phybuf; + uint8_t agc; + int rssi; + + agc = (letoh32(phy->agc) >> 9) & 0x7f; + + rssi = MAX(letoh16(phy->rssi[0]) & 0xff, + letoh16(phy->rssi[1]) & 0xff); + rssi = MAX(letoh16(phy->rssi[2]) & 0xff, rssi); return rssi - agc - IWN_RSSI_TO_DBM; } /* - * Get the average noise among Rx antennas (in dBm). + * Retrieve the average noise (in dBm) among receivers. */ int iwn_get_noise(const struct iwn_rx_general_stats *stats) @@ -2605,15 +2920,15 @@ iwn_get_noise(const struct iwn_rx_general_stats *stats) total += noise; nbant++; } - /* there should be at least one antenna but check anyway */ + /* There should be at least one antenna but check anyway. */ return (nbant == 0) ? -127 : (total / nbant) - 107; } /* - * Read temperature (in degC) from the on-board thermal sensor. + * Compute temperature (in degC) from last received statistics. */ int -iwn_get_temperature(struct iwn_softc *sc) +iwn4965_get_temperature(struct iwn_softc *sc) { struct iwn_ucode_info *uc = &sc->ucode_info; int32_t r1, r2, r3, r4, temp; @@ -2623,12 +2938,12 @@ iwn_get_temperature(struct iwn_softc *sc) r3 = letoh32(uc->temp[2].chan20MHz); r4 = letoh32(sc->rawtemp); - if (r1 == r3) /* prevents division by 0 (should not happen) */ + if (r1 == r3) /* Prevents division by 0 (should not happen.) */ return 0; - /* sign-extend 23-bit R4 value to 32-bit */ + /* Sign-extend 23-bit R4 value to 32-bit. */ r4 = (r4 << 8) >> 8; - /* compute temperature */ + /* Compute temperature in Kelvin. */ temp = (259 * (r4 - r2)) / (r3 - r1); temp = (temp * 97) / 100 + 8; @@ -2636,107 +2951,189 @@ iwn_get_temperature(struct iwn_softc *sc) return IWN_KTOC(temp); } +int +iwn5000_get_temperature(struct iwn_softc *sc) +{ + /* + * 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 letoh32(sc->rawtemp); +} + /* * Initialize sensitivity calibration state machine. */ int iwn_init_sensitivity(struct iwn_softc *sc) { + const struct iwn_hal *hal = sc->sc_hal; struct iwn_calib_state *calib = &sc->calib; - struct iwn_phy_calib_cmd cmd; + uint32_t flags; int error; - /* reset calibration state */ + /* Reset calibration state machine. */ memset(calib, 0, sizeof (*calib)); calib->state = IWN_CALIB_STATE_INIT; calib->cck_state = IWN_CCK_STATE_HIFA; - /* initial values taken from the reference driver */ - calib->corr_ofdm_x1 = 105; - calib->corr_ofdm_mrc_x1 = 220; - calib->corr_ofdm_x4 = 90; - calib->corr_ofdm_mrc_x4 = 170; - calib->corr_cck_x4 = 125; - calib->corr_cck_mrc_x4 = 200; - calib->energy_cck = 100; - - /* write initial sensitivity values */ + /* Set initial correlation values. */ + calib->ofdm_x1 = hal->limits->min_ofdm_x1; + calib->ofdm_mrc_x1 = hal->limits->min_ofdm_mrc_x1; + calib->ofdm_x4 = 90; + calib->ofdm_mrc_x4 = hal->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; + + /* Write initial sensitivity. */ if ((error = iwn_send_sensitivity(sc)) != 0) return error; - memset(&cmd, 0, sizeof cmd); - cmd.code = IWN_SET_DIFF_GAIN; - /* differential gains initially set to 0 for all 3 antennas */ - DPRINTF(("setting differential gains\n")); - return iwn_cmd(sc, IWN_PHY_CALIB, &cmd, sizeof cmd, 1); + /* Write initial gains. */ + if ((error = hal->init_gains(sc)) != 0) + return error; + + /* Request statistics at each beacon interval. */ + flags = 0; + DPRINTF(("sending request for statistics\n")); + return iwn_cmd(sc, IWN_CMD_GET_STATISTICS, &flags, sizeof flags, 1); } /* * Collect noise and RSSI statistics for the first 20 beacons received * after association and use them to determine connected antennas and - * set differential gains. + * to set differential gains. */ void -iwn_compute_differential_gain(struct iwn_softc *sc, +iwn_collect_noise(struct iwn_softc *sc, const struct iwn_rx_general_stats *stats) { + const struct iwn_hal *hal = sc->sc_hal; struct iwn_calib_state *calib = &sc->calib; - struct iwn_phy_calib_cmd cmd; - int i, val; + uint32_t val; + int i; - /* accumulate RSSI and noise for all 3 antennas */ + /* Accumulate RSSI and noise for all 3 antennas. */ for (i = 0; i < 3; i++) { calib->rssi[i] += letoh32(stats->rssi[i]) & 0xff; calib->noise[i] += letoh32(stats->noise[i]) & 0xff; } - - /* we update differential gain only once after 20 beacons */ + /* NB: We update differential gains only once after 20 beacons. */ if (++calib->nbeacons < 20) return; - /* determine antenna with highest average RSSI */ + /* Determine highest average RSSI. */ val = MAX(calib->rssi[0], calib->rssi[1]); val = MAX(calib->rssi[2], val); - /* determine which antennas are connected */ + /* Determine which antennas are connected. */ sc->antmsk = 0; for (i = 0; i < 3; i++) if (val - calib->rssi[i] <= 15 * 20) sc->antmsk |= 1 << i; - /* if neither Ant A and Ant B are connected.. */ + /* If neither antenna A nor antenna B are connected... */ if ((sc->antmsk & (1 << 0 | 1 << 1)) == 0) - sc->antmsk |= 1 << 1; /* ..mark Ant B as connected! */ + sc->antmsk |= 1 << 1; /* ...mark antenna B as connected! */ + + (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); + + /* XXX Enable power-saving mode. */ + if (ic->ic_flags & IEEE80211_F_PMGTON) + (void)iwn_set_pslevel(sc, 0, 3, 1); +#endif +} + +int +iwn4965_init_gains(struct iwn_softc *sc) +{ + struct iwn_phy_calib_cmd cmd; - /* get minimal noise among connected antennas */ - val = INT_MAX; /* ok, there's at least one */ + memset(&cmd, 0, sizeof cmd); + cmd.code = IWN4965_SET_DIFF_GAIN; + /* Differential gains initially set to 0 for all 3 antennas. */ + DPRINTF(("setting initial differential gains\n")); + return iwn_cmd(sc, IWN_PHY_CALIB, &cmd, sizeof cmd, 1); +} + +int +iwn5000_init_gains(struct iwn_softc *sc) +{ + uint32_t code; + + code = htole32(IWN5000_PHY_CALIB_RESET_NOISE_GAIN); + DPRINTF(("setting initial differential gains\n")); + return iwn_cmd(sc, IWN_PHY_CALIB, &code, sizeof code, 1); +} + +int +iwn4965_set_gains(struct iwn_softc *sc) +{ + struct iwn_calib_state *calib = &sc->calib; + struct iwn_phy_calib_cmd cmd; + int i, delta, noise; + + /* Get minimal noise among connected antennas. */ + noise = INT_MAX; /* NB: There's at least one antenna. */ for (i = 0; i < 3; i++) if (sc->antmsk & (1 << i)) - val = MIN(calib->noise[i], val); + noise = MIN(calib->noise[i], noise); memset(&cmd, 0, sizeof cmd); - cmd.code = IWN_SET_DIFF_GAIN; - /* set differential gains for connected antennas */ + cmd.code = IWN4965_SET_DIFF_GAIN; + /* Set differential gains for connected antennas. */ for (i = 0; i < 3; i++) { if (sc->antmsk & (1 << i)) { - cmd.gain[i] = (calib->noise[i] - val) / 30; - /* limit differential gain to 3 */ - cmd.gain[i] = MIN(cmd.gain[i], 3); - cmd.gain[i] |= IWN_GAIN_SET; + delta = ((int32_t)calib->noise[i] - noise) / 30; + cmd.gain[i] = MIN(delta, 3); + cmd.gain[i] |= 1 << 2; } } DPRINTF(("setting differential gains Ant A/B/C: %x/%x/%x (%x)\n", cmd.gain[0], cmd.gain[1], cmd.gain[2], sc->antmsk)); - if (iwn_cmd(sc, IWN_PHY_CALIB, &cmd, sizeof cmd, 1) == 0) - calib->state = IWN_CALIB_STATE_RUN; + return iwn_cmd(sc, IWN_PHY_CALIB, &cmd, sizeof cmd, 1); +} + +int +iwn5000_set_gains(struct iwn_softc *sc) +{ + struct iwn_calib_state *calib = &sc->calib; + struct iwn_phy_calib_cmd cmd; + int i, delta; + + memset(&cmd, 0, sizeof cmd); + cmd.code = IWN5000_PHY_CALIB_NOISE_GAIN; + /* 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] - + (int32_t)calib->noise[i]) / 30; + /* Limit differential gain to [-3,+3]. */ + cmd.gain[i] = MIN(abs(delta), 3); + if (delta < 0) + cmd.gain[i] |= 1 << 2; /* sign bit */ + } + } + DPRINTF(("setting differential gains Ant A/B/C: %x/%x/%x (%x)\n", + cmd.gain[0], cmd.gain[1], cmd.gain[2], sc->antmsk)); + return iwn_cmd(sc, IWN_PHY_CALIB, &cmd, sizeof cmd, 1); } /* - * Tune RF Rx sensitivity based on the number of false alarms detected + * Tune RF RX sensitivity based on the number of false alarms detected * during the last beacon period. */ void iwn_tune_sensitivity(struct iwn_softc *sc, const struct iwn_rx_stats *stats) { -#define inc_clip(val, inc, max) \ +#define inc(val, inc, max) \ if ((val) < (max)) { \ if ((val) < (max) - (inc)) \ (val) += (inc); \ @@ -2744,7 +3141,7 @@ iwn_tune_sensitivity(struct iwn_softc *sc, const struct iwn_rx_stats *stats) (val) = (max); \ needs_update = 1; \ } -#define dec_clip(val, dec, min) \ +#define dec(val, dec, min) \ if ((val) > (min)) { \ if ((val) > (min) + (dec)) \ (val) -= (dec); \ @@ -2753,219 +3150,312 @@ 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; struct iwn_calib_state *calib = &sc->calib; uint32_t val, rxena, fa; uint32_t energy[3], energy_min; uint8_t noise[3], noise_ref; int i, needs_update = 0; - /* check that we've been enabled long enough */ + /* Check that we've been enabled long enough. */ if ((rxena = letoh32(stats->general.load)) == 0) return; - /* compute number of false alarms since last call for OFDM */ + /* Compute number of false alarms since last call for OFDM. */ fa = letoh32(stats->ofdm.bad_plcp) - calib->bad_plcp_ofdm; fa += letoh32(stats->ofdm.fa) - calib->fa_ofdm; fa *= 200 * 1024; /* 200TU */ - /* save counters values for next call */ + /* Save counters values for next call. */ calib->bad_plcp_ofdm = letoh32(stats->ofdm.bad_plcp); calib->fa_ofdm = letoh32(stats->ofdm.fa); if (fa > 50 * rxena) { - /* high false alarm count, decrease sensitivity */ + /* High false alarm count, decrease sensitivity. */ DPRINTFN(2, ("OFDM high false alarm count: %u\n", fa)); - inc_clip(calib->corr_ofdm_x1, 1, 140); - inc_clip(calib->corr_ofdm_mrc_x1, 1, 270); - inc_clip(calib->corr_ofdm_x4, 1, 120); - inc_clip(calib->corr_ofdm_mrc_x4, 1, 210); + inc(calib->ofdm_x1, 1, limits->max_ofdm_x1); + inc(calib->ofdm_mrc_x1, 1, limits->max_ofdm_mrc_x1); + inc(calib->ofdm_x4, 1, limits->max_ofdm_x4); + inc(calib->ofdm_mrc_x4, 1, limits->max_ofdm_mrc_x4); } else if (fa < 5 * rxena) { - /* low false alarm count, increase sensitivity */ + /* Low false alarm count, increase sensitivity. */ DPRINTFN(2, ("OFDM low false alarm count: %u\n", fa)); - dec_clip(calib->corr_ofdm_x1, 1, 105); - dec_clip(calib->corr_ofdm_mrc_x1, 1, 220); - dec_clip(calib->corr_ofdm_x4, 1, 85); - dec_clip(calib->corr_ofdm_mrc_x4, 1, 170); + dec(calib->ofdm_x1, 1, limits->min_ofdm_x1); + dec(calib->ofdm_mrc_x1, 1, limits->min_ofdm_mrc_x1); + dec(calib->ofdm_x4, 1, limits->min_ofdm_x4); + dec(calib->ofdm_mrc_x4, 1, limits->min_ofdm_mrc_x4); } - /* compute maximum noise among 3 antennas */ + /* Compute maximum noise among 3 receivers. */ for (i = 0; i < 3; i++) noise[i] = (letoh32(stats->general.noise[i]) >> 8) & 0xff; val = MAX(noise[0], noise[1]); val = MAX(noise[2], val); - /* insert it into our samples table */ + /* Insert it into our samples table. */ calib->noise_samples[calib->cur_noise_sample] = val; calib->cur_noise_sample = (calib->cur_noise_sample + 1) % 20; - /* compute maximum noise among last 20 samples */ + /* Compute maximum noise among last 20 samples. */ noise_ref = calib->noise_samples[0]; for (i = 1; i < 20; i++) noise_ref = MAX(noise_ref, calib->noise_samples[i]); - /* compute maximum energy among 3 antennas */ + /* Compute maximum energy among 3 receivers. */ for (i = 0; i < 3; i++) energy[i] = letoh32(stats->general.energy[i]); val = MIN(energy[0], energy[1]); val = MIN(energy[2], val); - /* insert it into our samples table */ + /* Insert it into our samples table. */ calib->energy_samples[calib->cur_energy_sample] = val; calib->cur_energy_sample = (calib->cur_energy_sample + 1) % 10; - /* compute minimum energy among last 10 samples */ + /* Compute minimum energy among last 10 samples. */ energy_min = calib->energy_samples[0]; for (i = 1; i < 10; i++) energy_min = MAX(energy_min, calib->energy_samples[i]); energy_min += 6; - /* compute number of false alarms since last call for CCK */ + /* Compute number of false alarms since last call for CCK. */ fa = letoh32(stats->cck.bad_plcp) - calib->bad_plcp_cck; fa += letoh32(stats->cck.fa) - calib->fa_cck; fa *= 200 * 1024; /* 200TU */ - /* save counters values for next call */ + /* Save counters values for next call. */ calib->bad_plcp_cck = letoh32(stats->cck.bad_plcp); calib->fa_cck = letoh32(stats->cck.fa); if (fa > 50 * rxena) { - /* high false alarm count, decrease sensitivity */ + /* High false alarm count, decrease sensitivity. */ DPRINTFN(2, ("CCK high false alarm count: %u\n", fa)); calib->cck_state = IWN_CCK_STATE_HIFA; calib->low_fa = 0; - if (calib->corr_cck_x4 > 160) { + if (calib->cck_x4 > 160) { calib->noise_ref = noise_ref; if (calib->energy_cck > 2) - dec_clip(calib->energy_cck, 2, energy_min); + dec(calib->energy_cck, 2, energy_min); } - if (calib->corr_cck_x4 < 160) { - calib->corr_cck_x4 = 161; + if (calib->cck_x4 < 160) { + calib->cck_x4 = 161; needs_update = 1; } else - inc_clip(calib->corr_cck_x4, 3, 200); + inc(calib->cck_x4, 3, limits->max_cck_x4); - inc_clip(calib->corr_cck_mrc_x4, 3, 400); + inc(calib->cck_mrc_x4, 3, limits->max_cck_mrc_x4); } else if (fa < 5 * rxena) { - /* low false alarm count, increase sensitivity */ + /* Low false alarm count, increase sensitivity. */ DPRINTFN(2, ("CCK low false alarm count: %u\n", fa)); calib->cck_state = IWN_CCK_STATE_LOFA; calib->low_fa++; - if (calib->cck_state != 0 && - ((calib->noise_ref - noise_ref) > 2 || + if (calib->cck_state != IWN_CCK_STATE_INIT && + (((int32_t)calib->noise_ref - (int32_t)noise_ref) > 2 || calib->low_fa > 100)) { - inc_clip(calib->energy_cck, 2, 97); - dec_clip(calib->corr_cck_x4, 3, 125); - dec_clip(calib->corr_cck_mrc_x4, 3, 200); + inc(calib->energy_cck, 2, limits->min_energy_cck); + dec(calib->cck_x4, 3, limits->min_cck_x4); + dec(calib->cck_mrc_x4, 3, limits->min_cck_mrc_x4); } } else { - /* not worth to increase or decrease sensitivity */ + /* Not worth to increase or decrease sensitivity. */ DPRINTFN(2, ("CCK normal false alarm count: %u\n", fa)); calib->low_fa = 0; calib->noise_ref = noise_ref; if (calib->cck_state == IWN_CCK_STATE_HIFA) { /* previous interval had many false alarms */ - dec_clip(calib->energy_cck, 8, energy_min); + dec(calib->energy_cck, 8, energy_min); } calib->cck_state = IWN_CCK_STATE_INIT; } if (needs_update) (void)iwn_send_sensitivity(sc); -#undef dec_clip -#undef inc_clip +#undef dec +#undef inc } 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; memset(&cmd, 0, sizeof cmd); cmd.which = IWN_SENSITIVITY_WORKTBL; - /* OFDM modulation */ - cmd.corr_ofdm_x1 = htole16(calib->corr_ofdm_x1); - cmd.corr_ofdm_mrc_x1 = htole16(calib->corr_ofdm_mrc_x1); - cmd.corr_ofdm_x4 = htole16(calib->corr_ofdm_x4); - cmd.corr_ofdm_mrc_x4 = htole16(calib->corr_ofdm_mrc_x4); - cmd.energy_ofdm = htole16(100); + /* OFDM modulation. */ + cmd.corr_ofdm_x1 = htole16(calib->ofdm_x1); + 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_th = htole16(62); - /* CCK modulation */ - cmd.corr_cck_x4 = htole16(calib->corr_cck_x4); - cmd.corr_cck_mrc_x4 = htole16(calib->corr_cck_mrc_x4); + /* CCK modulation. */ + cmd.corr_cck_x4 = htole16(calib->cck_x4); + cmd.corr_cck_mrc_x4 = htole16(calib->cck_mrc_x4); cmd.energy_cck = htole16(calib->energy_cck); - /* Barker modulation: use default values */ + /* Barker modulation: use default values. */ cmd.corr_barker = htole16(190); cmd.corr_barker_mrc = htole16(390); DPRINTFN(2, ("setting sensitivity %d/%d/%d/%d/%d/%d/%d\n", - calib->corr_ofdm_x1, calib->corr_ofdm_mrc_x1, calib->corr_ofdm_x4, - calib->corr_ofdm_mrc_x4, calib->corr_cck_x4, - calib->corr_cck_mrc_x4, calib->energy_cck)); + calib->ofdm_x1, calib->ofdm_mrc_x1, calib->ofdm_x4, + calib->ofdm_mrc_x4, calib->cck_x4, calib->cck_mrc_x4, + calib->energy_cck)); return iwn_cmd(sc, IWN_SENSITIVITY, &cmd, sizeof cmd, 1); } +/* + * Set STA mode power saving level (between 0 and 5). + * Level 0 is CAM (Continuously Aware Mode), 5 is for maximum power saving. + */ int -iwn_auth(struct iwn_softc *sc) +iwn_set_pslevel(struct iwn_softc *sc, int dtim, int level, int async) +{ + struct iwn_pmgt_cmd cmd; + const struct iwn_pmgt *pmgt; + uint32_t max, skip_dtim; + pcireg_t reg; + int i; + + /* Select which PS parameters to use. */ + if (dtim <= 2) + pmgt = &iwn_pmgt[0][level]; + else if (dtim <= 10) + pmgt = &iwn_pmgt[1][level]; + else + pmgt = &iwn_pmgt[2][level]; + + memset(&cmd, 0, sizeof cmd); + if (level != 0) /* not CAM */ + cmd.flags |= htole16(IWN_PS_ALLOW_SLEEP); + if (level == 5) + cmd.flags |= htole16(IWN_PS_FAST_PD); + /* Retrieve PCIe Active State Power Management (ASPM). */ + reg = pci_conf_read(sc->sc_pct, sc->sc_pcitag, + sc->sc_cap_off + PCI_PCIE_LCSR); + if (!(reg & PCI_PCIE_LCSR_ASPM_L0S)) /* L0s Entry disabled. */ + cmd.flags |= htole16(IWN_PS_PCI_PMGT); + cmd.rxtimeout = htole32(pmgt->rxtimeout * 1024); + cmd.txtimeout = htole32(pmgt->txtimeout * 1024); + + if (dtim == 0) { + dtim = 1; + skip_dtim = 0; + } else + skip_dtim = pmgt->skip_dtim; + if (skip_dtim != 0) { + cmd.flags |= htole16(IWN_PS_SLEEP_OVER_DTIM); + max = pmgt->intval[4]; + if (max == (uint32_t)-1) + max = dtim * (skip_dtim + 1); + else if (max > dtim) + max = (max / dtim) * dtim; + } else + max = dtim; + for (i = 0; i < 5; i++) + cmd.intval[i] = htole32(MIN(max, pmgt->intval[i])); + + DPRINTF(("setting power saving level to %d\n", level)); + return iwn_cmd(sc, IWN_CMD_SET_POWER_MODE, &cmd, sizeof cmd, async); +} + +int +iwn_config(struct iwn_softc *sc) { + const struct iwn_hal *hal = sc->sc_hal; struct ieee80211com *ic = &sc->sc_ic; - struct ieee80211_node *ni = ic->ic_bss; + struct ifnet *ifp = &ic->ic_if; + struct iwn_bluetooth bluetooth; struct iwn_node_info node; + uint16_t rxchain; uint8_t rate; int error; - /* update adapter's configuration */ - IEEE80211_ADDR_COPY(sc->config.bssid, ni->ni_bssid); - sc->config.chan = ieee80211_chan2ieee(ic, ni->ni_chan); - sc->config.flags = htole32(IWN_CONFIG_TSF); - if (IEEE80211_IS_CHAN_2GHZ(ni->ni_chan)) { - sc->config.flags |= htole32(IWN_CONFIG_AUTO | - IWN_CONFIG_24GHZ); + /* Set power-saving level to CAM during initialization. */ + if ((error = iwn_set_pslevel(sc, 0, 0, 0)) != 0) { + printf("%s: could not set power saving level\n", + sc->sc_dev.dv_xname); + return error; } - switch (ic->ic_curmode) { - case IEEE80211_MODE_11A: - sc->config.cck_mask = 0; - sc->config.ofdm_mask = 0x15; + + /* Configure bluetooth coexistence. */ + memset(&bluetooth, 0, sizeof bluetooth); + bluetooth.flags = 3; + bluetooth.lead = 0xaa; + bluetooth.kill = 1; + DPRINTF(("configuring bluetooth coexistence\n")); + error = iwn_cmd(sc, IWN_CMD_BT_COEX, &bluetooth, sizeof bluetooth, 0); + if (error != 0) { + printf("%s: could not configure bluetooth coexistence\n", + sc->sc_dev.dv_xname); + return error; + } + + /* Configure adapter. */ + memset(&sc->rxon, 0, sizeof (struct iwn_rxon)); + IEEE80211_ADDR_COPY(ic->ic_myaddr, LLADDR(ifp->if_sadl)); + IEEE80211_ADDR_COPY(sc->rxon.myaddr, ic->ic_myaddr); + IEEE80211_ADDR_COPY(sc->rxon.wlap, ic->ic_myaddr); + /* Set default channel. */ + sc->rxon.chan = ieee80211_chan2ieee(ic, ic->ic_ibss_chan); + sc->rxon.flags = htole32(IWN_RXON_TSF | IWN_RXON_CTS_TO_SELF); + if (IEEE80211_IS_CHAN_2GHZ(ic->ic_ibss_chan)) + sc->rxon.flags |= htole32(IWN_RXON_AUTO | IWN_RXON_24GHZ); + switch (ic->ic_opmode) { + case IEEE80211_M_STA: + sc->rxon.mode = IWN_MODE_STA; + sc->rxon.filter = htole32(IWN_FILTER_MULTICAST); break; - case IEEE80211_MODE_11B: - sc->config.cck_mask = 0x03; - sc->config.ofdm_mask = 0; +#ifndef IEEE80211_STA_ONLY +#ifdef notyet + 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; +#endif +#endif + case IEEE80211_M_MONITOR: + sc->rxon.mode = IWN_MODE_MONITOR; + sc->rxon.filter = htole32(IWN_FILTER_MULTICAST | + IWN_FILTER_CTL | IWN_FILTER_PROMISC); + break; + default: + /* Should not get there. */ break; - default: /* assume 802.11b/g */ - sc->config.cck_mask = 0x0f; - sc->config.ofdm_mask = 0x15; } - if (ic->ic_flags & IEEE80211_F_SHSLOT) - sc->config.flags |= htole32(IWN_CONFIG_SHSLOT); - if (ic->ic_flags & IEEE80211_F_SHPREAMBLE) - sc->config.flags |= htole32(IWN_CONFIG_SHPREAMBLE); - DPRINTF(("config chan %d flags %x cck %x ofdm %x\n", sc->config.chan, - sc->config.flags, sc->config.cck_mask, sc->config.ofdm_mask)); - error = iwn_cmd(sc, IWN_CMD_CONFIGURE, &sc->config, - sizeof (struct iwn_config), 1); + sc->rxon.cck_mask = 0x0f; /* not yet negotiated */ + 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.rxchain = htole16(rxchain); + DPRINTF(("setting configuration\n")); + error = iwn_cmd(sc, IWN_CMD_CONFIGURE, &sc->rxon, hal->rxonsz, 0); if (error != 0) { - printf("%s: could not configure\n", sc->sc_dev.dv_xname); + printf("%s: configure command failed\n", sc->sc_dev.dv_xname); return error; } - /* configuration has changed, set Tx power accordingly */ - if ((error = iwn_set_txpower(sc, ni->ni_chan, 1)) != 0) { - printf("%s: could not set Tx power\n", sc->sc_dev.dv_xname); + /* Configuration has changed, set TX power accordingly. */ + if ((error = hal->set_txpower(sc, 0)) != 0) { + printf("%s: could not set TX power\n", sc->sc_dev.dv_xname); return error; } - /* - * Reconfiguring clears the adapter's nodes table so we must - * add the broadcast node again. - */ + /* Add broadcast node. */ memset(&node, 0, sizeof node); IEEE80211_ADDR_COPY(node.macaddr, etherbroadcastaddr); - node.id = IWN_ID_BROADCAST; + node.id = hal->broadcast_id; DPRINTF(("adding broadcast node\n")); - error = iwn_cmd(sc, IWN_CMD_ADD_NODE, &node, sizeof node, 1); + error = hal->add_node(sc, &node, 0); if (error != 0) { printf("%s: could not add broadcast node\n", sc->sc_dev.dv_xname); @@ -2973,110 +3463,28 @@ iwn_auth(struct iwn_softc *sc) } DPRINTF(("setting fixed rate for node %d\n", node.id)); rate = (ic->ic_curmode == IEEE80211_MODE_11A) ? 12 : 2; - if ((error = iwn_set_fixed_rate(sc, node.id, rate, 1)) != 0) { - printf("%s: could not setup MRR for broadcast node\n", - sc->sc_dev.dv_xname, node.id); - return error; - } - - return 0; -} - -/* - * Configure the adapter for associated state. - */ -int -iwn_run(struct iwn_softc *sc) -{ - struct ieee80211com *ic = &sc->sc_ic; - struct ieee80211_node *ni = ic->ic_bss; - struct iwn_node_info node; - int error; - - if (ic->ic_opmode == IEEE80211_M_MONITOR) { - /* link LED blinks while monitoring */ - iwn_set_led(sc, IWN_LED_LINK, 5, 5); - return 0; - } - - iwn_enable_tsf(sc, ni); - - /* update adapter's configuration */ - sc->config.associd = htole16(ni->ni_associd & ~0xc000); - /* short preamble/slot time are negotiated when associating */ - sc->config.flags &= ~htole32(IWN_CONFIG_SHPREAMBLE | - IWN_CONFIG_SHSLOT); - if (ic->ic_flags & IEEE80211_F_SHSLOT) - sc->config.flags |= htole32(IWN_CONFIG_SHSLOT); - if (ic->ic_flags & IEEE80211_F_SHPREAMBLE) - sc->config.flags |= htole32(IWN_CONFIG_SHPREAMBLE); - sc->config.filter |= htole32(IWN_FILTER_BSS); - - DPRINTF(("config chan %d flags %x\n", sc->config.chan, - sc->config.flags)); - error = iwn_cmd(sc, IWN_CMD_CONFIGURE, &sc->config, - sizeof (struct iwn_config), 1); - if (error != 0) { - printf("%s: could not update configuration\n", - sc->sc_dev.dv_xname); - return error; - } - - /* configuration has changed, set Tx power accordingly */ - if ((error = iwn_set_txpower(sc, ni->ni_chan, 1)) != 0) { - printf("%s: could not set Tx power\n", - sc->sc_dev.dv_xname); - return error; - } - - /* add BSS node */ - memset(&node, 0, sizeof node); - IEEE80211_ADDR_COPY(node.macaddr, ni->ni_macaddr); - node.id = IWN_ID_BSS; - node.htflags = htole32(3 << IWN_AMDPU_SIZE_FACTOR_SHIFT | - 5 << IWN_AMDPU_DENSITY_SHIFT); - DPRINTF(("adding BSS node\n")); - error = iwn_cmd(sc, IWN_CMD_ADD_NODE, &node, sizeof node, 1); - if (error != 0) { - printf("%s: could not add BSS node\n", sc->sc_dev.dv_xname); - return error; - } - DPRINTF(("setting MRR for node %d\n", node.id)); - if ((error = iwn_setup_node_mrr(sc, ni, node.id)) != 0) { + if ((error = iwn_set_fixed_rate(sc, node.id, rate, 0)) != 0) { printf("%s: could not setup MRR for node %d\n", sc->sc_dev.dv_xname, node.id); return error; } - if (ic->ic_opmode == IEEE80211_M_STA) { - /* fake a join to init the tx rate */ - iwn_newassoc(ic, ni, 1); - } - - if ((error = iwn_init_sensitivity(sc)) != 0) { - printf("%s: could not set sensitivity\n", + if ((error = iwn_set_critical_temp(sc)) != 0) { + printf("%s: could not set critical temperature\n", sc->sc_dev.dv_xname); return error; } - - /* start periodic calibration timer */ - sc->calib.state = IWN_CALIB_STATE_ASSOC; - sc->calib_cnt = 0; - timeout_add(&sc->calib_to, hz / 2); - - /* link LED always on while associated */ - iwn_set_led(sc, IWN_LED_LINK, 0, 1); - return 0; } /* - * Send a scan request to the firmware. Since this command is huge, we map it - * into a mbuf instead of using the pre-allocated set of commands. + * Send a scan request to the firmware. Since this command is huge, we map + * it into an mbuf instead of using the pre-allocated set of commands. */ int iwn_scan(struct iwn_softc *sc, uint16_t flags) { + const struct iwn_hal *hal = sc->sc_hal; struct ieee80211com *ic = &sc->sc_ic; struct iwn_tx_ring *ring = &sc->txq[4]; struct iwn_tx_desc *desc; @@ -3089,8 +3497,8 @@ iwn_scan(struct iwn_softc *sc, uint16_t flags) struct ieee80211_frame *wh; struct ieee80211_rateset *rs; struct ieee80211_channel *c; - enum ieee80211_phymode mode; uint8_t *frm; + uint16_t rxchain; int pktlen, error; desc = &ring->desc[ring->cur]; @@ -3120,36 +3528,46 @@ iwn_scan(struct iwn_softc *sc, uint16_t flags) hdr = (struct iwn_scan_hdr *)cmd->data; memset(hdr, 0, sizeof (struct iwn_scan_hdr)); /* - * Move to the next channel if no packets are received within 5 msecs + * Move to the next channel if no packets are received within 10 msecs * after sending the probe request (this helps to reduce the duration * of active scans). */ - hdr->quiet = htole16(5); /* timeout in milliseconds */ - hdr->plcp_threshold = htole16(1); /* min # of packets */ - - /* select Ant B and Ant C for scanning */ - hdr->rxchain = htole16(0x3e1 | 7 << IWN_RXCHAIN_ANTMSK_SHIFT); + hdr->quiet_time = htole16(10); /* timeout in milliseconds */ + 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); + if (sc->hw_type == IWN_HW_REV_TYPE_4965) { + /* Ant A must be avoided because of an HW bug in 5GHz. */ + rxchain |= IWN_RXCHAIN_SEL(IWN_ANT_B | IWN_ANT_C); + } else /* Use all available RX antennas. */ + rxchain |= IWN_RXCHAIN_SEL((1 << sc->nrxchains) - 1); + hdr->rxchain = htole16(rxchain); + hdr->filter = htole32(IWN_FILTER_MULTICAST | IWN_FILTER_BEACON); tx = (struct iwn_cmd_data *)(hdr + 1); memset(tx, 0, sizeof (struct iwn_cmd_data)); - tx->flags = htole32(IWN_TX_AUTO_SEQ | 0x200); /* XXX */ - tx->id = IWN_ID_BROADCAST; + tx->flags = htole32(IWN_TX_AUTO_SEQ); + tx->id = hal->broadcast_id; tx->lifetime = htole32(IWN_LIFETIME_INFINITE); - tx->rflags = IWN_RFLAG_ANT_B; - if (flags & IEEE80211_CHAN_A) { + if (flags & IEEE80211_CHAN_5GHZ) { hdr->crc_threshold = htole16(1); - /* send probe requests at 6Mbps */ + /* Send probe requests at 6Mbps. */ tx->rate = iwn_ridx_to_plcp[IWN_OFDM6]; + rs = &ic->ic_sup_rates[IEEE80211_MODE_11A]; } else { - hdr->flags = htole32(IWN_CONFIG_24GHZ | IWN_CONFIG_AUTO); - /* send probe requests at 1Mbps */ + hdr->flags = htole32(IWN_RXON_24GHZ | IWN_RXON_AUTO); + /* Send probe requests at 1Mbps. */ tx->rate = iwn_ridx_to_plcp[IWN_CCK1]; - tx->rflags |= IWN_RFLAG_CCK; + tx->rflags = IWN_RFLAG_CCK; + rs = &ic->ic_sup_rates[IEEE80211_MODE_11G]; } + tx->rflags |= IWN_RFLAG_ANT_B; essid = (struct iwn_scan_essid *)(tx + 1); - memset(essid, 0, 4 * sizeof (struct iwn_scan_essid)); + memset(essid, 0, 20 * sizeof (struct iwn_scan_essid)); essid[0].id = IEEE80211_ELEMID_SSID; essid[0].len = ic->ic_des_esslen; memcpy(essid[0].data, ic->ic_des_essid, ic->ic_des_esslen); @@ -3158,32 +3576,23 @@ iwn_scan(struct iwn_softc *sc, uint16_t flags) * Build a probe request frame. Most of the following code is a * copy & paste of what is done in net80211. */ - wh = (struct ieee80211_frame *)&essid[4]; + wh = (struct ieee80211_frame *)&essid[20]; wh->i_fc[0] = IEEE80211_FC0_VERSION_0 | IEEE80211_FC0_TYPE_MGT | IEEE80211_FC0_SUBTYPE_PROBE_REQ; wh->i_fc[1] = IEEE80211_FC1_DIR_NODS; IEEE80211_ADDR_COPY(wh->i_addr1, etherbroadcastaddr); IEEE80211_ADDR_COPY(wh->i_addr2, ic->ic_myaddr); IEEE80211_ADDR_COPY(wh->i_addr3, etherbroadcastaddr); - *(u_int16_t *)&wh->i_dur[0] = 0; /* filled by h/w */ - *(u_int16_t *)&wh->i_seq[0] = 0; /* filled by h/w */ + *(u_int16_t *)&wh->i_dur[0] = 0; /* filled by HW */ + *(u_int16_t *)&wh->i_seq[0] = 0; /* filled by HW */ frm = (uint8_t *)(wh + 1); - - /* add SSID IE */ frm = ieee80211_add_ssid(frm, ic->ic_des_essid, ic->ic_des_esslen); - - mode = ieee80211_chan2mode(ic, ic->ic_ibss_chan); - rs = &ic->ic_sup_rates[mode]; - - /* add supported rates IE */ frm = ieee80211_add_rates(frm, rs); - - /* add supported xrates IE */ if (rs->rs_nrates > IEEE80211_RATE_SIZE) frm = ieee80211_add_xrates(frm, rs); - /* setup length of probe request */ + /* Set length of probe request. */ tx->len = htole16(frm - (uint8_t *)wh); chan = (struct iwn_scan_chan *)frm; @@ -3192,12 +3601,12 @@ iwn_scan(struct iwn_softc *sc, uint16_t flags) if ((c->ic_flags & flags) != flags) continue; - chan->chan = ieee80211_chan2ieee(ic, c); - chan->flags = 0; + chan->chan = htole16(ieee80211_chan2ieee(ic, c)); + chan->type = 0; if (!(c->ic_flags & IEEE80211_CHAN_PASSIVE)) { - chan->flags |= IWN_CHAN_ACTIVE; + chan->type |= IWN_CHAN_ACTIVE; if (ic->ic_des_esslen != 0) - chan->flags |= IWN_CHAN_DIRECT; + chan->type |= IWN_CHAN_DIRECT; } chan->dsp_gain = 0x6e; if (IEEE80211_IS_CHAN_5GHZ(c)) { @@ -3231,377 +3640,1131 @@ iwn_scan(struct iwn_softc *sc, uint16_t flags) IWN_SET_DESC_NSEGS(desc, 1); IWN_SET_DESC_SEG(desc, 0, data->map->dm_segs[0].ds_addr, data->map->dm_segs[0].ds_len); - sc->shared->len[ring->qid][ring->cur] = htole16(8); - if (ring->cur < IWN_TX_WINDOW) { - sc->shared->len[ring->qid][ring->cur + IWN_TX_RING_COUNT] = - htole16(8); - } - /* kick cmd ring */ + /* Update TX scheduler. */ + hal->update_sched(sc, ring->qid, ring->cur, 0, 0); + + /* Kick command ring. */ ring->cur = (ring->cur + 1) % IWN_TX_RING_COUNT; - IWN_WRITE(sc, IWN_TX_WIDX, ring->qid << 8 | ring->cur); + IWN_WRITE(sc, IWN_HBUS_TARG_WRPTR, ring->qid << 8 | ring->cur); - return 0; /* will be notified async. of failure/success */ + return 0; } int -iwn_config(struct iwn_softc *sc) +iwn_auth(struct iwn_softc *sc) { + const struct iwn_hal *hal = sc->sc_hal; struct ieee80211com *ic = &sc->sc_ic; - struct ifnet *ifp = &ic->ic_if; - struct iwn_power power; - struct iwn_bluetooth bluetooth; + struct ieee80211_node *ni = ic->ic_bss; struct iwn_node_info node; uint8_t rate; int error; - /* set power mode */ - memset(&power, 0, sizeof power); - power.flags = htole16(IWN_POWER_CAM | 0x8); - DPRINTF(("setting power mode\n")); - error = iwn_cmd(sc, IWN_CMD_SET_POWER_MODE, &power, sizeof power, 0); + /* Update adapter's configuration. */ + IEEE80211_ADDR_COPY(sc->rxon.bssid, ni->ni_bssid); + sc->rxon.chan = ieee80211_chan2ieee(ic, ni->ni_chan); + 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); + 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); + switch (ic->ic_curmode) { + case IEEE80211_MODE_11A: + sc->rxon.cck_mask = 0; + sc->rxon.ofdm_mask = 0x15; + break; + case IEEE80211_MODE_11B: + sc->rxon.cck_mask = 0x03; + sc->rxon.ofdm_mask = 0; + break; + default: /* Assume 802.11b/g. */ + sc->rxon.cck_mask = 0x0f; + sc->rxon.ofdm_mask = 0x15; + } + DPRINTF(("rxon chan %d flags %x cck %x ofdm %x\n", sc->rxon.chan, + sc->rxon.flags, sc->rxon.cck_mask, sc->rxon.ofdm_mask)); + error = iwn_cmd(sc, IWN_CMD_CONFIGURE, &sc->rxon, hal->rxonsz, 1); if (error != 0) { - printf("%s: could not set power mode\n", sc->sc_dev.dv_xname); + printf("%s: could not configure\n", sc->sc_dev.dv_xname); return error; } - /* configure bluetooth coexistence */ - memset(&bluetooth, 0, sizeof bluetooth); - bluetooth.flags = 3; - bluetooth.lead = 0xaa; - bluetooth.kill = 1; - DPRINTF(("configuring bluetooth coexistence\n")); - error = iwn_cmd(sc, IWN_CMD_BLUETOOTH, &bluetooth, sizeof bluetooth, - 0); + /* Configuration has changed, set TX power accordingly. */ + if ((error = hal->set_txpower(sc, 1)) != 0) { + printf("%s: could not set TX power\n", sc->sc_dev.dv_xname); + return error; + } + + /* + * Reconfiguring clears the adapter's nodes table so we must add + * the broadcast node again. + */ + memset(&node, 0, sizeof node); + IEEE80211_ADDR_COPY(node.macaddr, etherbroadcastaddr); + node.id = hal->broadcast_id; + DPRINTF(("adding broadcast node\n")); + error = hal->add_node(sc, &node, 1); if (error != 0) { - printf("%s: could not configure bluetooth coexistence\n", + printf("%s: could not add broadcast node\n", sc->sc_dev.dv_xname); return error; } + DPRINTF(("setting fixed rate for node %d\n", node.id)); + rate = (ic->ic_curmode == IEEE80211_MODE_11A) ? 12 : 2; + if ((error = iwn_set_fixed_rate(sc, node.id, rate, 1)) != 0) { + printf("%s: could not setup MRR for broadcast node\n", + sc->sc_dev.dv_xname, node.id); + return error; + } + return 0; +} - /* configure adapter */ - memset(&sc->config, 0, sizeof (struct iwn_config)); - IEEE80211_ADDR_COPY(ic->ic_myaddr, LLADDR(ifp->if_sadl)); - IEEE80211_ADDR_COPY(sc->config.myaddr, ic->ic_myaddr); - IEEE80211_ADDR_COPY(sc->config.wlap, ic->ic_myaddr); - /* set default channel */ - sc->config.chan = ieee80211_chan2ieee(ic, ic->ic_ibss_chan); - sc->config.flags = htole32(IWN_CONFIG_TSF); - if (IEEE80211_IS_CHAN_2GHZ(ic->ic_ibss_chan)) { - sc->config.flags |= htole32(IWN_CONFIG_AUTO | - IWN_CONFIG_24GHZ); - } - sc->config.filter = 0; - switch (ic->ic_opmode) { - case IEEE80211_M_STA: - sc->config.mode = IWN_MODE_STA; - sc->config.filter |= htole32(IWN_FILTER_MULTICAST); - break; -#ifndef IEEE80211_STA_ONLY -#ifdef notyet - case IEEE80211_M_IBSS: - case IEEE80211_M_AHDEMO: - sc->config.mode = IWN_MODE_IBSS; - break; - case IEEE80211_M_HOSTAP: - sc->config.mode = IWN_MODE_HOSTAP; - break; -#endif -#endif - case IEEE80211_M_MONITOR: - sc->config.mode = IWN_MODE_MONITOR; - sc->config.filter |= htole32(IWN_FILTER_MULTICAST | - IWN_FILTER_CTL | IWN_FILTER_PROMISC); - break; - default: - /* should not get there */ - break; +int +iwn_run(struct iwn_softc *sc) +{ + const struct iwn_hal *hal = sc->sc_hal; + struct ieee80211com *ic = &sc->sc_ic; + struct ieee80211_node *ni = ic->ic_bss; + struct iwn_node_info node; + int error; + + if (ic->ic_opmode == IEEE80211_M_MONITOR) { + /* link LED blinks while monitoring */ + iwn_set_led(sc, IWN_LED_LINK, 5, 5); + return 0; } - sc->config.cck_mask = 0x0f; /* not yet negotiated */ - sc->config.ofdm_mask = 0xff; /* not yet negotiated */ - sc->config.ht_single_mask = 0xff; - sc->config.ht_dual_mask = 0xff; - sc->config.rxchain = htole16(0x2800 | 7 << IWN_RXCHAIN_ANTMSK_SHIFT); - DPRINTF(("setting configuration\n")); - error = iwn_cmd(sc, IWN_CMD_CONFIGURE, &sc->config, - sizeof (struct iwn_config), 0); + if ((error = iwn_set_timing(sc, ni)) != 0) { + printf("%s: could not set timing\n", sc->sc_dev.dv_xname); + return error; + } + + /* Update adapter's configuration. */ + 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); + 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); + sc->rxon.filter |= htole32(IWN_FILTER_BSS); + DPRINTF(("rxon chan %d flags %x\n", sc->rxon.chan, sc->rxon.flags)); + error = iwn_cmd(sc, IWN_CMD_CONFIGURE, &sc->rxon, hal->rxonsz, 1); if (error != 0) { - printf("%s: configure command failed\n", sc->sc_dev.dv_xname); + printf("%s: could not update configuration\n", + sc->sc_dev.dv_xname); return error; } - /* configuration has changed, set Tx power accordingly */ - if ((error = iwn_set_txpower(sc, ic->ic_ibss_chan, 0)) != 0) { - printf("%s: could not set Tx power\n", sc->sc_dev.dv_xname); + /* Configuration has changed, set TX power accordingly. */ + if ((error = hal->set_txpower(sc, 1)) != 0) { + printf("%s: could not set TX power\n", sc->sc_dev.dv_xname); return error; } - /* add broadcast node */ + /* Add BSS node. */ memset(&node, 0, sizeof node); - IEEE80211_ADDR_COPY(node.macaddr, etherbroadcastaddr); - node.id = IWN_ID_BROADCAST; - DPRINTF(("adding broadcast node\n")); - error = iwn_cmd(sc, IWN_CMD_ADD_NODE, &node, sizeof node, 0); + IEEE80211_ADDR_COPY(node.macaddr, ni->ni_macaddr); + node.id = IWN_ID_BSS; +#ifdef notyet + node.htflags = htole32(IWN_AMDPU_SIZE_FACTOR(3) | + IWN_AMDPU_DENSITY(5)); /* 2us */ +#endif + DPRINTF(("adding BSS node\n")); + error = hal->add_node(sc, &node, 1); if (error != 0) { - printf("%s: could not add broadcast node\n", - sc->sc_dev.dv_xname); + printf("%s: could not add BSS node\n", sc->sc_dev.dv_xname); return error; } - DPRINTF(("setting fixed rate for node %d\n", node.id)); - rate = (ic->ic_curmode == IEEE80211_MODE_11A) ? 12 : 2; - if ((error = iwn_set_fixed_rate(sc, node.id, rate, 0)) != 0) { + DPRINTF(("setting MRR for node %d\n", node.id)); + if ((error = iwn_setup_node_mrr(sc, ni, node.id)) != 0) { printf("%s: could not setup MRR for node %d\n", sc->sc_dev.dv_xname, node.id); return error; } - if ((error = iwn_set_critical_temp(sc)) != 0) { - printf("%s: could not set critical temperature\n", + if (ic->ic_opmode == IEEE80211_M_STA) { + /* Fake a join to initialize the TX rate. */ + iwn_newassoc(ic, ni, 1); + } + + if ((error = iwn_init_sensitivity(sc)) != 0) { + printf("%s: could not set sensitivity\n", sc->sc_dev.dv_xname); return error; } + /* Start periodic calibration timer. */ + sc->calib.state = IWN_CALIB_STATE_ASSOC; + sc->calib_cnt = 0; + timeout_add(&sc->calib_to, hz / 2); + /* Link LED always on while associated. */ + iwn_set_led(sc, IWN_LED_LINK, 0, 1); + return 0; +} + +int +iwn4965_addba(struct iwn_softc *sc, struct ieee80211_node *ni, int qid, + int tid, int ssn) +{ + const struct iwn_hal *hal = sc->sc_hal; + struct iwn_node *wn = (void *)ni; + struct iwn_node_info node; + uint32_t tmp; + int error; + + /* Enable TID for RA. */ + wn->disable_tid &= ~(1 << tid); + memset(&node, 0, sizeof node); + node.control = IWN_NODE_UPDATE; + node.flags = IWN_FLAG_SET_DISABLE_TID; + node.disable_tid = htole16(wn->disable_tid); + if ((error = hal->add_node(sc, &node, 1)) != 0) + return error; + + if ((error = iwn_nic_lock(sc)) != 0) + return error; + + /* 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 mode for the queue. */ + tmp = iwn_prph_read(sc, IWN4965_SCHED_QCHAIN_SEL); + iwn_prph_write(sc, IWN4965_SCHED_QCHAIN_SEL, tmp | 1 << qid); + + /* Set starting sequence number from the ADDBA request. */ + IWN_WRITE(sc, IWN_HBUS_TARG_WRPTR, ssn); + 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. */ + tmp = iwn_prph_read(sc, IWN4965_SCHED_INTR_MASK); + iwn_prph_write(sc, IWN4965_SCHED_INTR_MASK, tmp | 1 << qid); + + /* Mark the queue as active. */ + iwn_prph_write(sc, IWN5000_SCHED_QUEUE_STATUS(qid), + IWN4965_TXQ_STATUS_ACTIVE | IWN4965_TXQ_STATUS_AGGR_ENA | + iwn_tid2fifo[tid] << 1); + + iwn_nic_unlock(sc); + return 0; +} + +int +iwn4965_delba(struct iwn_softc *sc, int qid, int tid, int ssn) +{ + uint32_t tmp; + int error; + + if ((error = iwn_nic_lock(sc)) != 0) + return error; + + /* 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, ssn); + iwn_prph_write(sc, IWN4965_SCHED_QUEUE_RDPTR(qid), ssn); + + /* Disable interrupts for the queue. */ + tmp = iwn_prph_read(sc, IWN4965_SCHED_INTR_MASK); + iwn_prph_write(sc, IWN4965_SCHED_INTR_MASK, tmp & ~(1 << qid)); + + /* Mark the queue as inactive. */ + iwn_prph_write(sc, IWN4965_SCHED_QUEUE_STATUS(qid), + IWN4965_TXQ_STATUS_INACTIVE | iwn_tid2fifo[tid] << 1); + + iwn_nic_unlock(sc); + return 0; +} + +int +iwn5000_addba(struct iwn_softc *sc, struct ieee80211_node *ni, int qid, + int tid, int ssn) +{ + const struct iwn_hal *hal = sc->sc_hal; + struct iwn_node *wn = (void *)ni; + struct iwn_node_info node; + uint32_t tmp; + int error; + + /* Enable TID for RA. */ + wn->disable_tid &= ~(1 << tid); + memset(&node, 0, sizeof node); + node.control = IWN_NODE_UPDATE; + node.flags = IWN_FLAG_SET_DISABLE_TID; + node.disable_tid = htole16(wn->disable_tid); + if ((error = hal->add_node(sc, &node, 1)) != 0) + return error; + + if ((error = iwn_nic_lock(sc)) != 0) + return error; + + /* 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 mode for the queue. */ + tmp = iwn_prph_read(sc, IWN5000_SCHED_QCHAIN_SEL); + iwn_prph_write(sc, IWN5000_SCHED_QCHAIN_SEL, tmp | 1 << qid); + + /* Enable aggregation for the queue. */ + tmp = iwn_prph_read(sc, IWN5000_SCHED_AGGR_SEL); + iwn_prph_write(sc, IWN5000_SCHED_AGGR_SEL, tmp | 1 << qid); + + /* Set starting sequence number from the ADDBA request. */ + IWN_WRITE(sc, IWN_HBUS_TARG_WRPTR, ssn); + 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. */ + tmp = iwn_prph_read(sc, IWN5000_SCHED_INTR_MASK); + iwn_prph_write(sc, IWN5000_SCHED_INTR_MASK, tmp | 1 << qid); + + /* Mark the queue as active. */ + iwn_prph_write(sc, IWN5000_SCHED_QUEUE_STATUS(qid), + IWN5000_TXQ_STATUS_ACTIVE | iwn_tid2fifo[tid]); + + iwn_nic_unlock(sc); + return 0; +} + +int +iwn5000_delba(struct iwn_softc *sc, int qid, int tid, int ssn) +{ + uint32_t tmp; + int error; + + if ((error = iwn_nic_lock(sc)) != 0) + return error; + + /* 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. */ + tmp = iwn_prph_read(sc, IWN5000_SCHED_AGGR_SEL); + iwn_prph_write(sc, IWN5000_SCHED_AGGR_SEL, tmp & ~(1 << qid)); + + /* Set starting sequence number from the ADDBA request. */ + IWN_WRITE(sc, IWN_HBUS_TARG_WRPTR, ssn); + iwn_prph_write(sc, IWN5000_SCHED_QUEUE_RDPTR(qid), ssn); + + /* Disable interrupts for the queue. */ + tmp = iwn_prph_read(sc, IWN5000_SCHED_INTR_MASK); + iwn_prph_write(sc, IWN5000_SCHED_INTR_MASK, tmp & ~(1 << qid)); + + /* Mark the queue as inactive. */ + iwn_prph_write(sc, IWN5000_SCHED_QUEUE_STATUS(qid), + IWN5000_TXQ_STATUS_INACTIVE | iwn_tid2fifo[tid]); + + iwn_nic_unlock(sc); return 0; } /* - * Do post-alive initialization of the NIC (after firmware upload). + * Query calibration tables from the initialization firmware. We do this + * only once at first boot. Called from a process context. */ -void -iwn_post_alive(struct iwn_softc *sc) +int +iwn5000_query_calibration(struct iwn_softc *sc) { - uint32_t base; - uint16_t offset; - int qid; + struct iwn5000_calib_config cmd; + int error; + + memset(&cmd, 0, sizeof cmd); + cmd.ucode.once.enable = 0xffffffff; + cmd.ucode.once.start = 0xffffffff; + cmd.ucode.once.send = 0xffffffff; + cmd.ucode.flags = 0xffffffff; + DPRINTF(("sending calibration query\n")); + error = iwn_cmd(sc, IWN5000_CMD_CALIB_CONFIG, &cmd, sizeof cmd, 0); + if (error != 0) + return error; - iwn_mem_lock(sc); + /* Wait at most two seconds for calibration to complete. */ + return tsleep(sc, PCATCH, "iwncal", 2 * hz); +} - /* clear SRAM */ - base = iwn_mem_read(sc, IWN_SRAM_BASE); - for (offset = 0x380; offset < 0x520; offset += 4) { - IWN_WRITE(sc, IWN_MEM_WADDR, base + offset); - IWN_WRITE(sc, IWN_MEM_WDATA, 0); +/* + * Send calibration results to the runtime firmware. These results were + * obtained on first boot from the initialization firmware. + */ +int +iwn5000_send_calibration(struct iwn_softc *sc) +{ + int idx, error; + + for (idx = 0; idx < 3; idx++) { + if (sc->calibcmd[idx].buf == NULL) + continue; /* No results available. */ + DPRINTF(("send calibration result idx=%d len=%d\n", + idx, sc->calibcmd[idx].len)); + error = iwn_cmd(sc, IWN_PHY_CALIB, sc->calibcmd[idx].buf, + sc->calibcmd[idx].len, 0); + if (error != 0) { + printf("%s: could not send calibration result\n", + sc->sc_dev.dv_xname); + return error; + } } + return 0; +} - /* shared area is aligned on a 1K boundary */ - iwn_mem_write(sc, IWN_SRAM_BASE, sc->shared_dma.paddr >> 10); - iwn_mem_write(sc, IWN_SELECT_QCHAIN, 0); +/* + * This function is called after the runtime firmware notifies us of its + * readiness (called in a process context). + */ +int +iwn4965_post_alive(struct iwn_softc *sc) +{ + int error, qid; + + if ((error = iwn_nic_lock(sc)) != 0) + return error; + + /* Clear TX scheduler's 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); + + /* Set physical address of TX scheduler rings (1KB aligned.) */ + iwn_prph_write(sc, IWN4965_SCHED_DRAM_ADDR, sc->sched_dma.paddr >> 10); + /* Disable chain mode for all our 16 queues. */ + iwn_prph_write(sc, IWN4965_SCHED_QCHAIN_SEL, 0); + + for (qid = 0; qid < IWN4965_NTXQUEUES; qid++) { + iwn_prph_write(sc, IWN4965_SCHED_QUEUE_RDPTR(qid), 0); + IWN_WRITE(sc, IWN_HBUS_TARG_WRPTR, qid << 8 | 0); - for (qid = 0; qid < IWN_NTXQUEUES; qid++) { - iwn_mem_write(sc, IWN_QUEUE_RIDX(qid), 0); - IWN_WRITE(sc, IWN_TX_WIDX, qid << 8 | 0); + /* 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 all our 16 queues. */ + iwn_prph_write(sc, IWN4965_SCHED_INTR_MASK, 0xffff); + /* Identify TX FIFO rings (0-7). */ + iwn_prph_write(sc, IWN4965_SCHED_TXFACT, 0xff); - /* set sched. window size */ - IWN_WRITE(sc, IWN_MEM_WADDR, base + IWN_QUEUE_OFFSET(qid)); - IWN_WRITE(sc, IWN_MEM_WDATA, 64); - /* set sched. frame limit */ - IWN_WRITE(sc, IWN_MEM_WADDR, base + IWN_QUEUE_OFFSET(qid) + 4); - IWN_WRITE(sc, IWN_MEM_WDATA, 64 << 16); + /* Mark TX rings (4 EDCA + cmd + 2 HCCA) as active. */ + for (qid = 0; qid < 7; qid++) { + static uint8_t qid2fifo[] = { 3, 2, 1, 0, 4, 5, 6 }; + iwn_prph_write(sc, IWN4965_SCHED_QUEUE_STATUS(qid), + IWN4965_TXQ_STATUS_ACTIVE | qid2fifo[qid] << 1); } + iwn_nic_unlock(sc); + return 0; +} + +/* + * This function is called after the initialization or runtime firmware + * notifies us of its readiness (called in a process context). + */ +int +iwn5000_post_alive(struct iwn_softc *sc) +{ + struct iwn5000_wimax_coex wimax; + int error, qid; + + if ((error = iwn_nic_lock(sc)) != 0) + return error; + + /* Clear TX scheduler's 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); + + /* Set physical address of TX scheduler rings (1KB aligned.) */ + iwn_prph_write(sc, IWN5000_SCHED_DRAM_ADDR, sc->sched_dma.paddr >> 10); + /* Enable chain mode for all our 20 queues. */ + iwn_prph_write(sc, IWN5000_SCHED_QCHAIN_SEL, 0xfffff); + iwn_prph_write(sc, IWN5000_SCHED_AGGR_SEL, 0); - /* enable interrupts for all 16 queues */ - iwn_mem_write(sc, IWN_QUEUE_INTR_MASK, 0xffff); + for (qid = 0; qid < IWN5000_NTXQUEUES; qid++) { + iwn_prph_write(sc, IWN5000_SCHED_QUEUE_RDPTR(qid), 0); + IWN_WRITE(sc, IWN_HBUS_TARG_WRPTR, qid << 8 | 0); - /* identify active Tx rings (0-7) */ - iwn_mem_write(sc, IWN_TX_ACTIVE, 0xff); + iwn_mem_write(sc, sc->sched_base + + IWN5000_SCHED_QUEUE_OFFSET(qid), 0); + /* 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 all our 20 queues. */ + iwn_prph_write(sc, IWN5000_SCHED_INTR_MASK, 0xfffff); + /* Identify TX FIFO rings (0-7). */ + iwn_prph_write(sc, IWN5000_SCHED_TXFACT, 0xff); - /* mark Tx rings (4 EDCA + cmd + 2 HCCA) as active */ + /* Mark TX rings (4 EDCA + cmd + 2 HCCA) as active. */ for (qid = 0; qid < 7; qid++) { - iwn_mem_write(sc, IWN_TXQ_STATUS(qid), - IWN_TXQ_STATUS_ACTIVE | qid << 1); + static uint8_t qid2fifo[] = { 3, 2, 1, 0, 7, 5, 6 }; + iwn_prph_write(sc, IWN5000_SCHED_QUEUE_STATUS(qid), + IWN5000_TXQ_STATUS_ACTIVE | qid2fifo[qid] << 0); } + iwn_nic_unlock(sc); - iwn_mem_unlock(sc); + /* Configure WiMax (IEEE 802.16) coexistence. */ + memset(&wimax, 0, sizeof wimax); + DPRINTF(("Configuring WiMax coexistence\n")); + error = iwn_cmd(sc, IWN5000_CMD_WIMAX_COEX, &wimax, sizeof wimax, 0); + if (error != 0) { + printf("%s: could not configure WiMax coexistence\n", + sc->sc_dev.dv_xname); + return error; + } + + if (sc->hw_type != IWN_HW_REV_TYPE_5150) { + struct iwn5000_phy_calib calib; + + /* Perform crystal calibration. */ + memset(&calib, 0, sizeof calib); + calib.code = IWN5000_PHY_CALIB_CRYSTAL; + calib.data[0] = letoh32(sc->eeprom_crystal) & 0xff; + calib.data[1] = (letoh32(sc->eeprom_crystal) >> 16) & 0xff; + DPRINTF(("sending crystal calibration %d, %d\n", + calib.data[0], calib.data[1])); + error = iwn_cmd(sc, IWN_PHY_CALIB, &calib, sizeof calib, 0); + if (error != 0) { + printf("%s: crystal calibration failed\n", + sc->sc_dev.dv_xname); + return error; + } + } + if (sc->sc_flags & IWN_FLAG_FIRST_BOOT) { + /* Query calibration from the initialization firmware. */ + if ((error = iwn5000_query_calibration(sc)) != 0) { + printf("%s: could not query calibration\n", + sc->sc_dev.dv_xname); + return error; + } + /* + * We have the calibration results now so we can skip + * loading the initialization firmware next time. + */ + sc->sc_flags &= ~IWN_FLAG_FIRST_BOOT; + + /* Reboot (call ourselves recursively!) */ + iwn_hw_stop(sc); + error = iwn_hw_init(sc); + } else { + /* Send calibration results to runtime firmware. */ + error = iwn5000_send_calibration(sc); + } + return error; } -void -iwn_stop_master(struct iwn_softc *sc) +/* + * The firmware boot code is small and is intended to be copied directly into + * the NIC internal memory (no DMA transfer.) + */ +int +iwn4965_load_bootcode(struct iwn_softc *sc, const uint8_t *ucode, int size) { - uint32_t tmp; - int ntries; + int error, ntries; - tmp = IWN_READ(sc, IWN_RESET); - IWN_WRITE(sc, IWN_RESET, tmp | IWN_STOP_MASTER); + size /= sizeof (uint32_t); - tmp = IWN_READ(sc, IWN_GPIO_CTL); - if ((tmp & IWN_GPIO_PWR_STATUS) == IWN_GPIO_PWR_SLEEP) - return; /* already asleep */ + if ((error = iwn_nic_lock(sc)) != 0) + return error; - for (ntries = 0; ntries < 100; ntries++) { - if (IWN_READ(sc, IWN_RESET) & IWN_MASTER_DISABLED) + /* Copy microcode image into NIC memory. */ + iwn_prph_write_region_4(sc, IWN_BSM_SRAM_BASE, + (const uint32_t *)ucode, size); + + iwn_prph_write(sc, IWN_BSM_WR_MEM_SRC, 0); + iwn_prph_write(sc, IWN_BSM_WR_MEM_DST, IWN_FW_TEXT_BASE); + iwn_prph_write(sc, IWN_BSM_WR_DWCOUNT, size); + + /* Start boot load now. */ + iwn_prph_write(sc, IWN_BSM_WR_CTRL, IWN_BSM_WR_CTRL_START); + + /* Wait for transfer to complete. */ + for (ntries = 0; ntries < 1000; ntries++) { + if (!(iwn_prph_read(sc, IWN_BSM_WR_CTRL) & + IWN_BSM_WR_CTRL_START)) break; DELAY(10); } - if (ntries == 100) { - printf("%s: timeout waiting for master\n", + if (ntries == 1000) { + printf("%s: could not load boot firmware\n", sc->sc_dev.dv_xname); + iwn_nic_unlock(sc); + return ETIMEDOUT; } + + /* Enable boot after power up. */ + iwn_prph_write(sc, IWN_BSM_WR_CTRL, IWN_BSM_WR_CTRL_START_EN); + + iwn_nic_unlock(sc); + return 0; } int -iwn_reset(struct iwn_softc *sc) +iwn4965_load_firmware(struct iwn_softc *sc) { - uint32_t tmp; - int ntries; + struct iwn_fw_info *fw = &sc->fw; + struct iwn_dma_info *dma = &sc->fw_dma; + int error; + + /* Copy initialization images into pre-allocated DMA-safe memory. */ + memcpy(dma->vaddr, fw->init.data, fw->init.datasz); + memcpy(dma->vaddr + IWN4965_FW_DATA_MAXSZ, + fw->init.text, fw->init.textsz); + + /* Tell adapter where to find initialization images. */ + if ((error = iwn_nic_lock(sc)) != 0) + return error; + iwn_prph_write(sc, IWN_BSM_DRAM_DATA_ADDR, dma->paddr >> 4); + iwn_prph_write(sc, IWN_BSM_DRAM_DATA_SIZE, fw->init.datasz); + iwn_prph_write(sc, IWN_BSM_DRAM_TEXT_ADDR, + (dma->paddr + IWN4965_FW_DATA_MAXSZ) >> 4); + iwn_prph_write(sc, IWN_BSM_DRAM_TEXT_SIZE, fw->init.textsz); + iwn_nic_unlock(sc); + + /* Load firmware boot code. */ + error = iwn4965_load_bootcode(sc, fw->boot.text, fw->boot.textsz); + if (error != 0) { + printf("%s: could not load boot firmware\n", + sc->sc_dev.dv_xname); + return error; + } + /* Now press "execute". */ + IWN_WRITE(sc, IWN_RESET, 0); + + /* Wait at most one second for first alive notification. */ + if ((error = tsleep(sc, PCATCH, "iwninit", hz)) != 0) { + printf("%s: timeout waiting for adapter to initialize\n", + sc->sc_dev.dv_xname); + return error; + } + + /* Retrieve current temperature for initial TX power calibration. */ + sc->rawtemp = sc->ucode_info.temp[3].chan20MHz; + sc->temp = iwn4965_get_temperature(sc); + + /* Copy runtime sections into pre-allocated DMA-safe memory. */ + memcpy(dma->vaddr, fw->main.data, fw->main.datasz); + memcpy(dma->vaddr + IWN4965_FW_DATA_MAXSZ, + fw->main.text, fw->main.textsz); + + /* Tell adapter where to find runtime sections. */ + if ((error = iwn_nic_lock(sc)) != 0) + return error; + iwn_prph_write(sc, IWN_BSM_DRAM_DATA_ADDR, dma->paddr >> 4); + iwn_prph_write(sc, IWN_BSM_DRAM_DATA_SIZE, fw->main.datasz); + iwn_prph_write(sc, IWN_BSM_DRAM_TEXT_ADDR, + (dma->paddr + IWN4965_FW_DATA_MAXSZ) >> 4); + iwn_prph_write(sc, IWN_BSM_DRAM_TEXT_SIZE, + IWN_FW_UPDATED | fw->main.textsz); + iwn_nic_unlock(sc); + + return 0; +} + +/* + * Load a firmware text or data section through Flow Handler DMA. + */ +int +iwn5000_load_firmware_section(struct iwn_softc *sc, uint32_t dst, + const uint8_t *section, int size) +{ + struct iwn_dma_info *dma = &sc->fw_dma; + int error; + + /* Copy firmware section into pre-allocated DMA-safe memory. */ + memcpy(dma->vaddr, section, size); + + if ((error = iwn_nic_lock(sc)) != 0) + return error; + + IWN_WRITE(sc, IWN_FH_TX_CONFIG(IWN_SRVC_CHNL), + IWN_FH_TX_CONFIG_DMA_PAUSE); + + IWN_WRITE(sc, IWN_FH_SRAM_ADDR(IWN_SRVC_CHNL), dst); + IWN_WRITE(sc, IWN_FH_TFBD_CTRL0(IWN_SRVC_CHNL), dma->paddr); + IWN_WRITE(sc, IWN_FH_TFBD_CTRL1(IWN_SRVC_CHNL), size); + IWN_WRITE(sc, IWN_FH_TXBUF_STATUS(IWN_SRVC_CHNL), + IWN_FH_TXBUF_STATUS_TBNUM(1) | + IWN_FH_TXBUF_STATUS_TBIDX(1) | + IWN_FH_TXBUF_STATUS_TFBD_VALID); + + /* Kick Flow Handler to start DMA transfer. */ + IWN_WRITE(sc, IWN_FH_TX_CONFIG(IWN_SRVC_CHNL), + IWN_FH_TX_CONFIG_DMA_ENA | IWN_FH_TX_CONFIG_CIRQ_HOST_ENDTFD); + + iwn_nic_unlock(sc); + + /* Wait at most five seconds for FH DMA transfer to complete. */ + return tsleep(sc, PCATCH, "iwninit", 5 * hz); +} + +int +iwn5000_load_firmware(struct iwn_softc *sc) +{ + struct iwn_fw_part *fw; + int error; + + /* Load the initialization firmware on first boot only. */ + fw = (sc->sc_flags & IWN_FLAG_FIRST_BOOT) ? + &sc->fw.init : &sc->fw.main; + + error = iwn5000_load_firmware_section(sc, IWN_FW_TEXT_BASE, + fw->text, fw->textsz); + if (error != 0) { + printf("%s: could not load firmware %s section\n", + sc->sc_dev.dv_xname, ".text"); + return error; + } + + error = iwn5000_load_firmware_section(sc, IWN_FW_DATA_BASE, + fw->data, fw->datasz); + if (error != 0) { + printf("%s: could not load firmware %s section\n", + sc->sc_dev.dv_xname, ".data"); + return error; + } + + /* Now press "execute". */ + IWN_WRITE(sc, IWN_RESET, 0); + return 0; +} + +int +iwn_read_firmware(struct iwn_softc *sc) +{ + const struct iwn_hal *hal = sc->sc_hal; + struct iwn_fw_info *fw = &sc->fw; + const struct iwn_firmware_hdr *hdr; + size_t size; + int error; + + /* Read firmware image from filesystem. */ + if ((error = loadfirmware(hal->fwname, &fw->data, &size)) != 0) { + printf("%s: error, %d, could not read firmware %s\n", + sc->sc_dev.dv_xname, error, hal->fwname); + return error; + } + if (size < sizeof (*hdr)) { + printf("%s: truncated firmware header: %d bytes\n", + sc->sc_dev.dv_xname, size); + free(fw->data, M_DEVBUF); + return EINVAL; + } + /* Extract firmware header information. */ + hdr = (struct iwn_firmware_hdr *)fw->data; + fw->main.textsz = letoh32(hdr->main_textsz); + fw->main.datasz = letoh32(hdr->main_datasz); + fw->init.textsz = letoh32(hdr->init_textsz); + fw->init.datasz = letoh32(hdr->init_datasz); + fw->boot.textsz = letoh32(hdr->boot_textsz); + fw->boot.datasz = 0; + + /* Sanity-check firmware header. */ + if (fw->main.textsz > hal->fw_text_maxsz || + fw->main.datasz > hal->fw_data_maxsz || + fw->init.textsz > hal->fw_text_maxsz || + fw->init.datasz > hal->fw_data_maxsz || + fw->boot.textsz > IWN_FW_BOOT_TEXT_MAXSZ || + (fw->boot.textsz & 3) != 0) { + printf("%s: invalid firmware header\n", sc->sc_dev.dv_xname); + free(fw->data, M_DEVBUF); + return EINVAL; + } + + /* 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) { + printf("%s: firmware file too short: %d bytes\n", + sc->sc_dev.dv_xname, size); + free(fw->data, M_DEVBUF); + return EINVAL; + } + + /* get pointers to firmware sections */ + fw->main.text = (const uint8_t *)(hdr + 1); + 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; + fw->boot.text = fw->init.data + fw->init.datasz; - /* clear any pending interrupts */ - IWN_WRITE(sc, IWN_INTR, 0xffffffff); + return 0; +} - tmp = IWN_READ(sc, IWN_CHICKEN); - IWN_WRITE(sc, IWN_CHICKEN, tmp | IWN_CHICKEN_DISLOS); +int +iwn_clock_wait(struct iwn_softc *sc) +{ + int ntries; - tmp = IWN_READ(sc, IWN_GPIO_CTL); - IWN_WRITE(sc, IWN_GPIO_CTL, tmp | IWN_GPIO_INIT); + /* Set "initialization complete" bit. */ + IWN_SETBITS(sc, IWN_GP_CNTRL, IWN_GP_CNTRL_INIT_DONE); - /* wait for clock stabilization */ + /* Wait for clock stabilization. */ for (ntries = 0; ntries < 25000; ntries++) { - if (IWN_READ(sc, IWN_GPIO_CTL) & IWN_GPIO_CLOCK) - break; + if (IWN_READ(sc, IWN_GP_CNTRL) & IWN_GP_CNTRL_MAC_CLOCK_READY) + return 0; DELAY(100); } - if (ntries == 25000) { - printf("%s: timeout waiting for clock stabilization\n", - sc->sc_dev.dv_xname); - return ETIMEDOUT; - } + printf("%s: timeout waiting for clock stabilization\n", + sc->sc_dev.dv_xname); + return ETIMEDOUT; +} + +int +iwn4965_apm_init(struct iwn_softc *sc) +{ + uint32_t tmp; + 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); + + if ((error = iwn_clock_wait(sc)) != 0) + return error; + + if ((error = iwn_nic_lock(sc)) != 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. */ + tmp = iwn_prph_read(sc, IWN_APMG_PCI_STT); + iwn_prph_write(sc, IWN_APMG_PCI_STT, tmp | IWN_APMG_PCI_STT_L1A_DIS); + iwn_nic_unlock(sc); + return 0; } -void -iwn_hw_config(struct iwn_softc *sc) +int +iwn5000_apm_init(struct iwn_softc *sc) { - uint32_t tmp, hw; + uint32_t tmp; + 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); - /* enable interrupts mitigation */ - IWN_WRITE(sc, IWN_INTR_MIT, 512 / 32); + /* Set Flow Handler wait threshold to the maximum. */ + IWN_SETBITS(sc, IWN_DBG_HPET_MEM, 0xffff0000); + + /* Enable HAP to move adapter from L1a to L0s. */ + IWN_SETBITS(sc, IWN_HW_IF_CONFIG, IWN_HW_IF_CONFIG_HAP_WAKE_L1A); + IWN_SETBITS(sc, IWN_ANA_PLL, IWN_ANA_PLL_INIT); + + if ((error = iwn_clock_wait(sc)) != 0) + return error; + + if ((error = iwn_nic_lock(sc)) != 0) + return error; + /* Enable DMA. */ + iwn_prph_write(sc, IWN_APMG_CLK_CTRL, IWN_APMG_CLK_CTRL_DMA_CLK_RQT); + DELAY(20); + /* Disable L1. */ + tmp = iwn_prph_read(sc, IWN_APMG_PCI_STT); + iwn_prph_write(sc, IWN_APMG_PCI_STT, tmp | IWN_APMG_PCI_STT_L1A_DIS); + iwn_nic_unlock(sc); - /* voodoo from the reference driver */ - tmp = pci_conf_read(sc->sc_pct, sc->sc_pcitag, PCI_CLASS_REG); - tmp = PCI_REVISION(tmp); - if ((tmp & 0x80) && (tmp & 0x7f) < 8) { - /* enable "no snoop" field */ - tmp = pci_conf_read(sc->sc_pct, sc->sc_pcitag, 0xe8); - tmp &= ~IWN_DIS_NOSNOOP; - pci_conf_write(sc->sc_pct, sc->sc_pcitag, 0xe8, tmp); + return 0; +} + +void +iwn_apm_stop_master(struct iwn_softc *sc) +{ + int ntries; + + 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) + return; + DELAY(10); } + printf("%s: timeout waiting for master\n", sc->sc_dev.dv_xname); +} - /* disable L1 entry to work around a hardware bug */ - tmp = pci_conf_read(sc->sc_pct, sc->sc_pcitag, 0xf0); - tmp &= ~IWN_ENA_L1; - pci_conf_write(sc->sc_pct, sc->sc_pcitag, 0xf0, tmp); +void +iwn_apm_stop(struct iwn_softc *sc) +{ + iwn_apm_stop_master(sc); - hw = IWN_READ(sc, IWN_HWCONFIG); - IWN_WRITE(sc, IWN_HWCONFIG, hw | 0x310); + IWN_SETBITS(sc, IWN_RESET, IWN_RESET_SW); + DELAY(10); + /* Clear "initialization complete" bit. */ + IWN_CLRBITS(sc, IWN_GP_CNTRL, IWN_GP_CNTRL_INIT_DONE); +} - iwn_mem_lock(sc); - tmp = iwn_mem_read(sc, IWN_MEM_POWER); - iwn_mem_write(sc, IWN_MEM_POWER, tmp | IWN_POWER_RESET); - DELAY(5); - tmp = iwn_mem_read(sc, IWN_MEM_POWER); - iwn_mem_write(sc, IWN_MEM_POWER, tmp & ~IWN_POWER_RESET); - iwn_mem_unlock(sc); +int +iwn4965_nic_config(struct iwn_softc *sc) +{ + pcireg_t reg; + uint8_t rev; + + reg = pci_conf_read(sc->sc_pct, sc->sc_pcitag, PCI_CLASS_REG); + rev = PCI_REVISION(reg); + if ((rev & 0x80) && (rev & 0x7f) < 8) { + reg = pci_conf_read(sc->sc_pct, sc->sc_pcitag, + sc->sc_cap_off + PCI_PCIE_DCSR); + /* Clear PCIe "Enable No Snoop" bit. */ + reg &= ~PCI_PCIE_DCSR_ENA_NO_SNOOP; + pci_conf_write(sc->sc_pct, sc->sc_pcitag, + sc->sc_cap_off + PCI_PCIE_DCSR, reg); + } + + /* Retrieve PCIe Active State Power Management (ASPM). */ + reg = pci_conf_read(sc->sc_pct, sc->sc_pcitag, + sc->sc_cap_off + PCI_PCIE_LCSR); + if (reg & PCI_PCIE_LCSR_ASPM_L1) /* 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 + * vendor driver is doing. Probably the bits should not be + * shifted in IWN_RFCFG_*. + */ + IWN_SETBITS(sc, IWN_HW_IF_CONFIG, + IWN_RFCFG_TYPE(sc->rfcfg) | + IWN_RFCFG_STEP(sc->rfcfg) | + IWN_RFCFG_DASH(sc->rfcfg)); + } + IWN_SETBITS(sc, IWN_HW_IF_CONFIG, + IWN_HW_IF_CONFIG_RADIO_SI | IWN_HW_IF_CONFIG_MAC_SI); + return 0; } int -iwn_init(struct ifnet *ifp) +iwn5000_nic_config(struct iwn_softc *sc) { - struct iwn_softc *sc = ifp->if_softc; - struct ieee80211com *ic = &sc->sc_ic; + uint32_t tmp; + pcireg_t reg; + int error; + + /* Retrieve Active State Power Management (ASPM). */ + reg = pci_conf_read(sc->sc_pct, sc->sc_pcitag, + sc->sc_cap_off + PCI_PCIE_LCSR); + if (reg & PCI_PCIE_LCSR_ASPM_L1) /* 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) | + IWN_RFCFG_STEP(sc->rfcfg) | + IWN_RFCFG_DASH(sc->rfcfg)); + } + IWN_SETBITS(sc, IWN_HW_IF_CONFIG, + IWN_HW_IF_CONFIG_RADIO_SI | IWN_HW_IF_CONFIG_MAC_SI); + + if ((error = iwn_nic_lock(sc)) != 0) + return error; + tmp = iwn_prph_read(sc, IWN_APMG_PS); + iwn_prph_write(sc, IWN_APMG_PS, tmp | IWN_APMG_PS_EARLY_PWROFF_DIS); + iwn_nic_unlock(sc); + return 0; +} + +int +iwn_hw_init(struct iwn_softc *sc) +{ + const struct iwn_hal *hal = sc->sc_hal; uint32_t tmp; int error, qid; - if ((error = iwn_reset(sc)) != 0) { - printf("%s: could not reset adapter\n", sc->sc_dev.dv_xname); - goto fail1; + /* Clear any pending interrupts. */ + IWN_WRITE(sc, IWN_INT, 0xffffffff); + + if ((error = hal->apm_init(sc)) != 0) { + printf("%s: could not power ON adapter\n", + sc->sc_dev.dv_xname); + return error; } - iwn_mem_lock(sc); - iwn_mem_read(sc, IWN_CLOCK_CTL); - iwn_mem_write(sc, IWN_CLOCK_CTL, 0xa00); - iwn_mem_read(sc, IWN_CLOCK_CTL); - iwn_mem_unlock(sc); + /* Select VMAIN power source. */ + if ((error = iwn_nic_lock(sc)) != 0) + return error; + tmp = iwn_prph_read(sc, IWN_APMG_PS); + tmp &= ~IWN_APMG_PS_PWR_SRC_MASK; + tmp |= IWN_APMG_PS_PWR_SRC(IWN_APMG_PS_PWR_SRC_VMAIN); + iwn_prph_write(sc, IWN_APMG_PS, tmp); + iwn_nic_unlock(sc); + + /* Perform adapter-specific initialization. */ + if ((error = hal->nic_config(sc)) != 0) + return error; - DELAY(20); + /* Initialize RX ring. */ + if ((error = iwn_nic_lock(sc)) != 0) + return error; + IWN_WRITE(sc, IWN_FH_RX_CONFIG, 0); + IWN_WRITE(sc, IWN_FH_RX_WPTR, 0); + /* Set physical address of RX ring (256-byte aligned.) */ + IWN_WRITE(sc, IWN_FH_RX_BASE, sc->rxq.desc_dma.paddr >> 8); + /* Set physical address of RX status (16-byte aligned.) */ + IWN_WRITE(sc, IWN_FH_STATUS_WPTR, sc->rxq.stat_dma.paddr >> 4); + /* Enable RX. */ + IWN_WRITE(sc, IWN_FH_RX_CONFIG, + IWN_FH_RX_CONFIG_ENA | + IWN_FH_RX_CONFIG_IGN_RXF_EMPTY | /* HW bug workaround */ + IWN_FH_RX_CONFIG_IRQ_DST_HOST | + IWN_FH_RX_CONFIG_RB_TIMEOUT(0) | + IWN_FH_RX_CONFIG_NRBD(IWN_RX_RING_COUNT_LOG)); + iwn_nic_unlock(sc); + IWN_WRITE(sc, IWN_FH_RX_WPTR, (IWN_RX_RING_COUNT - 1) & ~7); + + if ((error = iwn_nic_lock(sc)) != 0) + return error; - iwn_mem_lock(sc); - tmp = iwn_mem_read(sc, IWN_MEM_PCIDEV); - iwn_mem_write(sc, IWN_MEM_PCIDEV, tmp | 0x800); - iwn_mem_unlock(sc); + /* Initialize TX scheduler. */ + iwn_prph_write(sc, hal->sched_txfact_addr, 0); - iwn_mem_lock(sc); - tmp = iwn_mem_read(sc, IWN_MEM_POWER); - iwn_mem_write(sc, IWN_MEM_POWER, tmp & ~0x03000000); - iwn_mem_unlock(sc); + /* Set physical address of "keep warm" page (16-byte aligned.) */ + IWN_WRITE(sc, IWN_FH_KW_ADDR, sc->kw_dma.paddr >> 4); - iwn_hw_config(sc); + /* Initialize TX rings. */ + for (qid = 0; qid < hal->ntxqs; qid++) { + struct iwn_tx_ring *txq = &sc->txq[qid]; - /* init Rx ring */ - iwn_mem_lock(sc); - IWN_WRITE(sc, IWN_RX_CONFIG, 0); - IWN_WRITE(sc, IWN_RX_WIDX, 0); - /* Rx ring is aligned on a 256-byte boundary */ - IWN_WRITE(sc, IWN_RX_BASE, sc->rxq.desc_dma.paddr >> 8); - /* shared area is aligned on a 16-byte boundary */ - IWN_WRITE(sc, IWN_RW_WIDX_PTR, (sc->shared_dma.paddr + - offsetof(struct iwn_shared, closed_count)) >> 4); - IWN_WRITE(sc, IWN_RX_CONFIG, 0x80601000); - iwn_mem_unlock(sc); + /* Set physical address of TX ring (256-byte aligned.) */ + IWN_WRITE(sc, IWN_FH_CBBC_QUEUE(qid), + txq->desc_dma.paddr >> 8); + /* Enable TX for this ring. */ + IWN_WRITE(sc, IWN_FH_TX_CONFIG(qid), + IWN_FH_TX_CONFIG_DMA_ENA | + IWN_FH_TX_CONFIG_DMA_CREDIT_ENA); + } + iwn_nic_unlock(sc); - IWN_WRITE(sc, IWN_RX_WIDX, (IWN_RX_RING_COUNT - 1) & ~7); + /* Clear "radio off" and "commands blocked" bits. */ + IWN_WRITE(sc, IWN_UCODE_GP1_CLR, IWN_UCODE_GP1_RFKILL); + IWN_WRITE(sc, IWN_UCODE_GP1_CLR, IWN_UCODE_GP1_CMD_BLOCKED); - iwn_mem_lock(sc); - iwn_mem_write(sc, IWN_TX_ACTIVE, 0); + /* Clear any pending interrupts. */ + IWN_WRITE(sc, IWN_INT, 0xffffffff); + /* Enable interrupt coalescing. */ + IWN_WRITE(sc, IWN_INT_COALESCING, 512 / 8); + /* Enable interrupts. */ + IWN_WRITE(sc, IWN_MASK, IWN_INT_MASK); - /* set physical address of "keep warm" page */ - IWN_WRITE(sc, IWN_KW_BASE, sc->kw_dma.paddr >> 4); + /* _Really_ make sure "radio off" bit is cleared! */ + IWN_WRITE(sc, IWN_UCODE_GP1_CLR, IWN_UCODE_GP1_RFKILL); + IWN_WRITE(sc, IWN_UCODE_GP1_CLR, IWN_UCODE_GP1_RFKILL); - /* init Tx rings */ - for (qid = 0; qid < IWN_NTXQUEUES; qid++) { - struct iwn_tx_ring *txq = &sc->txq[qid]; - IWN_WRITE(sc, IWN_TX_BASE(qid), txq->desc_dma.paddr >> 8); - IWN_WRITE(sc, IWN_TX_CONFIG(qid), 0x80000008); + if ((error = hal->load_firmware(sc)) != 0) { + printf("%s: could not load firmware\n", sc->sc_dev.dv_xname); + return error; } - iwn_mem_unlock(sc); + /* Wait at most one second for firmware alive notification. */ + if ((error = tsleep(sc, PCATCH, "iwninit", hz)) != 0) { + printf("%s: timeout waiting for adapter to initialize\n", + sc->sc_dev.dv_xname); + return error; + } + /* Do post-firmware initialization. */ + return hal->post_alive(sc); +} - /* clear "radio off" and "disable command" bits (reversed logic) */ - IWN_WRITE(sc, IWN_UCODE_CLR, IWN_RADIO_OFF); - IWN_WRITE(sc, IWN_UCODE_CLR, IWN_DISABLE_CMD); +void +iwn_hw_stop(struct iwn_softc *sc) +{ + const struct iwn_hal *hal = sc->sc_hal; + int qid; - /* clear any pending interrupts */ - IWN_WRITE(sc, IWN_INTR, 0xffffffff); - /* enable interrupts */ - IWN_WRITE(sc, IWN_MASK, IWN_INTR_MASK); + IWN_WRITE(sc, IWN_RESET, IWN_RESET_NEVO); - /* not sure why/if this is necessary... */ - IWN_WRITE(sc, IWN_UCODE_CLR, IWN_RADIO_OFF); - IWN_WRITE(sc, IWN_UCODE_CLR, IWN_RADIO_OFF); + /* Disable interrupts. */ + IWN_WRITE(sc, IWN_MASK, 0); + IWN_WRITE(sc, IWN_INT, 0xffffffff); + IWN_WRITE(sc, IWN_FH_INT, 0xffffffff); + + /* Make sure we no longer hold the NIC lock. */ + iwn_nic_unlock(sc); + + /* Stop TX scheduler. */ + iwn_prph_write(sc, hal->sched_txfact_addr, 0); + + /* Stop all TX rings. */ + for (qid = 0; qid < hal->ntxqs; qid++) + iwn_reset_tx_ring(sc, &sc->txq[qid]); + + /* Stop RX ring. */ + iwn_reset_rx_ring(sc, &sc->rxq); - /* check that the radio is not disabled by RF switch */ - if (!(IWN_READ(sc, IWN_GPIO_CTL) & IWN_GPIO_RF_ENABLED)) { + if (iwn_nic_lock(sc) == 0) { + iwn_prph_write(sc, IWN_APMG_CLK_DIS, IWN_APMG_CLK_DMA_RQT); + iwn_nic_unlock(sc); + } + DELAY(5); + /* Power OFF adapter. */ + iwn_apm_stop(sc); +} + +int +iwn_init(struct ifnet *ifp) +{ + struct iwn_softc *sc = ifp->if_softc; + struct ieee80211com *ic = &sc->sc_ic; + int error; + + /* Check that the radio is not disabled by hardware switch. */ + if (!(IWN_READ(sc, IWN_GP_CNTRL) & IWN_GP_CNTRL_RFKILL)) { printf("%s: radio is disabled by hardware switch\n", sc->sc_dev.dv_xname); - error = EPERM; /* XXX ;-) */ - goto fail1; + error = EPERM; /* :-) */ + goto fail; } - if ((error = iwn_load_firmware(sc)) != 0) { - printf("%s: could not load firmware\n", sc->sc_dev.dv_xname); - goto fail1; + /* Read firmware images from the filesystem. */ + if ((error = iwn_read_firmware(sc)) != 0) { + printf("%s: could not read firmware\n", sc->sc_dev.dv_xname); + goto fail; } - /* firmware has notified us that it is alive.. */ - iwn_post_alive(sc); /* ..do post alive initialization */ - - sc->rawtemp = sc->ucode_info.temp[3].chan20MHz; - sc->temp = iwn_get_temperature(sc); - DPRINTF(("temperature=%d\n", sc->temp)); - sc->sensor.value = IWN_CTOMUK(sc->temp); - sc->sensor.flags &= ~SENSOR_FINVALID; + /* Initialize hardware and upload firmware. */ + error = iwn_hw_init(sc); + free(sc->fw.data, M_DEVBUF); + if (error != 0) { + printf("%s: could not initialize hardware\n", + sc->sc_dev.dv_xname); + goto fail; + } + /* Configure adapter now that it is ready. */ if ((error = iwn_config(sc)) != 0) { printf("%s: could not configure device\n", sc->sc_dev.dv_xname); - goto fail1; + goto fail; } ifp->if_flags &= ~IFF_OACTIVE; @@ -3614,7 +4777,7 @@ iwn_init(struct ifnet *ifp) return 0; -fail1: iwn_stop(ifp, 1); +fail: iwn_stop(ifp, 1); return error; } @@ -3623,49 +4786,19 @@ iwn_stop(struct ifnet *ifp, int disable) { struct iwn_softc *sc = ifp->if_softc; struct ieee80211com *ic = &sc->sc_ic; - uint32_t tmp; - int i; ifp->if_timer = sc->sc_tx_timer = 0; ifp->if_flags &= ~(IFF_RUNNING | IFF_OACTIVE); - /* in case we were scanning, release the scan "lock" */ + /* In case we were scanning, release the scan "lock". */ ic->ic_scan_lock = IEEE80211_SCAN_UNLOCKED; ieee80211_new_state(ic, IEEE80211_S_INIT, -1); - IWN_WRITE(sc, IWN_RESET, IWN_NEVO_RESET); - - /* disable interrupts */ - IWN_WRITE(sc, IWN_MASK, 0); - IWN_WRITE(sc, IWN_INTR, 0xffffffff); - IWN_WRITE(sc, IWN_INTR_STATUS, 0xffffffff); - - /* make sure we no longer hold the memory lock */ - iwn_mem_unlock(sc); + /* Power OFF hardware. */ + iwn_hw_stop(sc); - /* reset all Tx rings */ - for (i = 0; i < IWN_NTXQUEUES; i++) - iwn_reset_tx_ring(sc, &sc->txq[i]); - - /* reset Rx ring */ - iwn_reset_rx_ring(sc, &sc->rxq); - - /* temperature is no longer valid */ + /* Temperature sensor is no longer valid. */ sc->sensor.value = 0; sc->sensor.flags |= SENSOR_FINVALID; - - iwn_mem_lock(sc); - iwn_mem_write(sc, IWN_MEM_CLOCK2, 0x200); - iwn_mem_unlock(sc); - - DELAY(5); - - iwn_stop_master(sc); - tmp = IWN_READ(sc, IWN_RESET); - IWN_WRITE(sc, IWN_RESET, tmp | IWN_SW_RESET); } - -struct cfdriver iwn_cd = { - NULL, "iwn", DV_IFNET -}; diff --git a/sys/dev/pci/if_iwnreg.h b/sys/dev/pci/if_iwnreg.h index 6f3e859d029..8f0444b94b0 100644 --- a/sys/dev/pci/if_iwnreg.h +++ b/sys/dev/pci/if_iwnreg.h @@ -1,7 +1,7 @@ -/* $OpenBSD: if_iwnreg.h,v 1.10 2008/04/27 19:01:59 damien Exp $ */ +/* $OpenBSD: if_iwnreg.h,v 1.11 2008/10/13 16:37:10 damien Exp $ */ /*- - * Copyright (c) 2007 + * Copyright (c) 2007, 2008 * Damien Bergamini <damien.bergamini@free.fr> * * Permission to use, copy, modify, and distribute this software for any @@ -18,177 +18,278 @@ */ #define IWN_TX_RING_COUNT 256 -#define IWN_RX_RING_COUNT 64 +#define IWN_TX_RING_LOMARK 192 +#define IWN_TX_RING_HIMARK 224 +#define IWN_RX_RING_COUNT_LOG 8 +#define IWN_RX_RING_COUNT (1 << IWN_RX_RING_COUNT_LOG) -#define IWN_NTXQUEUES 16 -#define IWN_NTXCHAINS 2 +#define IWN4965_NTXQUEUES 16 +#define IWN5000_NTXQUEUES 20 +#define IWN_SRVC_CHNL 9 -/* - * Rings must be aligned on a 256-byte boundary. - */ -#define IWN_RING_DMA_ALIGN 256 - -/* maximum scatter/gather */ +/* Maximum number of DMA segments for TX. */ #define IWN_MAX_SCATTER 20 -/* Rx buffers must be large enough to hold a full 4K A-MPDU */ +/* RX buffers must be large enough to hold a full 4K A-MPDU. */ #define IWN_RBUF_SIZE (4 * 1024) +/* Base Address Register. */ +#define IWN_PCI_BAR0 PCI_MAPREG_START + +/* Possible flags for PCIe Device Control Register (see PCIe 7.8.4) */ +#define PCI_PCIE_DCSR_ENA_NO_SNOOP (1 << 11) + +/* Possible flags for PCIe Link Control Register (see PCIe 7.8.7) */ +#define PCI_PCIE_LCSR_ASPM_L0S (1 << 0) +#define PCI_PCIE_LCSR_ASPM_L1 (1 << 1) + /* * Control and status registers. */ -#define IWN_HWCONFIG 0x000 -#define IWN_INTR_MIT 0x004 -#define IWN_INTR 0x008 +#define IWN_HW_IF_CONFIG 0x000 +#define IWN_INT_COALESCING 0x004 +#define IWN_INT 0x008 #define IWN_MASK 0x00c -#define IWN_INTR_STATUS 0x010 +#define IWN_FH_INT 0x010 #define IWN_RESET 0x020 -#define IWN_GPIO_CTL 0x024 -#define IWN_EEPROM_CTL 0x02c -#define IWN_UCODE_CLR 0x05c -#define IWN_CHICKEN 0x100 -#define IWN_QUEUE_OFFSET(qid) (0x380 + (qid) * 8) +#define IWN_GP_CNTRL 0x024 +#define IWN_HW_REV 0x028 +#define IWN_EEPROM 0x02c +#define IWN_EEPROM_GP 0x030 +#define IWN_GIO 0x03c +#define IWN_UCODE_GP1_CLR 0x05c +#define IWN_LED 0x094 +#define IWN_GIO_CHICKEN 0x100 +#define IWN_ANA_PLL 0x20c +#define IWN_DBG_HPET_MEM 0x240 +#define IWN_MEM_RADDR 0x40c #define IWN_MEM_WADDR 0x410 #define IWN_MEM_WDATA 0x418 -#define IWN_WRITE_MEM_ADDR 0x444 -#define IWN_READ_MEM_ADDR 0x448 -#define IWN_WRITE_MEM_DATA 0x44c -#define IWN_READ_MEM_DATA 0x450 -#define IWN_TX_WIDX 0x460 - -#define IWN_KW_BASE 0x197c -#define IWN_TX_BASE(qid) (0x19d0 + (qid) * 4) -#define IWN_RW_WIDX_PTR 0x1bc0 -#define IWN_RX_BASE 0x1bc4 -#define IWN_RX_WIDX 0x1bc8 -#define IWN_RX_CONFIG 0x1c00 -#define IWN_RX_STATUS 0x1c44 -#define IWN_TX_CONFIG(qid) (0x1d00 + (qid) * 32) -#define IWN_TX_STATUS 0x1eb0 - -#define IWN_SRAM_BASE 0xa02c00 -#define IWN_TX_ACTIVE (IWN_SRAM_BASE + 0x01c) -#define IWN_QUEUE_RIDX(qid) (IWN_SRAM_BASE + 0x064 + (qid) * 4) -#define IWN_SELECT_QCHAIN (IWN_SRAM_BASE + 0x0d0) -#define IWN_QUEUE_INTR_MASK (IWN_SRAM_BASE + 0x0e4) -#define IWN_TXQ_STATUS(qid) (IWN_SRAM_BASE + 0x104 + (qid) * 4) +#define IWN_MEM_RDATA 0x41c +#define IWN_PRPH_WADDR 0x444 +#define IWN_PRPH_RADDR 0x448 +#define IWN_PRPH_WDATA 0x44c +#define IWN_PRPH_RDATA 0x450 +#define IWN_HBUS_TARG_WRPTR 0x460 /* - * NIC internal memory offsets. + * Flow-Handler registers. */ -#define IWN_CLOCK_CTL 0x3000 -#define IWN_MEM_CLOCK2 0x3008 -#define IWN_MEM_POWER 0x300c -#define IWN_MEM_PCIDEV 0x3010 -#define IWN_MEM_UCODE_CTL 0x3400 -#define IWN_MEM_UCODE_SRC 0x3404 -#define IWN_MEM_UCODE_DST 0x3408 -#define IWN_MEM_UCODE_SIZE 0x340c -#define IWN_MEM_TEXT_BASE 0x3490 -#define IWN_MEM_TEXT_SIZE 0x3494 -#define IWN_MEM_DATA_BASE 0x3498 -#define IWN_MEM_DATA_SIZE 0x349c -#define IWN_MEM_UCODE_BASE 0x3800 +#define IWN_FH_TFBD_CTRL0(qid) (0x1900 + (qid) * 8) +#define IWN_FH_TFBD_CTRL1(qid) (0x1904 + (qid) * 8) +#define IWN_FH_KW_ADDR 0x197c +#define IWN_FH_SRAM_ADDR(qid) (0x19a4 + (qid) * 4) +#define IWN_FH_CBBC_QUEUE(qid) (0x19d0 + (qid) * 4) +#define IWN_FH_STATUS_WPTR 0x1bc0 +#define IWN_FH_RX_BASE 0x1bc4 +#define IWN_FH_RX_WPTR 0x1bc8 +#define IWN_FH_RX_CONFIG 0x1c00 +#define IWN_FH_RX_STATUS 0x1c44 +#define IWN_FH_TX_CONFIG(qid) (0x1d00 + (qid) * 32) +#define IWN_FH_TXBUF_STATUS(qid) (0x1d08 + (qid) * 32) +#define IWN_FH_TX_STATUS 0x1eb0 +/* + * TX scheduler registers. + */ +#define IWN_SCHED_BASE 0xa02c00 +#define IWN_SCHED_SRAM_ADDR (IWN_SCHED_BASE + 0x000) +#define IWN5000_SCHED_DRAM_ADDR (IWN_SCHED_BASE + 0x008) +#define IWN4965_SCHED_DRAM_ADDR (IWN_SCHED_BASE + 0x010) +#define IWN5000_SCHED_TXFACT (IWN_SCHED_BASE + 0x010) +#define IWN4965_SCHED_TXFACT (IWN_SCHED_BASE + 0x01c) +#define IWN4965_SCHED_QUEUE_RDPTR(qid) (IWN_SCHED_BASE + 0x064 + (qid) * 4) +#define IWN5000_SCHED_QUEUE_RDPTR(qid) (IWN_SCHED_BASE + 0x068 + (qid) * 4) +#define IWN4965_SCHED_QCHAIN_SEL (IWN_SCHED_BASE + 0x0d0) +#define IWN4965_SCHED_INTR_MASK (IWN_SCHED_BASE + 0x0e4) +#define IWN5000_SCHED_QCHAIN_SEL (IWN_SCHED_BASE + 0x0e8) +#define IWN4965_SCHED_QUEUE_STATUS(qid) (IWN_SCHED_BASE + 0x104 + (qid) * 4) +#define IWN5000_SCHED_INTR_MASK (IWN_SCHED_BASE + 0x108) +#define IWN5000_SCHED_QUEUE_STATUS(qid) (IWN_SCHED_BASE + 0x10c + (qid) * 4) +#define IWN5000_SCHED_AGGR_SEL (IWN_SCHED_BASE + 0x248) -/* possible flags for register IWN_HWCONFIG */ -#define IWN_HW_EEPROM_LOCKED (1 << 21) - -/* possible flags for registers IWN_READ_MEM_ADDR/IWN_WRITE_MEM_ADDR */ -#define IWN_MEM_4 ((sizeof (uint32_t) - 1) << 24) +/* + * Offsets in TX scheduler's SRAM. + */ +#define IWN4965_SCHED_CTX_OFF 0x380 +#define IWN4965_SCHED_CTX_LEN 416 +#define IWN4965_SCHED_QUEUE_OFFSET(qid) (0x380 + (qid) * 8) +#define IWN4965_SCHED_TRANS_TBL(qid) (0x500 + (qid) * 2) +#define IWN5000_SCHED_CTX_OFF 0x600 +#define IWN5000_SCHED_CTX_LEN 520 +#define IWN5000_SCHED_QUEUE_OFFSET(qid) (0x600 + (qid) * 8) +#define IWN5000_SCHED_TRANS_TBL(qid) (0x7e0 + (qid) * 2) -/* possible values for IWN_MEM_UCODE_DST */ -#define IWN_FW_TEXT 0x00000000 +/* + * NIC internal memory offsets. + */ +#define IWN_CLOCK_CTL 0x3000 +#define IWN_APMG_CLK_CTRL 0x3004 +#define IWN_APMG_CLK_DIS 0x3008 +#define IWN_APMG_PS 0x300c +#define IWN_APMG_PCI_STT 0x3010 +#define IWN_BSM_WR_CTRL 0x3400 +#define IWN_BSM_WR_MEM_SRC 0x3404 +#define IWN_BSM_WR_MEM_DST 0x3408 +#define IWN_BSM_WR_DWCOUNT 0x340c +#define IWN_BSM_DRAM_TEXT_ADDR 0x3490 +#define IWN_BSM_DRAM_TEXT_SIZE 0x3494 +#define IWN_BSM_DRAM_DATA_ADDR 0x3498 +#define IWN_BSM_DRAM_DATA_SIZE 0x349c +#define IWN_BSM_SRAM_BASE 0x3800 + +/* possible values for IWN_APMG_CLK_DIS */ +#define IWN_APMG_CLK_DMA_RQT (1 << 9) + +/* possible flags for register IWN_HW_IF_CONFIG */ +#define IWN_HW_IF_CONFIG_4965_R (1 << 4) +#define IWN_HW_IF_CONFIG_MAC_SI (1 << 8) +#define IWN_HW_IF_CONFIG_RADIO_SI (1 << 9) +#define IWN_HW_IF_CONFIG_EEPROM_LOCKED (1 << 21) +#define IWN_HW_IF_CONFIG_HAP_WAKE_L1A (1 << 23) + +/* possible flags for registers IWN_PRPH_RADDR/IWN_PRPH_WADDR */ +#define IWN_PRPH_DWORD ((sizeof (uint32_t) - 1) << 24) + +/* possible values for IWN_BSM_WR_MEM_DST */ +#define IWN_FW_TEXT_BASE 0x00000000 +#define IWN_FW_DATA_BASE 0x00800000 /* possible flags for register IWN_RESET */ -#define IWN_NEVO_RESET (1 << 0) -#define IWN_SW_RESET (1 << 7) -#define IWN_MASTER_DISABLED (1 << 8) -#define IWN_STOP_MASTER (1 << 9) - -/* possible flags for register IWN_GPIO_CTL */ -#define IWN_GPIO_CLOCK (1 << 0) -#define IWN_GPIO_INIT (1 << 2) -#define IWN_GPIO_MAC (1 << 3) -#define IWN_GPIO_SLEEP (1 << 4) -#define IWN_GPIO_PWR_STATUS 0x07000000 -#define IWN_GPIO_PWR_SLEEP (4 << 24) -#define IWN_GPIO_RF_ENABLED (1 << 27) - -/* possible flags for register IWN_CHICKEN */ -#define IWN_CHICKEN_DISLOS (1 << 29) - -/* possible flags for register IWN_UCODE_CLR */ -#define IWN_RADIO_OFF (1 << 1) -#define IWN_DISABLE_CMD (1 << 2) -#define IWN_CTEMP_STOP_RF (1 << 3) - -/* possible flags for IWN_RX_STATUS */ -#define IWN_RX_IDLE (1 << 24) - -/* possible flags for register IWN_UC_CTL */ -#define IWN_UC_ENABLE (1 << 30) -#define IWN_UC_RUN (1 << 31) - -/* possible flags for register IWN_INTR */ -#define IWN_ALIVE_INTR (1 << 0) -#define IWN_WAKEUP_INTR (1 << 1) -#define IWN_SW_RX_INTR (1 << 3) -#define IWN_CT_REACHED (1 << 6) -#define IWN_RF_TOGGLED (1 << 7) -#define IWN_SW_ERROR (1 << 25) -#define IWN_TX_INTR (1 << 27) -#define IWN_HW_ERROR (1 << 29) -#define IWN_RX_INTR (1 << 31) - -#define IWN_INTR_MASK \ - (IWN_SW_ERROR | IWN_HW_ERROR | IWN_TX_INTR | IWN_RX_INTR | \ - IWN_ALIVE_INTR | IWN_WAKEUP_INTR | IWN_SW_RX_INTR | \ - IWN_CT_REACHED | IWN_RF_TOGGLED) - -/* possible flags for register IWN_INTR_STATUS */ -#define IWN_STATUS_TXQ(x) (1 << (x)) -#define IWN_STATUS_RXQ(x) (1 << ((x) + 16)) -#define IWN_STATUS_PRI (1 << 30) +#define IWN_RESET_NEVO (1 << 0) +#define IWN_RESET_SW (1 << 7) +#define IWN_RESET_MASTER_DISABLED (1 << 8) +#define IWN_RESET_STOP_MASTER (1 << 9) + +/* possible flags for register IWN_GP_CNTRL */ +#define IWN_GP_CNTRL_MAC_ACCESS_ENA (1 << 0) +#define IWN_GP_CNTRL_MAC_CLOCK_READY (1 << 0) +#define IWN_GP_CNTRL_INIT_DONE (1 << 2) +#define IWN_GP_CNTRL_MAC_ACCESS_REQ (1 << 3) +#define IWN_GP_CNTRL_SLEEP (1 << 4) +#define IWN_GP_CNTRL_RFKILL (1 << 27) + +/* possibles flags for register IWN_HW_REV */ +#define IWN_HW_REV_TYPE_SHIFT 4 +#define IWN_HW_REV_TYPE_MASK 0x000000f0 +#define IWN_HW_REV_TYPE_4965 0 +#define IWN_HW_REV_TYPE_5300 2 +#define IWN_HW_REV_TYPE_5350 3 +#define IWN_HW_REV_TYPE_5150 4 +#define IWN_HW_REV_TYPE_5100 5 + +/* possible flags for register IWN_GIO_CHICKEN */ +#define IWN_GIO_CHICKEN_L1A_NO_L0S_RX (1 << 23) +#define IWN_GIO_CHICKEN_DIS_L0S_TIMER (1 << 29) + +/* possible flags for register IWN_GIO */ +#define IWN_GIO_L0S_ENA (1 << 1) + +/* possible flags for register IWN_UCODE_GP1_CLR */ +#define IWN_UCODE_GP1_RFKILL (1 << 1) +#define IWN_UCODE_GP1_CMD_BLOCKED (1 << 2) +#define IWN_UCODE_GP1_CTEMP_STOP_RF (1 << 3) + +/* possible flags for register IWN_LED */ +#define IWN_LED_BSM_CTRL (1 << 5) + +/* possible values for register IWN_ANA_PLL */ +#define IWN_ANA_PLL_INIT 0x00880300 + +/* possible flags for register IWN_FH_RX_STATUS */ +#define IWN_FH_RX_STATUS_IDLE (1 << 24) + +/* possible flags for register IWN_BSM_WR_CTRL */ +#define IWN_BSM_WR_CTRL_START_EN (1 << 30) +#define IWN_BSM_WR_CTRL_START (1 << 31) + +/* possible flags for register IWN_INT */ +#define IWN_INT_ALIVE (1 << 0) +#define IWN_INT_WAKEUP (1 << 1) +#define IWN_INT_SW_RX (1 << 3) +#define IWN_INT_CT_REACHED (1 << 6) +#define IWN_INT_RF_TOGGLED (1 << 7) +#define IWN_INT_SW_ERR (1 << 25) +#define IWN_INT_FH_TX (1 << 27) +#define IWN_INT_HW_ERR (1 << 29) +#define IWN_INT_FH_RX (1 << 31) + +#define IWN_INT_MASK \ + (IWN_INT_SW_ERR | IWN_INT_HW_ERR | IWN_INT_FH_TX | \ + IWN_INT_FH_RX | IWN_INT_ALIVE | IWN_INT_WAKEUP | \ + IWN_INT_SW_RX | IWN_INT_CT_REACHED | IWN_INT_RF_TOGGLED) + +/* possible flags for register IWN_FH_INT */ +#define IWN_FH_INT_TX_CHNL(x) (1 << (x)) +#define IWN_FH_INT_RX_CHNL(x) (1 << ((x) + 16)) +#define IWN_FH_INT_HI_PRIOR (1 << 30) /* shortcuts for the above */ -#define IWN_TX_STATUS_INTR \ - (IWN_STATUS_TXQ(0) | IWN_STATUS_TXQ(1) | IWN_STATUS_TXQ(6)) -#define IWN_RX_STATUS_INTR \ - (IWN_STATUS_RXQ(0) | IWN_STATUS_RXQ(1) | IWN_STATUS_RXQ(2) | \ - IWN_STATUS_PRI) - -/* possible flags for register IWN_TX_STATUS */ -#define IWN_TX_IDLE(qid) (1 << ((qid) + 24) | 1 << ((qid) + 16)) - -/* possible flags for register IWN_EEPROM_CTL */ -#define IWN_EEPROM_READY (1 << 0) +#define IWN_FH_INT_TX \ + (IWN_FH_INT_TX_CHNL(0) | IWN_FH_INT_TX_CHNL(1)) +#define IWN_FH_INT_RX \ + (IWN_FH_INT_RX_CHNL(0) | IWN_FH_INT_RX_CHNL(1) | IWN_FH_INT_HI_PRIOR) + +/* possible flags/values for register IWN_FH_TX_CONFIG */ +#define IWN_FH_TX_CONFIG_DMA_PAUSE 0 +#define IWN_FH_TX_CONFIG_DMA_ENA (1 << 31) +#define IWN_FH_TX_CONFIG_CIRQ_HOST_ENDTFD (1 << 20) + +/* possible values for register IWN_FH_TXBUF_STATUS */ +#define IWN_FH_TXBUF_STATUS_TBNUM(x) ((x) << 20) +#define IWN_FH_TXBUF_STATUS_TBIDX(x) ((x) << 12) +#define IWN_FH_TXBUF_STATUS_TFBD_VALID 3 + +/* possible flags for register IWN_FH_TX_STATUS */ +#define IWN_FH_TX_STATUS_IDLE(chnl) \ + (1 << ((chnl) + 24) | 1 << ((chnl) + 16)) + +/* possible flags for register IWN_FH_RX_CONFIG */ +#define IWN_FH_RX_CONFIG_ENA (1 << 31) +#define IWN_FH_RX_CONFIG_NRBD(x) ((x) << 20) +#define IWN_FH_RX_CONFIG_RB_SIZE_8K (1 << 16) +#define IWN_FH_RX_CONFIG_IRQ_DST_HOST (1 << 12) +#define IWN_FH_RX_CONFIG_RB_TIMEOUT(x) ((x) << 4) +#define IWN_FH_RX_CONFIG_IGN_RXF_EMPTY (1 << 2) + +/* possible flags for register IWN_FH_TX_CONFIG */ +#define IWN_FH_TX_CONFIG_DMA_ENA (1 << 31) +#define IWN_FH_TX_CONFIG_DMA_CREDIT_ENA (1 << 3) + +/* possible flags for register IWN_EEPROM */ +#define IWN_EEPROM_READ_VALID (1 << 0) #define IWN_EEPROM_CMD (1 << 1) -/* possible flags for register IWN_TXQ_STATUS */ -#define IWN_TXQ_STATUS_ACTIVE 0x0007fc01 - -/* possible flags for register IWN_MEM_POWER */ -#define IWN_POWER_RESET (1 << 26) - -/* possible flags for register IWN_MEM_TEXT_SIZE */ +/* possible flags for register IWN5000_SCHED_QUEUE_STATUS */ +#define IWN4965_TXQ_STATUS_ACTIVE 0x0007fc01 +#define IWN4965_TXQ_STATUS_INACTIVE 0x0007fc00 +#define IWN4965_TXQ_STATUS_AGGR_ENA (1 << 5 | 1 << 8) +#define IWN4965_TXQ_STATUS_CHGACT (1 << 10) +#define IWN5000_TXQ_STATUS_ACTIVE 0x00ff0018 +#define IWN5000_TXQ_STATUS_INACTIVE 0x00ff0010 +#define IWN5000_TXQ_STATUS_CHGACT (1 << 19) + +/* possible flags for register IWN_APMG_CLK_CTRL */ +#define IWN_APMG_CLK_CTRL_DMA_CLK_RQT (1 << 9) +#define IWN_APMG_CLK_CTRL_BSM_CLK_RQT (1 << 11) + +/* possible flags for register IWN_APMG_PS */ +#define IWN_APMG_PS_EARLY_PWROFF_DIS (1 << 22) +#define IWN_APMG_PS_PWR_SRC_MASK (3 << 24) +#define IWN_APMG_PS_PWR_SRC(x) ((x) << 24) +#define IWN_APMG_PS_PWR_SRC_VMAIN 0 + +/* possible flags for IWN_APMG_PCI_STT */ +#define IWN_APMG_PCI_STT_L1A_DIS (1 << 11) + +/* possible flags for register IWN_BSM_DRAM_TEXT_SIZE */ #define IWN_FW_UPDATED (1 << 31) -/* possible flags for device-specific PCI register 0xe8 */ -#define IWN_DIS_NOSNOOP (1 << 11) - -/* possible flags for device-specific PCI register 0xf0 */ -#define IWN_ENA_L1 (1 << 1) - - -#define IWN_TX_WINDOW 64 -struct iwn_shared { - uint16_t len[IWN_NTXQUEUES][512]; /* 16KB total */ - uint16_t closed_count; - uint16_t closed_rx_count; - uint16_t finished_count; - uint16_t finished_rx_count; - uint32_t reserved[2]; -} __packed; +#define IWN_SCHED_WINSZ 64 +#define IWN_SCHED_LIMIT 64 +#define IWN4965_SCHED_COUNT 512 +#define IWN5000_SCHED_COUNT (IWN_TX_RING_COUNT + IWN_SCHED_WINSZ) +#define IWN4965_SCHEDSZ (IWN4965_NTXQUEUES * IWN4965_SCHED_COUNT * 2) +#define IWN5000_SCHEDSZ (IWN5000_NTXQUEUES * IWN5000_SCHED_COUNT * 2) struct iwn_tx_desc { uint32_t flags; @@ -217,21 +318,31 @@ struct iwn_tx_desc { } \ } while (0) +struct iwn_rx_status { + uint16_t closed_count; + uint16_t closed_rx_count; + uint16_t finished_count; + uint16_t finished_rx_count; + uint32_t reserved[2]; +} __packed; + struct iwn_rx_desc { uint32_t len; uint8_t type; -#define IWN_UC_READY 1 -#define IWN_ADD_NODE_DONE 24 -#define IWN_TX_DONE 28 -#define IWN_START_SCAN 130 -#define IWN_STOP_SCAN 132 -#define IWN_RX_STATISTICS 156 -#define IWN_BEACON_STATISTICS 157 -#define IWN_STATE_CHANGED 161 -#define IWN_BEACON_MISSED 162 -#define IWN_AMPDU_RX_START 192 -#define IWN_AMPDU_RX_DONE 193 -#define IWN_RX_DONE 195 +#define IWN_UC_READY 1 +#define IWN_ADD_NODE_DONE 24 +#define IWN_TX_DONE 28 +#define IWN5000_CALIBRATION_RESULT 102 +#define IWN5000_CALIBRATION_DONE 103 +#define IWN_START_SCAN 130 +#define IWN_STOP_SCAN 132 +#define IWN_RX_STATISTICS 156 +#define IWN_BEACON_STATISTICS 157 +#define IWN_STATE_CHANGED 161 +#define IWN_BEACON_MISSED 162 +#define IWN_AMPDU_RX_START 192 +#define IWN_AMPDU_RX_DONE 193 +#define IWN_RX_DONE 195 uint8_t flags; uint8_t idx; @@ -249,15 +360,18 @@ struct iwn_tx_cmd { #define IWN_CMD_CONFIGURE 16 #define IWN_CMD_ASSOCIATE 17 #define IWN_CMD_EDCA_PARAMS 19 -#define IWN_CMD_TSF 20 +#define IWN_CMD_TIMING 20 #define IWN_CMD_ADD_NODE 24 #define IWN_CMD_TX_DATA 28 #define IWN_CMD_NODE_MRR_SETUP 78 #define IWN_CMD_SET_LED 72 +#define IWN5000_CMD_WIMAX_COEX 90 +#define IWN5000_CMD_CALIB_CONFIG 101 #define IWN_CMD_SET_POWER_MODE 119 #define IWN_CMD_SCAN 128 #define IWN_CMD_TXPOWER 151 -#define IWN_CMD_BLUETOOTH 155 +#define IWN_CMD_TXPOWER_DBM 152 +#define IWN_CMD_BT_COEX 155 #define IWN_CMD_GET_STATISTICS 156 #define IWN_CMD_SET_CRITICAL_TEMP 164 #define IWN_SENSITIVITY 168 @@ -270,7 +384,7 @@ struct iwn_tx_cmd { } __packed; /* structure for command IWN_CMD_CONFIGURE */ -struct iwn_config { +struct iwn_rxon { uint8_t myaddr[IEEE80211_ADDR_LEN]; uint16_t reserved1; uint8_t bssid[IEEE80211_ADDR_LEN]; @@ -283,24 +397,37 @@ struct iwn_config { #define IWN_MODE_IBSS 4 #define IWN_MODE_MONITOR 6 - uint8_t reserved4; + uint8_t air; uint16_t rxchain; -#define IWN_RXCHAIN_ANTMSK_SHIFT 1 -#define IWN_RXCHAIN_FORCE_MIMO (1 << 14) +#define IWN_ANT_A (1 << 0) +#define IWN_ANT_B (1 << 1) +#define IWN_ANT_C (1 << 2) +/* Shortcut. */ +#define IWN_ANT_ABC (IWN_ANT_A | IWN_ANT_B | IWN_ANT_C) + +#define IWN_RXCHAIN_FORCE (1 << 0) +#define IWN_RXCHAIN_VALID(x) ((x) << 1) +#define IWN_RXCHAIN_SEL(x) ((x) << 4) +#define IWN_RXCHAIN_MIMO(x) ((x) << 7) +#define IWN_RXCHAIN_IDLE_COUNT(x) ((x) << 10) +#define IWN_RXCHAIN_MIMO_COUNT(x) ((x) << 12) +#define IWN_RXCHAIN_MIMO_FORCE (1 << 14) + uint8_t ofdm_mask; uint8_t cck_mask; uint16_t associd; uint32_t flags; -#define IWN_CONFIG_24GHZ (1 << 0) -#define IWN_CONFIG_CCK (1 << 1) -#define IWN_CONFIG_AUTO (1 << 2) -#define IWN_CONFIG_SHSLOT (1 << 4) -#define IWN_CONFIG_SHPREAMBLE (1 << 5) -#define IWN_CONFIG_NODIVERSITY (1 << 7) -#define IWN_CONFIG_ANTENNA_A (1 << 8) -#define IWN_CONFIG_ANTENNA_B (1 << 9) -#define IWN_CONFIG_TSF (1 << 15) +#define IWN_RXON_24GHZ (1 << 0) +#define IWN_RXON_CCK (1 << 1) +#define IWN_RXON_AUTO (1 << 2) +#define IWN_RXON_SHSLOT (1 << 4) +#define IWN_RXON_SHPREAMBLE (1 << 5) +#define IWN_RXON_NODIVERSITY (1 << 7) +#define IWN_RXON_ANTENNA_A (1 << 8) +#define IWN_RXON_ANTENNA_B (1 << 9) +#define IWN_RXON_TSF (1 << 15) +#define IWN_RXON_CTS_TO_SELF (1 << 30) uint32_t filter; #define IWN_FILTER_PROMISC (1 << 0) @@ -308,13 +435,22 @@ struct iwn_config { #define IWN_FILTER_MULTICAST (1 << 2) #define IWN_FILTER_NODECRYPT (1 << 3) #define IWN_FILTER_BSS (1 << 5) +#define IWN_FILTER_BEACON (1 << 6) uint8_t chan; - uint8_t reserved5; + uint8_t reserved4; uint8_t ht_single_mask; uint8_t ht_dual_mask; + /* the following fields are for 5000 Series only */ + uint8_t ht_triple_mask; + uint8_t reserved5; + uint16_t acquisition; + uint16_t reserved6; } __packed; +#define IWN4965_RXONSZ (sizeof (struct iwn_rxon) - 6) +#define IWN5000_RXONSZ (sizeof (struct iwn_rxon)) + /* structure for command IWN_CMD_ASSOCIATE */ struct iwn_assoc { uint32_t flags; @@ -339,8 +475,8 @@ struct iwn_edca_params { } __packed ac[EDCA_NUM_AC]; } __packed; -/* structure for command IWN_CMD_TSF */ -struct iwn_cmd_tsf { +/* structure for command IWN_CMD_TIMING */ +struct iwn_cmd_timing { uint64_t tstamp; uint16_t bintval; uint16_t atim; @@ -355,61 +491,100 @@ struct iwn_node_info { #define IWN_NODE_UPDATE (1 << 0) uint8_t reserved1[3]; + uint8_t macaddr[IEEE80211_ADDR_LEN]; uint16_t reserved2; uint8_t id; #define IWN_ID_BSS 0 -#define IWN_ID_BROADCAST 31 +#define IWN5000_ID_BROADCAST 15 +#define IWN4965_ID_BROADCAST 31 uint8_t flags; -#define IWN_FLAG_SET_KEY (1 << 0) +#define IWN_FLAG_SET_KEY (1 << 0) +#define IWN_FLAG_SET_DISABLE_TID (1 << 1) uint16_t reserved3; - uint16_t security; + + uint16_t keyflags; uint8_t tsc2; /* TKIP TSC2 */ uint8_t reserved4; uint16_t ttak[5]; - uint16_t reserved5; - uint8_t key[IEEE80211_KEYBUF_SIZE]; + uint8_t kid; + uint8_t reserved5; + uint8_t key[16]; + uint64_t tsc; + uint8_t rxmic[IEEE80211_TKIP_MICLEN]; + uint8_t txmic[IEEE80211_TKIP_MICLEN]; + uint32_t htflags; -#define IWN_AMDPU_SIZE_FACTOR_SHIFT 19 -#define IWN_AMDPU_DENSITY_SHIFT 23 +#define IWN_AMDPU_SIZE_FACTOR(x) ((x) << 19) +#define IWN_AMDPU_DENSITY(x) ((x) << 23) uint32_t mask; - uint16_t tid; - uint8_t rate; - uint8_t rflags; + uint16_t disable_tid; + uint16_t reserved6; + + uint8_t addba; + uint8_t delba; + uint16_t addba_ssn; + uint32_t reserved7; +} __packed; + +struct iwn4965_node_info { + uint8_t control; + uint8_t reserved1[3]; + uint8_t macaddr[IEEE80211_ADDR_LEN]; + uint16_t reserved2; + uint8_t id; + uint8_t flags; + uint16_t reserved3; + uint16_t keyflags; + uint8_t tsc2; /* TKIP TSC2 */ + uint8_t reserved4; + uint16_t ttak[5]; + uint8_t kid; + uint8_t reserved5; + uint8_t key[16]; + uint32_t htflags; + uint32_t mask; + uint16_t disable_tid; + uint16_t reserved6; + uint8_t addba; + uint8_t delba; + uint16_t addba_ssn; + uint32_t reserved7; +} __packed; + #define IWN_RFLAG_CCK (1 << 1) #define IWN_RFLAG_ANT_A (1 << 6) #define IWN_RFLAG_ANT_B (1 << 7) - uint8_t add_imm; - uint8_t del_imm; - uint16_t add_imm_start; - uint32_t reserved6; -} __packed; - /* structure for command IWN_CMD_TX_DATA */ struct iwn_cmd_data { uint16_t len; uint16_t lnext; uint32_t flags; +#define IWN5000_TX_NEED_RTS_CTS (1 << 0) /* 5000 only */ #define IWN_TX_NEED_RTS (1 << 1) #define IWN_TX_NEED_CTS (1 << 2) #define IWN_TX_NEED_ACK (1 << 3) #define IWN_TX_MRR_INDEX (1 << 4) +#define IWN_TX_IMM_BA (1 << 6) #define IWN_TX_FULL_TXOP (1 << 7) #define IWN_TX_BT_DISABLE (1 << 12) /* bluetooth coexistence */ #define IWN_TX_AUTO_SEQ (1 << 13) +#define IWN_TX_MORE_FRAG (1 << 14) #define IWN_TX_INSERT_TSTAMP (1 << 16) #define IWN_TX_NEED_PADDING (1 << 20) uint8_t ntries; uint8_t bluetooth; uint16_t reserved1; + uint8_t rate; uint8_t rflags; uint16_t xrflags; + uint8_t id; uint8_t security; #define IWN_CIPHER_WEP40 1 @@ -419,7 +594,7 @@ struct iwn_cmd_data { uint8_t ridx; uint8_t reserved2; - uint8_t key[IEEE80211_KEYBUF_SIZE]; + uint8_t key[16]; uint16_t fnext; uint16_t reserved3; uint32_t lifetime; @@ -451,14 +626,13 @@ struct iwn_cmd_mrr { uint32_t reserved2; struct { uint8_t rate; + uint8_t rflags; + uint16_t xrflags; #define IWN_CCK1 0 #define IWN_CCK11 3 #define IWN_OFDM6 4 #define IWN_OFDM54 11 - - uint8_t rflags; - uint16_t xrflags; - } table[IWN_MAX_TX_RETRIES]; + } __packed table[IWN_MAX_TX_RETRIES]; uint32_t reserved3; } __packed; @@ -474,16 +648,52 @@ struct iwn_cmd_led { uint8_t reserved; } __packed; +/* structure for command IWN5000_CMD_WIMAX_COEX */ +struct iwn5000_wimax_coex { + uint32_t flags; + struct { + uint8_t request; + uint8_t window; + uint8_t reserved; + uint8_t flags; + } __packed events[16]; +} __packed; + +/* structures for command IWN5000_CMD_CALIB_CONFIG */ +struct iwn5000_calib_elem { + uint32_t enable; + uint32_t start; + uint32_t send; + uint32_t apply; + uint32_t reserved; +} __packed; + +struct iwn5000_calib_status { + struct iwn5000_calib_elem once; + struct iwn5000_calib_elem perd; + uint32_t flags; +} __packed; + +struct iwn5000_calib_config { + struct iwn5000_calib_status ucode; + struct iwn5000_calib_status driver; + uint32_t reserved; +} __packed; + /* structure for command IWN_CMD_SET_POWER_MODE */ -struct iwn_power { +struct iwn_pmgt_cmd { uint16_t flags; -#define IWN_POWER_CAM 0 /* constantly awake mode */ +#define IWN_PS_ALLOW_SLEEP (1 << 0) +#define IWN_PS_NOTIFY (1 << 1) +#define IWN_PS_SLEEP_OVER_DTIM (1 << 2) +#define IWN_PS_PCI_PMGT (1 << 3) +#define IWN_PS_FAST_PD (1 << 4) - uint8_t alive; + uint8_t keepalive; uint8_t debug; - uint32_t rx_timeout; - uint32_t tx_timeout; - uint32_t sleep[5]; + uint32_t rxtimeout; + uint32_t txtimeout; + uint32_t intval[5]; uint32_t beacons; } __packed; @@ -498,8 +708,8 @@ struct iwn_scan_hdr { uint16_t len; uint8_t reserved1; uint8_t nchan; - uint16_t quiet; - uint16_t plcp_threshold; + uint16_t quiet_time; + uint16_t quiet_threshold; uint16_t crc_threshold; uint16_t rxchain; uint32_t max_svc; /* background scans */ @@ -508,17 +718,17 @@ struct iwn_scan_hdr { uint32_t filter; /* followed by a struct iwn_cmd_data */ - /* followed by an array of 4x struct iwn_scan_essid */ + /* followed by an array of 20 struct iwn_scan_essid */ /* followed by probe request body */ /* followed by nchan x struct iwn_scan_chan */ } __packed; struct iwn_scan_chan { - uint8_t flags; + uint32_t type; #define IWN_CHAN_ACTIVE (1 << 0) #define IWN_CHAN_DIRECT (1 << 1) - uint8_t chan; + uint16_t chan; uint8_t rf_gain; uint8_t dsp_gain; uint16_t active; /* msecs */ @@ -527,15 +737,28 @@ struct iwn_scan_chan { /* structure for command IWN_CMD_TXPOWER */ #define IWN_RIDX_MAX 32 -struct iwn_cmd_txpower { - uint8_t band; - uint8_t reserved1; - uint8_t chan; - uint8_t reserved2; +struct iwn4965_cmd_txpower { + uint8_t band; + uint8_t reserved1; + uint8_t chan; + uint8_t reserved2; struct { - uint8_t rf_gain[IWN_NTXCHAINS]; - uint8_t dsp_gain[IWN_NTXCHAINS]; - } power[IWN_RIDX_MAX + 1]; + uint8_t rf_gain[2]; + uint8_t dsp_gain[2]; + } __packed power[IWN_RIDX_MAX + 1]; +} __packed; + +/* structure for command IWN_CMD_TXPOWER_DBM */ +struct iwn5000_cmd_txpower { + int8_t global_limit; /* in half-dBm */ +#define IWN5000_TX_POWER_AUTO 0x7f +#define IWN5000_TX_POWER_MAX_DBM 16 + + uint8_t flags; +#define IWN5000_TX_POWER_NO_CLOSED (1 << 6) + + int8_t srv_limit; /* in half-dBm */ + uint8_t reserved; } __packed; /* structure for command IWN_CMD_BLUETOOTH */ @@ -581,16 +804,38 @@ struct iwn_sensitivity_cmd { /* structure for command IWN_PHY_CALIB */ struct iwn_phy_calib_cmd { uint8_t code; -#define IWN_SET_DIFF_GAIN 7 uint8_t flags; uint16_t reserved1; int8_t gain[3]; -#define IWN_GAIN_SET (1 << 2) - uint8_t reserved2; } __packed; +struct iwn5000_calib { + uint8_t code; +#define IWN4965_SET_DIFF_GAIN 7 +#define IWN5000_PHY_CALIB_LO 9 +#define IWN5000_PHY_CALIB_LO_TX_IQ 11 +#define IWN5000_PHY_CALIB_CRYSTAL 15 +#define IWN5000_PHY_CALIB_LO_TX_IQ_PERD 17 +#define IWN5000_PHY_CALIB_RESET_NOISE_GAIN 18 +#define IWN5000_PHY_CALIB_NOISE_GAIN 19 + + uint8_t group; + uint8_t ngroups; + uint8_t valid; +} __packed; + +/* structure for command IWN_PHY_CALIB */ +struct iwn5000_phy_calib { + uint8_t code; + + uint8_t group; + uint8_t ngroups; + uint8_t valid; + uint8_t data[2]; +} __packed; + /* structure for IWN_UC_READY notification */ #define IWN_NATTEN_GROUPS 5 @@ -616,15 +861,15 @@ struct iwn_ucode_info { int32_t chan20MHz; int32_t chan40MHz; } __packed temp[4]; - int32_t atten[IWN_NATTEN_GROUPS][IWN_NTXCHAINS]; + int32_t atten[IWN_NATTEN_GROUPS][2]; } __packed; /* structure for IWN_TX_DONE notification */ -struct iwn_tx_stat { +struct iwn4965_tx_stat { uint8_t nframes; - uint8_t nkill; - uint8_t nrts; - uint8_t ntries; + uint8_t killcnt; + uint8_t rtscnt; + uint8_t retrycnt; uint8_t rate; uint8_t rflags; uint16_t xrflags; @@ -634,6 +879,25 @@ struct iwn_tx_stat { uint32_t status; } __packed; +struct iwn5000_tx_stat { + uint8_t nframes; + uint8_t killcnt; + uint8_t rtscnt; + uint8_t retrycnt; + uint8_t rate; + uint8_t rflags; + uint16_t xrflags; + uint16_t duration; + uint16_t reserved; + uint32_t power[2]; + uint32_t info; + uint16_t seq; + uint16_t len; + uint32_t tlc; + uint16_t status; + uint16_t sequence; +} __packed; + /* structure for IWN_BEACON_MISSED notification */ struct iwn_beacon_missed { uint32_t consecutive; @@ -648,7 +912,20 @@ struct iwn_rx_ampdu { uint16_t reserved; } __packed; -/* structure for IWN_RX_DONE and IWN_AMPDU_RX_START notifications */ +/* structures for IWN_RX_DONE and IWN_AMPDU_RX_START notifications */ + +struct iwn4965_rx_phystat { + uint16_t antenna; + uint16_t agc; + uint8_t rssi[6]; +} __packed; + +struct iwn5000_rx_phystat { + uint32_t reserved1; + uint32_t agc; + uint16_t rssi[3]; +} __packed; + struct iwn_rx_stat { uint8_t phy_len; uint8_t cfg_phy_len; @@ -660,12 +937,7 @@ struct iwn_rx_stat { uint32_t beacon; uint16_t flags; uint16_t chan; - uint16_t antenna; - uint16_t agc; - uint8_t rssi[6]; -#define IWN_RSSI_TO_DBM 44 - - uint8_t reserved2[22]; + uint8_t phybuf[32]; uint8_t rate; uint8_t rflags; uint16_t xrflags; @@ -673,6 +945,8 @@ struct iwn_rx_stat { uint16_t reserve3; } __packed; +#define IWN_RSSI_TO_DBM 44 + /* structure for IWN_START_SCAN notification */ struct iwn_start_scan { uint64_t tstamp; @@ -819,28 +1093,47 @@ struct iwn_firmware_hdr { uint32_t boot_textsz; } __packed; -#define IWN_FW_MAIN_TEXT_MAXSZ (96 * 1024) -#define IWN_FW_MAIN_DATA_MAXSZ (40 * 1024) -#define IWN_FW_INIT_TEXT_MAXSZ (96 * 1024) -#define IWN_FW_INIT_DATA_MAXSZ (40 * 1024) +#define IWN4965_FW_TEXT_MAXSZ ( 96 * 1024) +#define IWN4965_FW_DATA_MAXSZ ( 40 * 1024) +#define IWN5000_FW_TEXT_MAXSZ (128 * 1024) +#define IWN5000_FW_DATA_MAXSZ ( 48 * 1024) #define IWN_FW_BOOT_TEXT_MAXSZ 1024 - +#define IWN4965_FWSZ (IWN4965_FW_TEXT_MAXSZ + IWN4965_FW_DATA_MAXSZ) +#define IWN5000_FWSZ IWN5000_FW_TEXT_MAXSZ /* * Offsets into EEPROM. */ #define IWN_EEPROM_MAC 0x015 -#define IWN_EEPROM_DOMAIN 0x060 -#define IWN_EEPROM_BAND1 0x063 -#define IWN_EEPROM_BAND2 0x072 -#define IWN_EEPROM_BAND3 0x080 -#define IWN_EEPROM_BAND4 0x08d -#define IWN_EEPROM_BAND5 0x099 -#define IWN_EEPROM_BAND6 0x0a0 -#define IWN_EEPROM_BAND7 0x0a8 -#define IWN_EEPROM_MAXPOW 0x0e8 -#define IWN_EEPROM_VOLTAGE 0x0e9 -#define IWN_EEPROM_BANDS 0x0ea +#define IWN_EEPROM_RFCFG 0x048 +#define IWN4965_EEPROM_DOMAIN 0x060 +#define IWN4965_EEPROM_BAND1 0x063 +#define IWN5000_EEPROM_REG 0x066 +#define IWN5000_EEPROM_CAL 0x067 +#define IWN4965_EEPROM_BAND2 0x072 +#define IWN4965_EEPROM_BAND3 0x080 +#define IWN4965_EEPROM_BAND4 0x08d +#define IWN4965_EEPROM_BAND5 0x099 +#define IWN4965_EEPROM_BAND6 0x0a0 +#define IWN4965_EEPROM_BAND7 0x0a8 +#define IWN4965_EEPROM_MAXPOW 0x0e8 +#define IWN4965_EEPROM_VOLTAGE 0x0e9 +#define IWN4965_EEPROM_BANDS 0x0ea +/* Indirect offsets. */ +#define IWN5000_EEPROM_DOMAIN 0x001 +#define IWN5000_EEPROM_BAND1 0x004 +#define IWN5000_EEPROM_BAND2 0x013 +#define IWN5000_EEPROM_BAND3 0x021 +#define IWN5000_EEPROM_BAND4 0x02e +#define IWN5000_EEPROM_BAND5 0x03a +#define IWN5000_EEPROM_BAND6 0x041 +#define IWN5000_EEPROM_BAND7 0x049 +#define IWN5000_EEPROM_CRYSTAL 0x094 + +/* possible flags for IWN_EEPROM_RFCFG */ +#define IWN_RFCFG_TYPE(x) (((x) >> 0) & 3) +#define IWN_RFCFG_STEP(x) (((x) >> 2) & 3) +#define IWN_RFCFG_DASH(x) (((x) >> 4) & 3) struct iwn_eeprom_chan { uint8_t flags; @@ -853,44 +1146,59 @@ struct iwn_eeprom_chan { } __packed; #define IWN_NSAMPLES 3 -struct iwn_eeprom_chan_samples { +struct iwn4965_eeprom_chan_samples { uint8_t num; struct { uint8_t temp; uint8_t gain; uint8_t power; int8_t pa_det; - } samples[IWN_NTXCHAINS][IWN_NSAMPLES]; + } samples[2][IWN_NSAMPLES]; } __packed; #define IWN_NBANDS 8 -struct iwn_eeprom_band { +struct iwn4965_eeprom_band { uint8_t lo; /* low channel number */ uint8_t hi; /* high channel number */ - struct iwn_eeprom_chan_samples chans[2]; + struct iwn4965_eeprom_chan_samples chans[2]; } __packed; +/* + * Offsets of channels descriptions in EEPROM. + */ +static const uint32_t iwn4965_regulatory_bands[IWN_NBANDS] = { + IWN4965_EEPROM_BAND1, + IWN4965_EEPROM_BAND2, + IWN4965_EEPROM_BAND3, + IWN4965_EEPROM_BAND4, + IWN4965_EEPROM_BAND5, + IWN4965_EEPROM_BAND6, + IWN4965_EEPROM_BAND7 +}; + +static const uint32_t iwn5000_regulatory_bands[IWN_NBANDS] = { + IWN5000_EEPROM_BAND1, + IWN5000_EEPROM_BAND2, + IWN5000_EEPROM_BAND3, + IWN5000_EEPROM_BAND4, + IWN5000_EEPROM_BAND5, + IWN5000_EEPROM_BAND6, + IWN5000_EEPROM_BAND7 +}; + #define IWN_CHAN_BANDS_COUNT 7 #define IWN_MAX_CHAN_PER_BAND 14 static const struct iwn_chan_band { - uint32_t addr; /* offset in EEPROM */ - uint8_t nchan; - uint8_t chan[IWN_MAX_CHAN_PER_BAND]; + uint8_t nchan; + uint8_t chan[IWN_MAX_CHAN_PER_BAND]; } iwn_bands[] = { - { IWN_EEPROM_BAND1, 14, - { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14 } }, - { IWN_EEPROM_BAND2, 13, - { 183, 184, 185, 187, 188, 189, 192, 196, 7, 8, 11, 12, 16 } }, - { IWN_EEPROM_BAND3, 12, - { 34, 36, 38, 40, 42, 44, 46, 48, 52, 56, 60, 64 } }, - { IWN_EEPROM_BAND4, 11, - { 100, 104, 108, 112, 116, 120, 124, 128, 132, 136, 140 } }, - { IWN_EEPROM_BAND5, 6, - { 145, 149, 153, 157, 161, 165 } }, - { IWN_EEPROM_BAND6, 7, - { 1, 2, 3, 4, 5, 6, 7 } }, - { IWN_EEPROM_BAND7, 11, - { 36, 44, 52, 60, 100, 108, 116, 124, 132, 149, 157 } } + { 14, { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14 } }, + { 13, { 183, 184, 185, 187, 188, 189, 192, 196, 7, 8, 11, 12, 16 } }, + { 12, { 34, 36, 38, 40, 42, 44, 46, 48, 52, 56, 60, 64 } }, + { 11, { 100, 104, 108, 112, 116, 120, 124, 128, 132, 136, 140 } }, + { 6, { 145, 149, 153, 157, 161, 165 } }, + { 7, { 1, 2, 3, 4, 5, 6, 7 } }, + { 11, { 36, 44, 52, 60, 100, 108, 116, 124, 132, 149, 157 } } }; static const uint8_t iwn_ridx_to_plcp[] = { @@ -898,13 +1206,13 @@ static const uint8_t iwn_ridx_to_plcp[] = { 0xd, 0xf, 0x5, 0x7, 0x9, 0xb, 0x1, 0x3, 0x3 /* OFDM R1-R4 */ }; -#define IWN_MAX_PWR_INDEX 107 +#define IWN4965_MAX_PWR_INDEX 107 /* * RF Tx gain values from highest to lowest power (values obtained from * the reference driver.) */ -static const uint8_t iwn_rf_gain_2ghz[IWN_MAX_PWR_INDEX + 1] = { +static const uint8_t iwn4965_rf_gain_2ghz[IWN4965_MAX_PWR_INDEX + 1] = { 0x3f, 0x3f, 0x3f, 0x3e, 0x3e, 0x3e, 0x3d, 0x3d, 0x3d, 0x3c, 0x3c, 0x3c, 0x3b, 0x3b, 0x3b, 0x3a, 0x3a, 0x3a, 0x39, 0x39, 0x39, 0x38, 0x38, 0x38, 0x37, 0x37, 0x37, 0x36, 0x36, 0x36, 0x35, 0x35, 0x35, @@ -917,7 +1225,7 @@ static const uint8_t iwn_rf_gain_2ghz[IWN_MAX_PWR_INDEX + 1] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; -static const uint8_t iwn_rf_gain_5ghz[IWN_MAX_PWR_INDEX + 1] = { +static const uint8_t iwn4965_rf_gain_5ghz[IWN4965_MAX_PWR_INDEX + 1] = { 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3e, 0x3e, 0x3e, 0x3d, 0x3d, 0x3d, 0x3c, 0x3c, 0x3c, 0x3b, 0x3b, 0x3b, 0x3a, 0x3a, 0x3a, 0x39, 0x39, 0x39, 0x38, 0x38, 0x38, 0x37, 0x37, 0x37, 0x36, 0x36, 0x36, 0x35, @@ -934,7 +1242,7 @@ static const uint8_t iwn_rf_gain_5ghz[IWN_MAX_PWR_INDEX + 1] = { * DSP pre-DAC gain values from highest to lowest power (values obtained * from the reference driver.) */ -static const uint8_t iwn_dsp_gain_2ghz[IWN_MAX_PWR_INDEX + 1] = { +static const uint8_t iwn4965_dsp_gain_2ghz[IWN4965_MAX_PWR_INDEX + 1] = { 0x6e, 0x68, 0x62, 0x6e, 0x68, 0x62, 0x6e, 0x68, 0x62, 0x6e, 0x68, 0x62, 0x6e, 0x68, 0x62, 0x6e, 0x68, 0x62, 0x6e, 0x68, 0x62, 0x6e, 0x68, 0x62, 0x6e, 0x68, 0x62, 0x6e, 0x68, 0x62, 0x6e, 0x68, 0x62, @@ -947,7 +1255,7 @@ static const uint8_t iwn_dsp_gain_2ghz[IWN_MAX_PWR_INDEX + 1] = { 0x43, 0x42, 0x41, 0x40, 0x3f, 0x3e, 0x3d, 0x3c, 0x3b }; -static const uint8_t iwn_dsp_gain_5ghz[IWN_MAX_PWR_INDEX + 1] = { +static const uint8_t iwn4965_dsp_gain_5ghz[IWN4965_MAX_PWR_INDEX + 1] = { 0x7b, 0x75, 0x6e, 0x68, 0x62, 0x6e, 0x68, 0x62, 0x6e, 0x68, 0x62, 0x6e, 0x68, 0x62, 0x6e, 0x68, 0x62, 0x6e, 0x68, 0x62, 0x6e, 0x68, 0x62, 0x6e, 0x68, 0x62, 0x6e, 0x68, 0x62, 0x6e, 0x68, 0x62, 0x6e, @@ -960,12 +1268,104 @@ static const uint8_t iwn_dsp_gain_5ghz[IWN_MAX_PWR_INDEX + 1] = { 0x68, 0x62, 0x6e, 0x68, 0x62, 0x5d, 0x58, 0x53, 0x4e }; +/* + * Power saving settings (values obtained from the reference driver.) + */ +#define IWN_NDTIMRANGES 3 +#define IWN_NPOWERLEVELS 6 +static const struct iwn_pmgt { + uint32_t rxtimeout; + uint32_t txtimeout; + uint32_t intval[5]; + int skip_dtim; +} iwn_pmgt[IWN_NDTIMRANGES][IWN_NPOWERLEVELS] = { + /* DTIM <= 2 */ + { + { 0, 0, { 0, 0, 0, 0, 0 }, 0 }, /* CAM */ + { 200, 500, { 1, 2, 2, 2, -1 }, 0 }, /* PS level 1 */ + { 200, 300, { 1, 2, 2, 2, -1 }, 0 }, /* PS level 2 */ + { 50, 100, { 2, 2, 2, 2, -1 }, 0 }, /* PS level 3 */ + { 50, 25, { 2, 2, 4, 4, -1 }, 1 }, /* PS level 4 */ + { 25, 25, { 2, 2, 4, 6, -1 }, 2 } /* PS level 5 */ + }, + /* 3 <= DTIM <= 10 */ + { + { 0, 0, { 0, 0, 0, 0, 0 }, 0 }, /* CAM */ + { 200, 500, { 1, 2, 3, 4, 4 }, 0 }, /* PS level 1 */ + { 200, 300, { 1, 2, 3, 4, 7 }, 0 }, /* PS level 2 */ + { 50, 100, { 2, 4, 6, 7, 9 }, 0 }, /* PS level 3 */ + { 50, 25, { 2, 4, 6, 9, 10 }, 1 }, /* PS level 4 */ + { 25, 25, { 2, 4, 7, 10, 10 }, 2 } /* PS level 5 */ + }, + /* DTIM >= 11 */ + { + { 0, 0, { 0, 0, 0, 0, 0 }, 0 }, /* CAM */ + { 200, 500, { 1, 2, 3, 4, -1 }, 0 }, /* PS level 1 */ + { 200, 300, { 2, 4, 6, 7, -1 }, 0 }, /* PS level 2 */ + { 50, 100, { 2, 7, 9, 9, -1 }, 0 }, /* PS level 3 */ + { 50, 25, { 2, 7, 9, 9, -1 }, 0 }, /* PS level 4 */ + { 25, 25, { 4, 7, 10, 10, -1 }, 0 } /* PS level 5 */ + } +}; + +struct iwn_sensitivity_limits { + uint32_t min_ofdm_x1; + uint32_t max_ofdm_x1; + uint32_t min_ofdm_mrc_x1; + uint32_t max_ofdm_mrc_x1; + uint32_t min_ofdm_x4; + uint32_t max_ofdm_x4; + uint32_t min_ofdm_mrc_x4; + uint32_t max_ofdm_mrc_x4; + uint32_t min_cck_x4; + uint32_t max_cck_x4; + uint32_t min_cck_mrc_x4; + uint32_t max_cck_mrc_x4; + uint32_t min_energy_cck; + uint32_t energy_cck; + uint32_t energy_ofdm; +}; + +/* + * RX sensitivity limits (values obtained from the reference driver.) + */ +static const struct iwn_sensitivity_limits iwn4965_sensitivity_limits = { + 105, 140, + 170, 210, + 85, 120, + 170, 210, + 125, 200, + 200, 400, + 97, + 100, + 100 +}; + +static const struct iwn_sensitivity_limits iwn5000_sensitivity_limits = { + 120, 155, + 240, 290, + 90, 120, + 170, 210, + 125, 200, + 170, 400, + 95, + 95, + 95 +}; + +/* Map TID to TX scheduler's FIFO. */ +static const uint8_t iwn_tid2fifo[] = { + 1, 0, 0, 1, 2, 2, 3, 3, 7, 7, 7, 7, 7, 7, 7, 7, 3 +}; + #define IWN_READ(sc, reg) \ bus_space_read_4((sc)->sc_st, (sc)->sc_sh, (reg)) #define IWN_WRITE(sc, reg, val) \ bus_space_write_4((sc)->sc_st, (sc)->sc_sh, (reg), (val)) -#define IWN_WRITE_REGION_4(sc, offset, datap, count) \ - bus_space_write_region_4((sc)->sc_st, (sc)->sc_sh, (offset), \ - (datap), (count)) +#define IWN_SETBITS(sc, reg, mask) \ + IWN_WRITE(sc, reg, IWN_READ(sc, reg) | (mask)) + +#define IWN_CLRBITS(sc, reg, mask) \ + IWN_WRITE(sc, reg, IWN_READ(sc, reg) & ~(mask)) diff --git a/sys/dev/pci/if_iwnvar.h b/sys/dev/pci/if_iwnvar.h index 6c1affe3186..41b04bf0961 100644 --- a/sys/dev/pci/if_iwnvar.h +++ b/sys/dev/pci/if_iwnvar.h @@ -1,7 +1,7 @@ -/* $OpenBSD: if_iwnvar.h,v 1.2 2007/11/19 19:34:25 damien Exp $ */ +/* $OpenBSD: if_iwnvar.h,v 1.3 2008/10/13 16:37:10 damien Exp $ */ /*- - * Copyright (c) 2007 + * Copyright (c) 2007, 2008 * Damien Bergamini <damien.bergamini@free.fr> * * Permission to use, copy, modify, and distribute this software for any @@ -62,6 +62,8 @@ struct iwn_dma_info { struct iwn_tx_data { bus_dmamap_t map; + bus_addr_t cmd_paddr; + bus_addr_t scratch_paddr; struct mbuf *m; struct ieee80211_node *ni; }; @@ -94,8 +96,10 @@ struct iwn_rx_data { struct iwn_rx_ring { struct iwn_dma_info desc_dma; + struct iwn_dma_info stat_dma; struct iwn_dma_info buf_dma; uint32_t *desc; + struct iwn_rx_status *stat; struct iwn_rx_data data[IWN_RX_RING_COUNT]; struct iwn_rbuf rbuf[IWN_RBUF_COUNT]; SLIST_HEAD(, iwn_rbuf) freelist; @@ -105,6 +109,8 @@ struct iwn_rx_ring { struct iwn_node { struct ieee80211_node ni; /* must be the first */ struct ieee80211_amrr_node amn; + uint16_t disable_tid; + uint8_t id; }; struct iwn_calib_state { @@ -116,12 +122,12 @@ struct iwn_calib_state { u_int nbeacons; uint32_t noise[3]; uint32_t rssi[3]; - uint32_t corr_ofdm_x1; - uint32_t corr_ofdm_mrc_x1; - uint32_t corr_ofdm_x4; - uint32_t corr_ofdm_mrc_x4; - uint32_t corr_cck_x4; - uint32_t corr_cck_mrc_x4; + uint32_t ofdm_x1; + uint32_t ofdm_mrc_x1; + uint32_t ofdm_x4; + uint32_t ofdm_mrc_x4; + uint32_t cck_x4; + uint32_t cck_mrc_x4; uint32_t bad_plcp_ofdm; uint32_t fa_ofdm; uint32_t bad_plcp_cck; @@ -140,6 +146,53 @@ struct iwn_calib_state { uint32_t energy_cck; }; +struct iwn_calib_info { + uint8_t *buf; + u_int len; +}; + +struct iwn_fw_part { + const uint8_t *text; + uint32_t textsz; + const uint8_t *data; + uint32_t datasz; +}; + +struct iwn_fw_info { + u_char *data; + struct iwn_fw_part init; + struct iwn_fw_part main; + struct iwn_fw_part boot; +}; + +struct iwn_hal { + int (*load_firmware)(struct iwn_softc *); + void (*read_eeprom)(struct iwn_softc *); + int (*post_alive)(struct iwn_softc *); + int (*apm_init)(struct iwn_softc *); + int (*nic_config)(struct iwn_softc *); + void (*update_sched)(struct iwn_softc *, int, int, uint8_t, + uint16_t); + int (*get_temperature)(struct iwn_softc *); + int (*get_rssi)(const struct iwn_rx_stat *); + int (*set_txpower)(struct iwn_softc *, int); + int (*init_gains)(struct iwn_softc *); + int (*set_gains)(struct iwn_softc *); + int (*add_node)(struct iwn_softc *, struct iwn_node_info *, + int); + void (*tx_done)(struct iwn_softc *, struct iwn_rx_desc *); + const char *fwname; + const struct iwn_sensitivity_limits *limits; + int ntxqs; + uint8_t broadcast_id; + int rxonsz; + int schedsz; + uint32_t fw_text_maxsz; + uint32_t fw_data_maxsz; + uint32_t fwsz; + bus_size_t sched_txfact_addr; +}; + struct iwn_softc { struct device sc_dev; @@ -151,18 +204,26 @@ struct iwn_softc { bus_dma_tag_t sc_dmat; - /* shared area */ - struct iwn_dma_info shared_dma; - struct iwn_shared *shared; + u_int sc_flags; +#define IWN_FLAG_HAS_5GHZ (1 << 0) +#define IWN_FLAG_FIRST_BOOT (1 << 1) + + uint8_t hw_type; + const struct iwn_hal *sc_hal; - /* "keep warm" page */ + /* TX scheduler rings. */ + struct iwn_dma_info sched_dma; + uint16_t *sched; + uint32_t sched_base; + + /* "Keep Warm" page. */ struct iwn_dma_info kw_dma; - /* firmware DMA transfer */ + /* Firmware DMA transfer. */ struct iwn_dma_info fw_dma; - /* rings */ - struct iwn_tx_ring txq[IWN_NTXQUEUES]; + /* TX/RX rings. */ + struct iwn_tx_ring txq[IWN5000_NTXQUEUES]; struct iwn_rx_ring rxq; bus_space_tag_t sc_st; @@ -171,6 +232,7 @@ struct iwn_softc { pci_chipset_tag_t sc_pct; pcitag_t sc_pcitag; bus_size_t sc_sz; + int sc_cap_off; /* PCIe Capabilities. */ struct ksensordev sensordev; struct ksensor sensor; @@ -178,21 +240,33 @@ struct iwn_softc { int calib_cnt; struct iwn_calib_state calib; + struct iwn_fw_info fw; + struct iwn_calib_info calibcmd[3]; + struct iwn_rx_stat last_rx_stat; int last_rx_valid; struct iwn_ucode_info ucode_info; - struct iwn_config config; + struct iwn_rxon rxon; uint32_t rawtemp; int temp; int noise; uint8_t antmsk; + uint32_t qfullmsk; - struct iwn_eeprom_band bands[IWN_NBANDS]; + struct iwn4965_eeprom_band + bands[IWN_NBANDS]; + uint16_t rfcfg; + char eeprom_domain[4]; + uint32_t eeprom_crystal; int16_t eeprom_voltage; int8_t maxpwr2GHz; int8_t maxpwr5GHz; int8_t maxpwr[IEEE80211_CHAN_MAX]; + uint32_t critical_temp; + uint8_t ntxchains; + uint8_t nrxchains; + int sc_tx_timer; void *powerhook; |