/* $NetBSD: if_iwn.c,v 1.78 2016/06/10 13:27:14 ozaki-r Exp $ */
/* $OpenBSD: if_iwn.c,v 1.135 2014/09/10 07:22:09 dcoppa Exp $ */
/*-
* Copyright (c) 2007-2010 Damien Bergamini <damien.bergamini@free.fr>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
/*
* Copyright 2016 Hans Rosenfeld <rosenfeld@grumpf.hope-2000.org>
*/
/*
* Driver for Intel WiFi Link 4965 and 100/1000/2000/5000/6000 Series 802.11
* network adapters.
*/
/*
* TODO:
* - turn tunables into driver properties
*/
#include <net/if_types.h>
#include <netinet/in_systm.h>
#include <sys/mac_provider.h>
#include <sys/mac_wifi.h>
#include <sys/net80211.h>
#include <sys/firmload.h>
#include <sys/sysmacros.h>
#include "if_iwncompat.h"
#include "if_iwnreg.h"
#include "if_iwnvar.h"
#include <inet/wifi_ioctl.h>
#ifdef DEBUG
#define IWN_DEBUG
#endif
/*
* regs access attributes
*/
};
/*
* DMA access attributes for descriptor
*/
};
/*
* DMA access attributes
*/
};
/*
* Supported rates for 802.11a/b/g modes (in 500Kbps unit).
*/
{ 8, { 12, 18, 24, 36, 48, 72, 96, 108 } };
{ 4, { 2, 4, 11, 22 } };
{ 12, { 2, 4, 11, 22, 12, 18, 24, 36, 48, 72, 96, 108 } };
kstat_t **, void **);
static void iwn_kstat_init(struct iwn_softc *);
static void iwn_kstat_init_2000(struct iwn_softc *);
static void iwn_kstat_init_4965(struct iwn_softc *);
static void iwn_kstat_init_6000(struct iwn_softc *);
static void iwn_intr_teardown(struct iwn_softc *);
static int iwn_intr_add(struct iwn_softc *, int);
static int iwn_intr_setup(struct iwn_softc *);
static int iwn4965_attach(struct iwn_softc *);
static int iwn_quiesce(dev_info_t *);
static int iwn_nic_lock(struct iwn_softc *);
static int iwn_eeprom_lock(struct iwn_softc *);
static int iwn_init_otprom(struct iwn_softc *);
static void iwn_dma_contig_free(struct iwn_dma_info *);
static int iwn_alloc_sched(struct iwn_softc *);
static void iwn_free_sched(struct iwn_softc *);
static int iwn_alloc_kw(struct iwn_softc *);
static void iwn_free_kw(struct iwn_softc *);
static int iwn_alloc_ict(struct iwn_softc *);
static void iwn_free_ict(struct iwn_softc *);
static int iwn_alloc_fwmem(struct iwn_softc *);
static void iwn_free_fwmem(struct iwn_softc *);
int);
static void iwn5000_ict_reset(struct iwn_softc *);
static int iwn_read_eeprom(struct iwn_softc *);
static void iwn4965_read_eeprom(struct iwn_softc *);
#ifdef IWN_DEBUG
static void iwn4965_print_power_group(struct iwn_softc *, int);
#endif
static void iwn5000_read_eeprom(struct iwn_softc *);
static void iwn_read_eeprom_enhinfo(struct iwn_softc *);
static void iwn_node_free(ieee80211_node_t *);
static void iwn_newassoc(struct ieee80211_node *, int);
static void iwn_iter_func(void *, struct ieee80211_node *);
static void iwn_calib_timeout(void *);
struct iwn_rx_data *);
struct iwn_rx_data *);
#ifndef IEEE80211_NO_HT
struct iwn_rx_data *);
#endif
static void iwn5000_rx_calib_results(struct iwn_softc *,
struct iwn_rx_desc *, struct iwn_rx_data *);
struct iwn_rx_data *);
struct iwn_rx_data *);
struct iwn_rx_data *);
uint8_t);
static void iwn_notif_intr(struct iwn_softc *);
static void iwn_wakeup_intr(struct iwn_softc *);
static void iwn_fatal_intr(struct iwn_softc *);
uint16_t);
uint16_t);
#ifdef notyet
static void iwn5000_reset_sched(struct iwn_softc *, int, int);
#endif
static void iwn_watchdog(void *);
int);
int);
static int iwn_set_link_quality(struct iwn_softc *,
struct ieee80211_node *);
static int iwn_add_broadcast_node(struct iwn_softc *, int);
static int iwn_set_critical_temp(struct iwn_softc *);
static void iwn4965_power_calibration(struct iwn_softc *, int);
static int iwn4965_set_txpower(struct iwn_softc *, int);
static int iwn5000_set_txpower(struct iwn_softc *, int);
static int iwn4965_get_rssi(const struct iwn_rx_stat *);
static int iwn5000_get_rssi(const struct iwn_rx_stat *);
static int iwn_get_noise(const struct iwn_rx_general_stats *);
static int iwn4965_get_temperature(struct iwn_softc *);
static int iwn5000_get_temperature(struct iwn_softc *);
static int iwn_init_sensitivity(struct iwn_softc *);
static void iwn_collect_noise(struct iwn_softc *,
const struct iwn_rx_general_stats *);
static int iwn4965_init_gains(struct iwn_softc *);
static int iwn5000_init_gains(struct iwn_softc *);
static int iwn4965_set_gains(struct iwn_softc *);
static int iwn5000_set_gains(struct iwn_softc *);
static void iwn_tune_sensitivity(struct iwn_softc *,
const struct iwn_rx_stats *);
static int iwn_send_sensitivity(struct iwn_softc *);
static int iwn_set_pslevel(struct iwn_softc *, int, int, int);
static int iwn5000_runtime_calib(struct iwn_softc *);
static int iwn_config_bt_coex_bluetooth(struct iwn_softc *);
static int iwn_config_bt_coex_prio_table(struct iwn_softc *);
static int iwn_config_bt_coex_adv1(struct iwn_softc *);
static int iwn_config_bt_coex_adv2(struct iwn_softc *);
static int iwn_config(struct iwn_softc *);
uint8_t);
#ifdef IWN_HWCRYPTO
struct ieee80211_key *);
struct ieee80211_key *);
#endif
static int iwn_wme_update(struct ieee80211com *);
#ifndef IEEE80211_NO_HT
static int iwn_ampdu_rx_start(struct ieee80211com *,
struct ieee80211_node *, uint8_t);
static void iwn_ampdu_rx_stop(struct ieee80211com *,
struct ieee80211_node *, uint8_t);
static int iwn_ampdu_tx_start(struct ieee80211com *,
struct ieee80211_node *, uint8_t);
static void iwn_ampdu_tx_stop(struct ieee80211com *,
struct ieee80211_node *, uint8_t);
static void iwn4965_ampdu_tx_start(struct iwn_softc *,
static void iwn4965_ampdu_tx_stop(struct iwn_softc *,
static void iwn5000_ampdu_tx_start(struct iwn_softc *,
static void iwn5000_ampdu_tx_stop(struct iwn_softc *,
#endif
static int iwn5000_query_calibration(struct iwn_softc *);
static int iwn5000_send_calibration(struct iwn_softc *);
static int iwn5000_send_wimax_coex(struct iwn_softc *);
static int iwn6000_temp_offset_calib(struct iwn_softc *);
static int iwn2000_temp_offset_calib(struct iwn_softc *);
static int iwn4965_post_alive(struct iwn_softc *);
static int iwn5000_post_alive(struct iwn_softc *);
int);
static int iwn4965_load_firmware(struct iwn_softc *);
const uint8_t *, int);
static int iwn5000_load_firmware(struct iwn_softc *);
static int iwn_read_firmware_leg(struct iwn_softc *,
struct iwn_fw_info *);
static int iwn_read_firmware_tlv(struct iwn_softc *,
struct iwn_fw_info *, uint16_t);
static int iwn_read_firmware(struct iwn_softc *);
static int iwn_clock_wait(struct iwn_softc *);
static int iwn_apm_init(struct iwn_softc *);
static void iwn_apm_stop_master(struct iwn_softc *);
static void iwn_apm_stop(struct iwn_softc *);
static int iwn4965_nic_config(struct iwn_softc *);
static int iwn5000_nic_config(struct iwn_softc *);
static int iwn_hw_prepare(struct iwn_softc *);
static int iwn_hw_init(struct iwn_softc *);
static void iwn_abort_scan(void *);
static void iwn_periodic(void *);
static int iwn_fast_recover(struct iwn_softc *);
const struct ieee80211_rateset *);
const struct ieee80211_rateset *);
struct iwn_rx_stat *);
#ifdef IWN_DEBUG
static int iwn_dbg_print = 0;
static void
{
if (iwn_dbg_print != 0) {
}
}
#else
#define IWN_DBG(...)
#endif
/*
* tunables
*/
/*
* enable 5GHz scanning
*/
/*
* If more than 50 consecutive beacons are missed,
* we've probably lost our connection.
* If more than 5 consecutive beacons are missed,
* reinitialize the sensitivity state machine.
*/
/*
* iwn_periodic interval, in units of msec
*/
/*
* scan timeout in sec
*/
/*
* Mac Call Back entries
*/
static int iwn_m_start(void *);
static void iwn_m_stop(void *);
static int iwn_m_unicst(void *, const uint8_t *);
static int iwn_m_promisc(void *, boolean_t);
const void *);
void *);
static void iwn_m_propinfo(void *, const char *, mac_prop_id_t,
.mc_getstat = iwn_m_stat,
.mc_start = iwn_m_start,
.mc_stop = iwn_m_stop,
.mc_reserved = NULL,
.mc_ioctl = iwn_m_ioctl,
.mc_getcapab = NULL,
};
static inline uint32_t
{
/*LINTED: E_PTR_BAD_CAST_ALIGN*/
}
static inline void
{
/*LINTED: E_PTR_BAD_CAST_ALIGN*/
}
static inline void
{
}
static void
{
size / sizeof (kstat_named_t), 0);
else
}
static void
{
}
static void
{
"temperature", KSTAT_DATA_ULONG);
"critical temperature", KSTAT_DATA_ULONG);
"power saving level", KSTAT_DATA_ULONG);
"noise", KSTAT_DATA_LONG);
"TX mask", KSTAT_DATA_ULONG);
"RX mask", KSTAT_DATA_ULONG);
"connected mask", KSTAT_DATA_ULONG);
"gain A", KSTAT_DATA_ULONG);
"gain B", KSTAT_DATA_ULONG);
"gain C", KSTAT_DATA_ULONG);
"OFDM X1", KSTAT_DATA_ULONG);
"OFDM MRC X1", KSTAT_DATA_ULONG);
"OFDM X4", KSTAT_DATA_ULONG);
"OFDM MRC X4", KSTAT_DATA_ULONG);
"CCK X4", KSTAT_DATA_ULONG);
"CCK MRC X4", KSTAT_DATA_ULONG);
"energy CCK", KSTAT_DATA_ULONG);
"bintval", KSTAT_DATA_ULONG);
"timestamp", KSTAT_DATA_ULONGLONG);
"init", KSTAT_DATA_ULONG);
"background cwmin", KSTAT_DATA_ULONG);
"background cwmax", KSTAT_DATA_ULONG);
"background aifsn", KSTAT_DATA_ULONG);
"background txop", KSTAT_DATA_ULONG);
"best effort cwmin", KSTAT_DATA_ULONG);
"best effort cwmax", KSTAT_DATA_ULONG);
"best effort aifsn", KSTAT_DATA_ULONG);
"best effort txop", KSTAT_DATA_ULONG);
"video cwmin", KSTAT_DATA_ULONG);
"video cwmax", KSTAT_DATA_ULONG);
"video aifsn", KSTAT_DATA_ULONG);
"video txop", KSTAT_DATA_ULONG);
"voice cwmin", KSTAT_DATA_ULONG);
"voice cwmax", KSTAT_DATA_ULONG);
"voice aifsn", KSTAT_DATA_ULONG);
"voice txop", KSTAT_DATA_ULONG);
}
static void
{
"temperature offset low", KSTAT_DATA_LONG);
"temperature offset high", KSTAT_DATA_LONG);
"reference voltage", KSTAT_DATA_LONG);
}
static void
{
int i, r;
"voltage comp", KSTAT_DATA_LONG);
"channel", KSTAT_DATA_LONG);
"attenuation group", KSTAT_DATA_LONG);
"sub-band", KSTAT_DATA_LONG);
for (i = 0; i != 2; i++) {
"Ant %d temperature compensation", i);
for (r = 0; r != IWN_RIDX_MAX; r++) {
"Ant %d Rate %d RF gain", i, r);
"Ant %d Rate %d DSP gain", i, r);
}
}
}
static void
{
"temperature offset", KSTAT_DATA_LONG);
}
static void
{
sc->sc_intr_count);
} else {
}
sc->sc_intr_size = 0;
}
}
static int
{
int ret;
char *func;
return (DDI_FAILURE);
return (DDI_FAILURE);
if (ret != DDI_SUCCESS) {
return (DDI_FAILURE);
}
if (ret != DDI_SUCCESS) {
return (DDI_FAILURE);
}
NULL);
if (ret != DDI_SUCCESS) {
return (DDI_FAILURE);
}
if (ret != DDI_SUCCESS) {
return (DDI_FAILURE);
}
sc->sc_intr_count);
func = "ddi_intr_enable_block";
} else {
func = "ddi_intr_enable";
}
if (ret != DDI_SUCCESS) {
return (DDI_FAILURE);
}
return (DDI_SUCCESS);
}
static int
{
int intr_type;
int ret;
if (ret != DDI_SUCCESS) {
"!ddi_intr_get_supported_types() failed");
return (DDI_FAILURE);
}
if ((intr_type & DDI_INTR_TYPE_MSIX)) {
return (DDI_SUCCESS);
}
if ((intr_type & DDI_INTR_TYPE_MSI)) {
return (DDI_SUCCESS);
}
if ((intr_type & DDI_INTR_TYPE_FIXED)) {
return (DDI_SUCCESS);
}
return (DDI_FAILURE);
}
static int
{
if (val == 0xff)
return (DDI_FAILURE);
continue;
return (DDI_SUCCESS);
}
return (DDI_FAILURE);
}
static int
{
int instance;
int i, error;
switch (cmd) {
case DDI_ATTACH:
break;
case DDI_RESUME:
instance);
}
return (DDI_SUCCESS);
default:
return (DDI_FAILURE);
}
return (DDI_FAILURE);
}
goto fail_pci_config;
}
/*
* Get the offset of the PCI Express Capability Structure in PCI
* Configuration Space.
*/
&sc->sc_cap_off);
if (error != DDI_SUCCESS) {
"!PCIe capability structure not found!");
goto fail_pci_capab;
}
/* Clear device-specific "PCI retry timeout" register (41h). */
if (reg)
if (error != DDI_SUCCESS) {
goto fail_regs_map;
}
/* Clear pending interrupts. */
/* Disable all interrupts. */
/* Install interrupt handler. */
goto fail_intr;
/* Read hardware revision and attach. */
else
if (error != 0) {
goto fail_hw;
}
goto fail_hw;
}
/* Read MAC address, channels, etc from EEPROM. */
goto fail_hw;
}
/* Allocate DMA memory for firmware transfers. */
"!could not allocate memory for firmware");
goto fail_fwmem;
}
/* Allocate "Keep Warm" page. */
"!could not allocate keep warm page");
goto fail_kw;
}
/* Allocate ICT table for 5000 Series. */
goto fail_ict;
}
/* Allocate TX scheduler "rings". */
"!could not allocate TX scheduler rings");
goto fail_sched;
}
/* Allocate TX rings (16 on 4965AGN, 20 on >=5000). */
"!could not allocate TX ring %d", i);
while (--i >= 0)
goto fail_txring;
}
}
/* Allocate RX ring. */
goto fail_rxring;
}
/* Clear pending interrupts. */
/* Count the number of available chains. */
/* Set device capabilities. */
/* XXX OpenBSD has IEEE80211_C_WEP, IEEE80211_C_RSN,
* and IEEE80211_C_PMGT too. */
IEEE80211_C_IBSS | /* IBSS mode support */
IEEE80211_C_WPA | /* 802.11i */
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 */
IEEE80211_C_WME; /* 802.11e */
#ifndef IEEE80211_NO_HT
/* Set HT capabilities. */
#if IWN_RBUF_SIZE == 8192
#endif
else
}
#endif /* !IEEE80211_NO_HT */
/* Set supported legacy rates. */
}
#ifndef IEEE80211_NO_HT
/* Set supported HT rates. */
}
#endif
/* IBSS channel undefined for now. */
#ifdef IWN_HWCRYPTO
#endif
#ifndef IEEE80211_NO_HT
#endif
/*
* attach to 802.11 module
*/
/* Override 802.11 state transition machine. */
/*
* initialize default tx key
*/
ic->ic_def_txkey = 0;
/*
* Initialize pointer to device specific functions
*/
/*
* create relation to GLD
*/
goto fail_mac_alloc;
}
/*
* Register the macp to mac
*/
if (error != DDI_SUCCESS) {
goto fail_mac_alloc;
}
/*
* Create minor node of type DDI_NT_NET_WIFI
*/
if (error != DDI_SUCCESS) {
goto fail_minor;
}
/*
* Notify link is down now
*/
iwn_periodic_interval * MICROSEC, 0);
if (sc->sc_ks_misc)
if (sc->sc_ks_sens)
if (sc->sc_ks_timing)
if (sc->sc_ks_edca)
if (sc->sc_ks_txpower)
if (sc->sc_ks_toff)
return (DDI_SUCCESS);
/* Free allocated memory if something failed during attachment. */
sizeof (struct iwn_ks_txpower));
sizeof (struct iwn_ks_toff_6000));
else
sizeof (struct iwn_ks_toff_2000));
sizeof (struct iwn_ks_misc));
sizeof (struct iwn_ks_ant));
sizeof (struct iwn_ks_sens));
sizeof (struct iwn_ks_timing));
sizeof (struct iwn_ks_edca));
return (DDI_FAILURE);
}
int
{
#ifndef IEEE80211_NO_HT
#endif
/* Override chains masks, ROM is known to be broken. */
return 0;
}
int
{
#ifndef IEEE80211_NO_HT
#endif
case IWN_HW_REV_TYPE_5100:
/* Override chains masks, ROM is known to be broken. */
break;
case IWN_HW_REV_TYPE_5150:
break;
case IWN_HW_REV_TYPE_5300:
case IWN_HW_REV_TYPE_5350:
break;
case IWN_HW_REV_TYPE_1000:
if (pid == PCI_PRODUCT_INTEL_WIFI_LINK_100_1 ||
else
break;
case IWN_HW_REV_TYPE_6000:
if (pid == PCI_PRODUCT_INTEL_WIFI_LINK_6000_IPA_1 ||
/* Override chains masks, ROM is known to be broken. */
}
break;
case IWN_HW_REV_TYPE_6050:
break;
case IWN_HW_REV_TYPE_6005:
/* Type 6030 cards return IWN_HW_REV_TYPE_6005 */
if (pid == PCI_PRODUCT_INTEL_WIFI_LINK_1030_1 ||
}
else
sizeof (struct iwn_ks_toff_6000),
break;
case IWN_HW_REV_TYPE_2030:
sizeof (struct iwn_ks_toff_2000),
break;
case IWN_HW_REV_TYPE_2000:
sizeof (struct iwn_ks_toff_2000),
break;
case IWN_HW_REV_TYPE_135:
sizeof (struct iwn_ks_toff_2000),
break;
case IWN_HW_REV_TYPE_105:
sizeof (struct iwn_ks_toff_2000),
break;
default:
return ENOTSUP;
}
return 0;
}
static int
{
switch (cmd) {
case DDI_DETACH:
break;
case DDI_SUSPEND:
}
return (DDI_SUCCESS);
default:
return (DDI_FAILURE);
}
return (DDI_FAILURE);
}
if (error != DDI_SUCCESS)
return (error);
/*
* stop chipset
*/
/*
* Unregister from GLD
*/
/* Uninstall interrupt handler. */
/* Free DMA resources. */
sizeof (struct iwn_ks_misc));
sizeof (struct iwn_ks_ant));
sizeof (struct iwn_ks_sens));
sizeof (struct iwn_ks_timing));
sizeof (struct iwn_ks_edca));
sizeof (struct iwn_ks_txpower));
sizeof (struct iwn_ks_toff_6000));
else
sizeof (struct iwn_ks_toff_2000));
return 0;
}
static int
{
return (DDI_FAILURE);
#ifdef IWN_DEBUG
/* bypass any messages */
iwn_dbg_print = 0;
#endif
/*
* No more blocking is allowed while we are in the
* quiesce(9E) entry point.
*/
/*
* Disable and mask all interrupts.
*/
return (DDI_SUCCESS);
}
static int
{
int ntries;
/* Request exclusive access to NIC. */
/* Spin until we actually get the lock. */
return 0;
DELAY(10);
}
return ETIMEDOUT;
}
static __inline void
{
}
{
}
static __inline void
{
}
static __inline void
{
}
static __inline void
{
}
static __inline void
{
}
{
}
static __inline void
{
}
#ifndef IEEE80211_NO_HT
static __inline void
{
if (addr & 3)
else
}
#endif
static __inline void
int count)
{
}
static __inline void
int count)
{
}
static int
{
int i, ntries;
for (i = 0; i < 100; i++) {
/* Request exclusive access to EEPROM. */
/* Spin until we actually get the lock. */
return 0;
DELAY(10);
}
}
return ETIMEDOUT;
}
static __inline void
{
}
/*
* Initialize access by host to One Time Programmable ROM.
* NB: This kind of ROM can be found on 1000 or 6000 Series only.
*/
static int
{
/* Wait for clock stabilization before accessing prph. */
return error;
return error;
DELAY(5);
/* Set auto clock gate disable bit for HW with OTP shadow RAM. */
}
/* Clear ECC status. */
/*
* Find the block before last block (contains the EEPROM image)
* for HW without OTP shadow RAM.
*/
/* Switch to absolute addressing mode. */
base = 0;
if (error != 0)
return error;
if (next == 0) /* End of linked-list. */
break;
}
return EIO;
/* Skip "next" word. */
}
return 0;
}
static int
{
int ntries;
if (val & IWN_EEPROM_READ_VALID)
break;
DELAY(5);
}
if (ntries == 10) {
"!timeout reading ROM at 0x%x", addr);
return ETIMEDOUT;
}
/* OTPROM, check for ECC errors. */
if (tmp & IWN_OTP_GP_ECC_UNCORR_STTS) {
"!OTPROM ECC error at 0x%x", addr);
return EIO;
}
if (tmp & IWN_OTP_GP_ECC_CORR_STTS) {
/* Correctable ECC error, clear bit. */
}
}
if (count > 1)
}
return 0;
}
static int
{
.dma_attr_addr_lo = 0,
.dma_attr_addr_hi = 0xfffffffffULL,
.dma_attr_count_max = 0xfffffffffULL,
.dma_attr_align = align,
.dma_attr_burstsizes = 0x7ff,
.dma_attr_minxfer = 1,
.dma_attr_maxxfer = 0xfffffffffULL,
.dma_attr_seg = 0xfffffffffULL,
.dma_attr_sgllen = 1,
.dma_attr_granular = 1,
.dma_attr_flags = 0,
};
int error;
if (error != DDI_SUCCESS) {
"ddi_dma_alloc_handle() failed, error = %d", error);
goto fail;
}
if (error != DDI_SUCCESS) {
"ddi_dma_mem_alloc() failed, error = %d", error);
goto fail2;
}
if (error != DDI_DMA_MAPPED) {
"ddi_dma_addr_bind_handle() failed, error = %d", error);
goto fail3;
}
return (DDI_SUCCESS);
fail:
return (DDI_FAILURE);
}
static void
{
}
}
static int
{
/* TX scheduler rings must be aligned on a 1KB boundary. */
&iwn_dma_accattr, 1024);
}
static void
{
}
static int
{
/* "Keep Warm" page must be aligned on a 4KB boundary. */
}
static void
{
}
static int
{
/* ICT table must be aligned on a 4KB boundary. */
&iwn_dma_descattr, 4096);
}
static void
{
}
static int
{
/* Must be aligned on a 16-byte boundary. */
}
static void
{
}
static int
{
int i, error;
/* Allocate RX descriptors (256-byte aligned). */
&iwn_dma_descattr, 256);
if (error != DDI_SUCCESS) {
"!could not allocate RX ring DMA memory");
goto fail;
}
/* Allocate RX status area (16-byte aligned). */
if (error != DDI_SUCCESS) {
"!could not allocate RX status DMA memory");
goto fail;
}
/*
* Allocate and map RX buffers.
*/
for (i = 0; i < IWN_RX_RING_COUNT; i++) {
256);
if (error != DDI_SUCCESS) {
"!could not create RX buf DMA map");
goto fail;
}
/* Set physical address of RX buffer (256-byte aligned). */
}
return 0;
return error;
}
static void
{
int ntries;
if (iwn_nic_lock(sc) == 0) {
break;
DELAY(10);
}
}
sc->last_rx_valid = 0;
}
static void
{
int i;
for (i = 0; i < IWN_RX_RING_COUNT; i++) {
}
}
static int
{
int i, error;
/* Allocate TX descriptors (256-byte aligned). */
&iwn_dma_descattr, 256);
if (error != DDI_SUCCESS) {
"!could not allocate TX ring DMA memory");
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;
&iwn_dma_accattr, 4);
if (error != DDI_SUCCESS) {
"!could not allocate TX cmd DMA memory");
goto fail;
}
for (i = 0; i < IWN_TX_RING_COUNT; i++) {
paddr += sizeof (struct iwn_tx_cmd);
256);
if (error != DDI_SUCCESS) {
"!could not create TX buf DMA map");
goto fail;
}
}
return 0;
return error;
}
static void
{
int i;
for (i = 0; i < IWN_TX_RING_COUNT; i++) {
}
/* Clear TX descriptors. */
}
static void
{
int i;
for (i = 0; i < IWN_TX_RING_COUNT; i++) {
}
}
static void
{
/* Disable interrupts. */
/* Reset ICT table. */
/* Set physical address of ICT table (4KB aligned). */
/* Enable periodic RX interrupt. */
/* Switch to ICT interrupt mode in driver. */
/* Re-enable interrupts. */
}
static int
{
int error;
/* Check whether adapter has an EEPROM or an OTPROM. */
IWN_DBG("%s found",
/* Adapter has to be powered on for EEPROM access to work. */
"!could not power ON adapter");
return error;
}
"!bad ROM signature");
return EIO;
}
"!could not lock ROM (error=%d)", error);
return error;
}
"!could not initialize OTPROM");
return error;
}
}
/* Check if HT support is bonded out. */
if (sc->txchainmask == 0)
if (sc->rxchainmask == 0)
/* Read MAC address. */
/* Read adapter-specific information from EEPROM. */
return 0;
}
static void
{
int i;
/* Read regulatory domain (4 ASCII characters). */
/* Read the list of authorized channels (20MHz ones only). */
for (i = 0; i < 5; i++) {
addr = iwn4965_regulatory_bands[i];
}
/* Read maximum allowed TX power for 2GHz and 5GHz bands. */
/* Check that EEPROM values are within valid range. */
/* Read samples for each TX power group. */
/* Read voltage at which samples were taken. */
#ifdef IWN_DEBUG
/* Print samples. */
if (iwn_dbg_print != 0) {
for (i = 0; i < IWN_NBANDS; i++)
}
#endif
}
#ifdef IWN_DEBUG
static void
{
int j, c;
for (c = 0; c < 2; c++) {
for (j = 0; j < IWN_NSAMPLES; j++) {
"temp=%d gain=%d power=%d pa_det=%d", c, j,
}
}
for (c = 0; c < 2; c++) {
for (j = 0; j < IWN_NSAMPLES; j++) {
"temp=%d gain=%d power=%d pa_det=%d", c, j,
}
}
}
#endif
static void
{
int i;
/* Read regulatory domain (4 ASCII characters). */
/* Read the list of authorized channels (20MHz ones only). */
for (i = 0; i < 5; i++) {
}
/* Read enhanced TX power information for 6000 Series. */
IWN_DBG("calib version=%u pa type=%u voltage=%u",
}
/* Compute temperature offset. */
IWN_DBG("temp=%d volt=%d offset=%dK",
} else {
/* Read crystal calibration. */
IWN_DBG("crystal calibration 0x%08x",
}
}
static void
{
int i;
continue;
if (n == 0) { /* 2GHz band */
} else { /* 5GHz band */
/*
* Some adapters support channels 7, 8, 11 and 12
* both in the 2GHz and 4.9GHz bands.
* Because of limitations in our net80211 layer,
* we don't support them in the 4.9GHz band.
*/
if (chan <= 14)
continue;
/* We have at least one valid 5GHz channel. */
}
/* Is active scan allowed on this channel? */
}
/* Save maximum allowed TX power for this channel. */
IWN_DBG("adding chan %d flags=0x%x maxpwr=%d",
}
}
static void
{
int i;
for (i = 0; i < __arraycount(enhinfo); i++) {
continue; /* Skip invalid entries. */
maxpwr = 0;
}
}
static struct ieee80211_node *
{
}
static void
{
}
static void
{
int i;
/*
*/
/* Map 802.11 rate to HW rate index. */
break;
}
}
static int
{
int error;
enum ieee80211_state, ostate,
const char *, ieee80211_state_name[ostate],
enum ieee80211_state, nstate,
const char *, ieee80211_state_name[nstate]);
return (IWN_FAIL);
}
nstate != IEEE80211_S_INIT) {
return (IWN_FAIL);
}
switch (nstate) {
case IEEE80211_S_SCAN:
/* XXX Do not abort a running scan. */
" while scanning(%d) ignored", nstate,
ostate);
return (0);
}
/* XXX Not sure if call and flags are needed. */
/* Make the link LED blink while we're scanning. */
if (error != 0) {
"!could not initiate scan");
return (error);
}
return (error);
case IEEE80211_S_ASSOC:
if (ostate != IEEE80211_S_RUN) {
break;
}
/* FALLTHROUGH */
case IEEE80211_S_AUTH:
/* Reset state to handle reassociations correctly. */
"!could not move to auth state");
return error;
}
break;
case IEEE80211_S_RUN:
"!could not move to run state");
return error;
}
break;
case IEEE80211_S_INIT:
/*
* set LED off after init
*/
break;
}
if (nstate == IEEE80211_S_RUN)
return (error);
}
static void
{
}
static void
{
else
}
/* Force automatic TX power calibration every 60 secs. */
sizeof flags, 1);
}
/* Automatic rate control triggered every 500ms. */
drv_usectohz(500000));
}
/*
* Process an RX_PHY firmware notification. This is usually immediately
* followed by an MPDU_RX_DONE notification.
*/
static void
struct iwn_rx_data *data)
{
sizeof (*stat), DDI_DMA_SYNC_FORKERNEL);
/* Save RX statistics, they will be used on MPDU_RX_DONE. */
}
/*
* Process an RX_DONE (4965AGN only) or MPDU_RX_DONE firmware notification.
* Each MPDU_RX_DONE notification must be preceded by an RX_PHY one.
*/
static void
struct iwn_rx_data *data)
{
mblk_t *m;
char *head;
/* Check for prior RX_PHY notification. */
if (!sc->last_rx_valid) {
"missing RX_PHY");
return;
}
sc->last_rx_valid = 0;
} else
"!invalid RX statistic header");
return;
}
} else {
}
/*LINTED: E_PTR_BAD_CAST_ALIGN*/
/* Discard frames with a bad FCS early. */
return;
}
/* Discard frames that are too short. */
return;
}
if (m == NULL) {
sc->sc_rx_nobuf++;
return;
}
/* Update RX descriptor. */
/* Grab a reference to the source node. */
/* XXX OpenBSD adds decryption here (see also comments in iwn_tx). */
/* NetBSD does decryption in ieee80211_input. */
/*
* convert dBm to percentage
*/
/ (75 * 75);
if (rssi > 100)
rssi = 100;
else if (rssi < 1)
rssi = 1;
/* XXX Added for NetBSD: scans never stop without it */
/* Send the frame to the 802.11 layer. */
/* Node is no longer needed. */
}
#ifndef IEEE80211_NO_HT
/* Process an incoming Compressed BlockAck. */
static void
struct iwn_rx_data *data)
{
sizeof (*ba), DDI_DMA_SYNC_FORKERNEL);
/* XXX TBD */
}
#endif
/*
* Process a CALIBRATION_RESULT notification sent by the initialization
* firmware on response to a CMD_CALIB_CONFIG command (5000 only).
*/
static void
struct iwn_rx_data *data)
{
/* Runtime firmware should not send such a notification. */
return;
case IWN5000_PHY_CALIB_DC:
idx = 0;
break;
case IWN5000_PHY_CALIB_LO:
idx = 1;
break;
case IWN5000_PHY_CALIB_TX_IQ:
idx = 2;
break;
idx = 3;
break;
idx = 4;
break;
}
return;
/* Save calibration result. */
return;
}
}
/*
* Process an RX_STATISTICS or BEACON_STATISTICS firmware notification.
* The latter is sent by the firmware after each received beacon.
*/
static void
struct iwn_rx_data *data)
{
int temp = 0;
/* Ignore statistics received during a scan. */
return;
sizeof (*stats), DDI_DMA_SYNC_FORKERNEL);
/* Test if temperature has changed. */
/* Convert "raw" temperature to degC. */
/* Update TX power if need be (4965AGN only). */
}
return; /* Reply to a statistics request. */
/* Test that RSSI and noise are present in stats report. */
return;
}
/*
* XXX Differential gain calibration makes the 6005 firmware
* crap out, so skip it for now. This effectively disables
* sensitivity tuning as well.
*/
return;
}
/*
* Process a TX_DONE firmware notification. Unfortunately, the 4965AGN
* and 5000 adapters have different incompatible TX status formats.
*/
static void
struct iwn_rx_data *data)
{
sizeof (*stat), DDI_DMA_SYNC_FORKERNEL);
}
static void
struct iwn_rx_data *data)
{
#ifdef notyet
/* Reset TX scheduler slot. */
#endif
sizeof (*stat), DDI_DMA_SYNC_FORKERNEL);
}
/*
* Adapter-independent backend for TX_DONE firmware notifications.
*/
static void
{
/* Update rate control statistics. */
if (ackfailcnt > 0)
else
sc->sc_tx_timer = 0;
}
}
/*
* Process a "command done" firmware notification. This is where we wakeup
* processes waiting for a synchronous command completion.
*/
static void
{
return; /* Not a command ack. */
/* If the command was mapped in an extra buffer, free it. */
}
}
/*
* Process an INT_FH_RX or INT_SW_RX interrupt.
*/
static void
{
case IWN_RX_PHY:
break;
case IWN_RX_DONE: /* 4965AGN only. */
case IWN_MPDU_RX_DONE:
/* An 802.11 frame has been received. */
break;
#ifndef IEEE80211_NO_HT
case IWN_RX_COMPRESSED_BA:
/* A Compressed BlockAck has been received. */
break;
#endif
case IWN_TX_DONE:
/* An 802.11 frame has been transmitted. */
break;
case IWN_RX_STATISTICS:
case IWN_BEACON_STATISTICS:
break;
case IWN_BEACON_MISSED:
{
/*
* If more than iwn_beacons_missed_disconnect
* consecutive beacons are missed, we've probably lost
* our connection.
* If more than iwn_beacons_missed_sensitivity
* consecutive beacons are missed, reinitialize the
* sensitivity state machine.
*/
struct iwn_beacon_missed *, miss);
"!iwn_notif_intr(): %d consecutive "
"beacons missed, disconnecting",
IEEE80211_S_INIT, -1);
(void)iwn_init_sensitivity(sc);
}
}
break;
}
case IWN_UC_READY:
{
/* The microcontroller is ready. */
"!microcontroller initialization failed");
break;
}
/* Save microcontroller report. */
}
/* Save the address of the error log in SRAM. */
break;
}
case IWN_STATE_CHANGED:
{
/*LINTED: E_PTR_BAD_CAST_ALIGN*/
/* The radio button has to be pushed. */
"!Radio transmitter is off");
/* Turn the interface down. */
IEEE80211_S_INIT, -1);
return; /* No further processing. */
}
break;
}
case IWN_START_SCAN:
{
/* Fix current channel. */
break;
}
case IWN_STOP_SCAN:
{
if (iwn_enable_5ghz != 0 &&
/*
* We just finished scanning 2GHz channels,
* start scanning 5GHz ones.
*/
break;
}
}
break;
}
break;
case IWN5000_CALIBRATION_DONE:
break;
}
}
/* Tell the firmware what we have processed. */
}
/*
* Process an INT_WAKEUP interrupt raised when the microcontroller wakes up
* from power-down sleep mode.
*/
static void
{
int qid;
/* Wakeup RX and TX rings. */
}
}
/*
* Dump the error log of the firmware when a firmware panic occurs. Although
* we can't debug the firmware because it is neither open source nor free, it
* can help us to identify certain classes of problems.
*/
static void
{
int i;
/* Force a complete recalibration on next init. */
/* Check that the error log address is valid. */
return;
}
if (iwn_nic_lock(sc) != 0) {
"!could not read firmware error log");
return;
}
/* Read firmware error log from SRAM. */
/*LINTED: E_PTR_BAD_CAST_ALIGN*/
"!firmware error log is empty");
return;
}
/* Dump driver status (TX and RX rings) while we're here. */
"! tx ring %2d: qid=%2d cur=%3d queued=%3d",
}
}
/*ARGSUSED1*/
static uint_t
{
/*LINTED: E_PTR_BAD_CAST_ALIGN*/
return (DDI_INTR_UNCLAIMED);
/* Disable interrupts. */
/* Read interrupts from ICT (fast) or from registers (slow). */
tmp = 0;
}
tmp = 0;
tmp |= 0x8000;
r2 = 0; /* Unused. */
} else {
return (DDI_INTR_UNCLAIMED); /* Hardware gone! */
}
return (DDI_INTR_UNCLAIMED); /* Interrupt not for us. */
}
/* Acknowledge interrupts. */
if (r1 & IWN_INT_RF_TOGGLED) {
"!RF switch: radio %s",
}
if (r1 & IWN_INT_CT_REACHED) {
"!critical temperature reached!");
}
"!fatal firmware error");
/* Dump firmware error log and stop. */
if (!IWN_CHK_FAST_RECOVER(sc))
return (DDI_INTR_CLAIMED);
}
(r2 & IWN_FH_INT_RX)) {
if (ena)
if (ena)
} else {
}
}
}
if (r1 & IWN_INT_ALIVE) {
}
if (r1 & IWN_INT_WAKEUP)
/* Re-enable interrupts. */
return (DDI_INTR_CLAIMED);
}
/*
* Update TX scheduler ring when transmitting an 802.11 frame (4965AGN and
* 5000 adapters use a slightly different format).
*/
static void
{
sizeof (uint16_t), DDI_DMA_SYNC_FORDEV);
if (idx < IWN_SCHED_WINSZ) {
*(w + IWN_TX_RING_COUNT) = *w;
sizeof (uint16_t), DDI_DMA_SYNC_FORDEV);
}
}
static void
{
sizeof (uint16_t), DDI_DMA_SYNC_FORDEV);
if (idx < IWN_SCHED_WINSZ) {
*(w + IWN_TX_RING_COUNT) = *w;
sizeof (uint16_t), DDI_DMA_SYNC_FORDEV);
}
}
#ifdef notyet
static void
{
sizeof (uint16_t), DDI_DMA_SYNC_FORDEV);
if (idx < IWN_SCHED_WINSZ) {
*(w + IWN_TX_RING_COUNT) = *w;
sizeof (uint16_t), DDI_DMA_SYNC_FORDEV);
}
}
#endif
/*
* This function is only for compatibility with Net80211 module.
* iwn_qosparam_to_hw() is the actual function updating EDCA
* parameters to hardware.
*/
static int
{
return (0);
}
static int
{
int qos_ac;
switch (wme_ac) {
case WME_AC_BE:
break;
case WME_AC_BK:
break;
case WME_AC_VI:
break;
case WME_AC_VO:
break;
default:
"WME AC index is not in suitable range.\n");
break;
}
return (qos_ac);
}
static uint16_t
{
while (cw_e > 0) {
cw <<= 1;
cw_e--;
}
cw -= 1;
return (cw);
}
static int
{
int i;
for (i = 0; i < WME_NUM_AC; i++) {
"Contention window is not in suitable range.\n");
return (IWN_FAIL);
}
"Arbitration interframe space number"
"is not in suitable range.\n");
return (IWN_FAIL);
}
}
return (IWN_SUCCESS);
}
/*
* This function updates EDCA parameters into hardware.
* FIFO0-background, FIFO1-best effort, FIFO2-video, FIFO3-voice.
*/
static int
{
int i, j;
} else {
return (IWN_SUCCESS);
}
if (err != IWN_SUCCESS) {
return (err);
}
}
}
for (i = 0; i < WME_NUM_AC; i++) {
j = iwn_wme_to_qos_ac(sc, i);
return (IWN_FAIL);
}
wmeparam[i].wmep_aifsn;
}
if (err != IWN_SUCCESS) {
"failed to update QoS parameters into hardware.");
return (err);
}
return (err);
}
static inline int
{
switch (tid) {
case 1:
case 2:
return (QOS_AC_BK);
case 0:
case 3:
return (QOS_AC_BE);
case 4:
case 5:
return (QOS_AC_VI);
case 6:
case 7:
return (QOS_AC_VO);
}
return (QOS_AC_BE);
}
static inline int
{
switch (qos_ac) {
case QOS_AC_BK:
return (QOS_AC_BK_TO_TXQ);
case QOS_AC_BE:
return (QOS_AC_BE_TO_TXQ);
case QOS_AC_VI:
return (QOS_AC_VI_TO_TXQ);
case QOS_AC_VO:
return (QOS_AC_VO_TO_TXQ);
}
return (QOS_AC_BE_TO_TXQ);
}
static int
{
int qos_ac;
if (tid < WME_TID_MIN ||
tid > WME_TID_MAX) {
"TID is not in suitable range.");
return (queue_n);
}
return (queue_n);
}
static int
{
struct ieee80211_key *k = NULL;
int noack = 0;
return (EIO);
return (EIO);
return(EIO);
}
/*
* determine send which AP or station in IBSS
*/
"failed to find tx node");
return(EIO);
}
/*
* Determine TX queue according to traffic ID in frame
* if working in QoS mode.
*/
if ((type & IEEE80211_FC0_TYPE_MASK) ==
if (txq_id < TXQ_FOR_AC_MIN ||
(txq_id > TXQ_FOR_AC_MAX)) {
return(EIO);
}
} else {
}
} else if ((type & IEEE80211_FC0_TYPE_MASK) ==
} else {
}
} else {
}
/* net80211-initiated send */
if ((type & IEEE80211_FC0_TYPE_MASK) !=
return (EAGAIN);
}
/* Choose a TX rate index. */
type != IEEE80211_FC0_TYPE_DATA) {
} else
if (m) {
}
mp = m;
} else {
/* net80211-initiated send */
if ((type & IEEE80211_FC0_TYPE_MASK) !=
return (EAGAIN);
}
/*
* Net80211 module encapsulate outbound data frames.
* Add some fields of 80211 frame.
*/
if ((type & IEEE80211_FC0_TYPE_MASK) ==
/* Encrypt the frame if need be. */
if (k == NULL) {
return(EIO);
}
/* Packet header may have moved, reset our local pointer. */
}
/* Prepare TX firmware command. */
/* NB: No need to clear tx, all fields are reinitialized here. */
flags = 0;
/* Unicast frame, check if an ACK is expected. */
if (!noack)
flags |= IWN_TX_NEED_ACK;
}
/* NB: Group frames are sent using CCK in 802.11b/g. */
flags |= IWN_TX_NEED_RTS;
ridx >= IWN_RIDX_OFDM6) {
flags |= IWN_TX_NEED_CTS;
flags |= IWN_TX_NEED_RTS;
}
} else
}
}
else
if (type == IEEE80211_FC0_TYPE_MGT) {
#ifndef IEEE80211_STA_ONLY
/* Tell HW to set timestamp in probe responses. */
/* XXX NetBSD rev 1.11 added probe requests here but */
/* probe requests do not take timestamps (from Bergamini). */
#endif
/* changes here. These are not needed (from Bergamini). */
if (subtype == IEEE80211_FC0_SUBTYPE_ASSOC_REQ ||
else
} else
if (hdrlen & 3) {
/* First segment length must be a multiple of 4. */
} else
pad = 0;
if (tid != WME_TID_INVALID) {
flags &= ~IWN_TX_AUTO_SEQ;
} else {
flags |= IWN_TX_AUTO_SEQ;
tid = 0;
}
/* Group or management frame. */
/* XXX Alternate between antenna A and B? */
} else {
}
/* Set physical address of "scratch area". */
/* Copy 802.11 header in TX command. */
/* XXX NetBSD changed this in rev 1.20 */
/* Fill TX descriptor. */
/* First DMA segment is used by the TX command. */
/* Other DMA segments are for data payload. */
seglen << 4);
}
sizeof (*cmd), DDI_DMA_SYNC_FORDEV);
sizeof (*desc), DDI_DMA_SYNC_FORDEV);
/* Update TX scheduler. */
/* Kick TX ring. */
/* Mark TX ring as full if we reach a certain threshold. */
if (sc->sc_tx_timer == 0)
return 0;
}
static mblk_t *
{
return (NULL);
}
return (NULL);
}
return (NULL);
}
break;
}
}
return (mp);
}
static void
{
if (sc->sc_tx_timer > 0) {
if (--sc->sc_tx_timer == 0) {
}
}
return;
} else {
}
}
static void
{
int error = 0;
/*
* This is special for the hidden AP connection.
* In any case, we should make sure only one 'scan'
* in the driver for a 'connect' CLI command. So
* when connecting to a hidden AP, the scan is just
* sent out to the air when we know the desired
* essid of the AP we want to connect.
*/
if (ic->ic_des_esslen) {
iwn_m_stop(sc);
(void) iwn_m_start(sc);
(void) ieee80211_new_state(ic,
IEEE80211_S_SCAN, -1);
}
}
}
}
/*
*/
static int
{
wldp_length, wldp_buf));
}
static void
{
}
static int
{
wldp_buf);
if (ic->ic_des_esslen) {
iwn_m_stop(sc);
(void) iwn_m_start(sc);
(void) ieee80211_new_state(ic,
IEEE80211_S_SCAN, -1);
}
}
err = 0;
}
return (err);
}
/*
* invoked by GLD get statistics from NIC and driver
*/
static int
{
switch (stat) {
case MAC_STAT_IFSPEED:
break;
case MAC_STAT_NOXMTBUF:
break;
case MAC_STAT_NORCVBUF:
break;
case MAC_STAT_IERRORS:
break;
case MAC_STAT_RBYTES:
break;
case MAC_STAT_IPACKETS:
break;
case MAC_STAT_OBYTES:
break;
case MAC_STAT_OPACKETS:
break;
case MAC_STAT_OERRORS:
case WIFI_STAT_TX_FAILED:
break;
case WIFI_STAT_TX_RETRANS:
break;
case WIFI_STAT_FCS_ERRORS:
case WIFI_STAT_WEP_ERRORS:
case WIFI_STAT_TX_FRAGS:
case WIFI_STAT_MCAST_TX:
case WIFI_STAT_RTS_SUCCESS:
case WIFI_STAT_RTS_FAILURE:
case WIFI_STAT_ACK_FAILURE:
case WIFI_STAT_RX_FRAGS:
case WIFI_STAT_MCAST_RX:
case WIFI_STAT_RX_DUPS:
default:
return (ENOTSUP);
}
return (0);
}
/*
* invoked by GLD to configure NIC
*/
static int
{
if (err != IWN_SUCCESS) {
"failed to configure device");
goto fail;
}
}
return (err);
fail:
return (err);
}
/*ARGSUSED*/
static int
{
return (IWN_SUCCESS);
}
/*ARGSUSED*/
static int
{
return (IWN_SUCCESS);
}
static void
{
return;
}
"!aborting scan, flags = %x, state = %s",
}
/*
* periodic function to deal with RF switch and HW error recovery
*/
static void
{
int err;
if (tmp & IWN_GP_CNTRL_RFKILL) {
} else {
}
/*
* If the RF is OFF, do nothing.
*/
return;
}
/*
* recovery fatal error
*/
"!trying to restore previous state");
if (IWN_CHK_FAST_RECOVER(sc)) {
/* save runtime configuration */
} else {
}
if (err != IWN_SUCCESS)
return;
if (!IWN_CHK_FAST_RECOVER(sc) ||
}
}
}
}
/*
* Send a command to the firmware.
*/
static int
{
/* Command is too large to fit in a descriptor. */
return ENOBUFS;
} else {
}
} else {
}
sizeof (*desc), DDI_DMA_SYNC_FORDEV);
/* Update TX scheduler. */
/* Kick command ring. */
if (async)
return (IWN_SUCCESS);
break;
return (ret);
}
static int
{
/*
* We use the node structure for 5000 Series internally (it is
* a superset of the one for 4965AGN). We thus copy the common
* fields before sending the command.
*/
/* Skip TSC, RX MIC and TX MIC fields from ``src''. */
}
static int
{
/* Direct mapping. */
}
static int
{
int i, txrate;
/* Use the first valid TX antenna. */
/* Start at highest available bit-rate. */
for (i = 0; i < IWN_MAX_TX_RETRIES; i++) {
/* Next retry at immediate lower bit-rate. */
if (txrate > 0)
txrate--;
}
}
/*
* Broadcast node is used to send group-addressed and management frames.
*/
static int
{
int i, error;
return error;
/* Use the first valid TX antenna. */
/* Use lowest mandatory bit-rate. */
/* Use same bit-rate for all TX retries. */
for (i = 1; i < IWN_MAX_TX_RETRIES; i++) {
}
}
static void
{
/* Clear microcode LED ownership. */
DTRACE_PROBE1(led__change, const char *,
}
/*
* Set the critical temperature at which the firmware will stop the radio
* and notify us.
*/
static int
{
else
temp = 110;
}
static int
{
/* Compute remaining time until next beacon. */
}
static void
{
/* Adjust TX power if need be (delta >= 3 degC). */
/* Record temperature of last calibration. */
}
}
/*
* 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.
*/
static int
{
/* 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. */
/* Retrieve current channel from last RXON. */
if (IEEE80211_IS_CHAN_5GHZ(ch)) {
} else {
}
/* Compute voltage compensation. */
if (vdiff > 0)
vdiff *= 2;
vdiff = 0;
/* Get channel attenuation group. */
grp = 4;
grp = 0;
grp = 1;
grp = 2;
else /* 125-200 */
grp = 3;
/* Get channel sub-band. */
for (i = 0; i < IWN_NBANDS; i++)
break;
if (i == IWN_NBANDS) /* Can't happen in real-life. */
return EINVAL;
for (c = 0; c < 2; c++) {
/* Compute temperature compensation. */
/* Convert dBm to half-dBm. */
/* Adjust TX power based on rate. */
else
/* Do not exceed channel max TX power. */
if (ridx == IWN_RIDX_MAX)
/* Make sure idx stays in a valid range. */
if (idx < 0)
idx = 0;
else if (idx > IWN4965_MAX_PWR_INDEX)
}
}
}
static int
{
/*
* TX power calibration is handled automatically by the firmware
* for 5000 Series.
*/
}
/*
* Retrieve the maximum RSSI (in dBm) among receivers.
*/
static int
{
int rssi;
rssi = 0;
}
static int
{
int rssi;
}
/*
* Retrieve the average noise (in dBm) among receivers.
*/
static int
{
for (i = 0; i < 3; i++) {
continue;
nbant++;
}
/* There should be at least one antenna but check anyway. */
}
/*
* Compute temperature (in degC) from last received statistics.
*/
static int
{
return 0;
/* Sign-extend 23-bit R4 value to 32-bit. */
/* Compute temperature in Kelvin. */
}
static int
{
/*
* Temperature is not used by the driver for 5000 Series because
* TX power calibration is handled by firmware. We export it to
* users through a kstat though.
*/
}
return temp;
}
/*
* Initialize sensitivity calibration state machine.
*/
static int
{
int error;
/* Reset calibration state machine. */
/* Set initial correlation values. */
/* Write initial sensitivity. */
return error;
/* Write initial gains. */
return error;
/* Request statistics at each beacon interval. */
flags = 0;
}
/*
* Collect noise and RSSI statistics for the first 20 beacons received
* after association and use them to determine connected antennas and
* to set differential gains.
*/
static void
const struct iwn_rx_general_stats *stats)
{
int i;
/* Accumulate RSSI and noise for all 3 antennas. */
for (i = 0; i < 3; i++) {
}
/* NB: We update differential gains only once after 20 beacons. */
return;
/* Determine highest average RSSI. */
/* Determine which antennas are connected. */
for (i = 0; i < 3; i++)
/* If none of the TX antennas are connected, keep at least one. */
#ifdef notyet
/* XXX Disable RX chains with no antennas connected. */
#endif
/* Enable power-saving mode if requested by user. */
}
static int
{
/* Differential gains initially set to 0 for all 3 antennas. */
}
static int
{
}
static int
{
/* Get minimal noise among connected antennas. */
for (i = 0; i < 3; i++)
/* Set differential gains for connected antennas. */
for (i = 0; i < 3; i++) {
/* Compute attenuation (in unit of 1.5dB). */
/* NB: delta <= 0 */
/* Limit to [-4.5dB,0]. */
if (delta < 0)
}
}
}
static int
{
/* We collected 20 beacons and !=6050 need a 1.5 factor. */
/* Get first available RX antenna as referential. */
/* Set differential gains for other antennas. */
/* The delta is relative to antenna "ant". */
/* Limit to [-4.5dB,+4.5dB]. */
if (delta < 0)
}
}
}
/*
* Tune RF RX sensitivity based on the number of false alarms detected
* during the last beacon period.
*/
static void
{
else \
needs_update = 1; \
}
else \
needs_update = 1; \
}
int i, needs_update = 0;
/* Check that we've been enabled long enough. */
return;
/* Compute number of false alarms since last call for OFDM. */
/* Save counters values for next call. */
/* High false alarm count, decrease sensitivity. */
/* Low false alarm count, increase sensitivity. */
}
/* Compute maximum noise among 3 receivers. */
for (i = 0; i < 3; i++)
/* Insert it into our samples table. */
/* Compute maximum noise among last 20 samples. */
for (i = 1; i < 20; i++)
/* Compute maximum energy among 3 receivers. */
for (i = 0; i < 3; i++)
/* Insert it into our samples table. */
/* Compute minimum energy among last 10 samples. */
for (i = 1; i < 10; i++)
energy_min += 6;
/* Compute number of false alarms since last call for CCK. */
/* Save counters values for next call. */
/* High false alarm count, decrease sensitivity. */
}
needs_update = 1;
} else
/* Low false alarm count, increase sensitivity. */
}
} else {
/* Not worth to increase or decrease sensitivity. */
/* Previous interval had many false alarms. */
}
}
if (needs_update)
(void)iwn_send_sensitivity(sc);
}
static int
{
int len;
len = sizeof (struct iwn_sensitivity_cmd);
/* OFDM modulation. */
/* CCK modulation. */
/* Barker modulation: use default values. */
goto send;
/* Enhanced sensitivity settings. */
len = sizeof (struct iwn_enhanced_sensitivity_cmd);
send:
}
/*
* Set STA mode power saving level (between 0 and 5).
* Level 0 is CAM (Continuously Aware Mode), 5 is for maximum power saving.
*/
static int
{
int i;
/* Select which PS parameters to use. */
if (dtim <= 2)
else if (dtim <= 10)
else
if (level != 0) /* not CAM */
if (level == 5)
/* Retrieve PCIe Active State Power Management (ASPM). */
if (dtim == 0) {
dtim = 1;
skip_dtim = 0;
} else
if (skip_dtim != 0) {
} else
for (i = 0; i < 5; i++)
}
int
{
}
static int
{
}
static int
{
&prio_table, sizeof prio_table, 0);
}
static int
{
int error;
if (error != 0) {
"!could not configure advanced bluetooth coexistence");
return error;
}
if (error != 0) {
"!could not configure send BT priority table");
return error;
}
/* Force BT state machine change */
if (error != 0) {
return error;
}
if (error != 0) {
return error;
}
return 0;
}
static int
{
struct iwn_bt_adv1 d;
memset(&d, 0, sizeof d);
d.tx_prio_boost = 0;
d.rx_prio_boost = 0;
}
static int
{
struct iwn_bt_adv2 d;
memset(&d, 0, sizeof d);
d.tx_prio_boost = 0;
d.rx_prio_boost = 0;
}
static int
{
int error;
if (error != 0) {
"!could not configure bluetooth coexistence");
return error;
}
/* Set radio temperature sensor offset. */
if (error != 0) {
"!could not set temperature offset");
return error;
}
}
if (error != 0) {
"!could not set temperature offset");
return error;
}
}
/* Configure runtime DC calibration. */
if (error != 0) {
"!could not configure runtime calibration");
return error;
}
}
/* Configure valid TX chains for 5000 Series. */
sizeof txmask, 0);
if (error != 0) {
"!could not configure valid TX chains");
return error;
}
}
/* Set mode, channel, RX filter and enable RX. */
case IEEE80211_M_IBSS:
break;
case IEEE80211_M_STA:
break;
case IEEE80211_M_MONITOR:
break;
default:
/* Should not get there. */
break;
}
rxchain =
if (error != 0) {
"!RXON command failed");
return error;
}
"!could not add broadcast node");
return error;
}
/* Configuration has changed, set TX power accordingly. */
"!could not set TX power");
return error;
}
"!could not set critical temperature");
return error;
}
/* Set power saving level to CAM during initialization. */
"!could not set power saving level");
return error;
}
return 0;
}
static uint16_t
{
/* No channel? Default to 2GHz settings */
if (flags & IEEE80211_CHAN_2GHZ)
return IWN_ACTIVE_DWELL_TIME_2GHZ +
/* 5GHz dwell time */
return IWN_ACTIVE_DWELL_TIME_5GHZ +
}
/*
* Limit the total dwell time to 85% of the beacon interval.
*
* Returns the dwell time in milliseconds.
*/
static uint16_t
{
int bintval = 0;
/* bintval is in TU (1.024mS) */
/*
* If it's non-zero, we should calculate the minimum of
* it and the DWELL_BASE.
*
* XXX Yes, the math should take into account that bintval
* is 1.024mS, not 1mS..
*/
if (bintval > 0)
/* No association context? Default */
return IWN_PASSIVE_DWELL_BASE;
}
static uint16_t
{
if (flags & IEEE80211_CHAN_2GHZ)
else
/* Clamp to the beacon interval if we're associated */
}
static int
{
struct ieee80211_channel *c;
"!could not allocate buffer for scan command");
return ENOMEM;
}
/*
* Move to the next channel if no frames are received within 20ms
* after sending the probe request.
*/
/* Select antennas for scanning. */
rxchain =
if ((flags & IEEE80211_CHAN_5GHZ) &&
/* Ant A must be avoided in 5GHz because of an HW bug. */
} else /* Use all available RX antennas. */
if (flags & IEEE80211_CHAN_5GHZ) {
/* Send probe requests at 6Mbps. */
} else {
/* Send probe requests at 1Mbps. */
}
/* Use the first valid TX antenna. */
/*
* Only do active scanning if we're announcing a probe request
* for a given SSID (or more, if we ever add it to the driver.)
*/
is_active = 0;
if (ic->ic_des_esslen != 0) {
is_active = 1;
/* hdr->crc_threshold = 0x1; */
}
/*
* Build a probe request frame. Most of the following code is a
* copy & paste of what is done in net80211.
*/
#ifndef IEEE80211_NO_HT
#endif
/* Set length of probe request. */
/*LINTED: E_PTRDIFF_OVERFLOW*/
/*
* If active scanning is requested but a certain channel is
* marked passive, we can do active scanning if we detect
* transmissions.
*
* There is an issue with some firmware versions that triggers
* a sysassert on a "good CRC threshold" of zero (== disabled),
* on a radar channel even though this means that we should NOT
* send probes.
*
* The "good CRC threshold" is the number of frames that we
* need to receive during our dwell time on a channel before
* sending out probes -- setting this to a huge value will
* mean we never reach it, but at the same time work around
* the aforementioned issue. Thus use IWN_GOOD_CRC_TH_NEVER
* here instead of IWN_GOOD_CRC_TH_DISABLED.
*
* This was fixed in later versions along with some other
* scan changes, and the threshold behaves as a flag in those
* versions.
*/
/*
* If we're doing active scanning, set the crc_threshold
* to a suitable value. This is different to active veruss
* passive scanning depending upon the channel flags; the
* firmware will obey that particular check for us.
*/
else
continue;
if (!(c->ich_flags & IEEE80211_CHAN_PASSIVE))
if (ic->ic_des_esslen != 0)
/*
*/
/* Make sure they're valid */
if (dwell_passive <= dwell_active)
if (IEEE80211_IS_CHAN_5GHZ(c)) {
} else {
}
chan++;
}
/*LINTED: E_PTRDIFF_OVERFLOW*/
return error;
}
static int
{
int error;
/* Update adapter configuration. */
switch (ic->ic_curmode) {
case IEEE80211_MODE_11A:
break;
case IEEE80211_MODE_11B:
break;
default: /* Assume 802.11b/g. */
}
if (error != 0) {
"!RXON command failed");
return error;
}
/* Configuration has changed, set TX power accordingly. */
"!could not set TX power");
return error;
}
/*
* Reconfiguring RXON clears the firmware nodes table so we must
* add the broadcast node again.
*/
"!could not add broadcast node");
return error;
}
return 0;
}
static int
{
/* restore runtime configuration */
"could not setup authentication");
return (err);
}
/* update adapter's configuration */
if (err != IWN_SUCCESS) {
"failed to setup association");
return (err);
}
/* set LED on */
/* start queue */
return (IWN_SUCCESS);
}
static int
{
int error;
/* Link LED blinks while monitoring. */
return 0;
}
"!could not set timing");
return error;
}
/* Update adapter configuration. */
/* Short preamble and slot time are negotiated when associating. */
if (error != 0) {
"!could not update configuration");
return error;
}
/* Configuration has changed, set TX power accordingly. */
"!could not set TX power");
return error;
}
/* Fake a join to initialize the TX rate. */
/* Add BSS node. */
#ifdef notyet
#endif
if (error != 0) {
"!could not add BSS node");
return error;
}
return error;
}
"!could not set sensitivity");
return error;
}
"!could not set QoS params");
return (error);
}
/* Start periodic calibration timer. */
/* Link LED always on while associated. */
return 0;
}
#ifdef IWN_HWCRYPTO
/*
* We support CCMP hardware encryption/decryption of unicast frames only.
* HW support for TKIP really sucks. We should let TKIP die anyway.
*/
static int
struct ieee80211_key *k)
{
if ((k->k_flags & IEEE80211_KEY_GROUP) ||
k->k_cipher != IEEE80211_CIPHER_CCMP)
if (k->k_flags & IEEE80211_KEY_GROUP)
}
static void
struct ieee80211_key *k)
{
if ((k->k_flags & IEEE80211_KEY_GROUP) ||
k->k_cipher != IEEE80211_CIPHER_CCMP) {
/* See comment about other ciphers above. */
return;
}
return; /* Nothing to do. */
}
#endif
#ifndef IEEE80211_NO_HT
/*
* This function is called by upper layer when an ADDBA request is received
* from another STA and before the ADDBA response is sent.
*/
static int
{
}
/*
* This function is called by upper layer on teardown of an HT-immediate
* Block Ack agreement (eg. uppon receipt of a DELBA frame).
*/
static void
{
}
/*
* This function is called by upper layer when an ADDBA response is received
* from another STA.
*/
static int
{
int error;
if (error != 0)
return error;
return error;
return 0;
}
static void
{
if (iwn_nic_lock(sc) != 0)
return;
}
static void
{
/* Stop TX scheduler while we're changing its configuration. */
/* Enable chain-building mode for the queue. */
/* Set starting sequence number from the ADDBA request. */
/* Set scheduler window size. */
/* Set scheduler frame limit. */
IWN_SCHED_LIMIT << 16);
/* Enable interrupts for the queue. */
/* Mark the queue as active. */
}
static void
{
/* Stop TX scheduler while we're changing its configuration. */
/* Set starting sequence number from the ADDBA request. */
/* Disable interrupts for the queue. */
/* Mark the queue as inactive. */
}
static void
{
/* Stop TX scheduler while we're changing its configuration. */
/* Enable chain-building mode for the queue. */
/* Enable aggregation for the queue. */
/* Set starting sequence number from the ADDBA request. */
/* Set scheduler window size and frame limit. */
/* Enable interrupts for the queue. */
/* Mark the queue as active. */
}
static void
{
/* Stop TX scheduler while we're changing its configuration. */
/* Disable aggregation for the queue. */
/* Set starting sequence number from the ADDBA request. */
/* Disable interrupts for the queue. */
/* Mark the queue as inactive. */
}
#endif /* !IEEE80211_NO_HT */
/*
* Query calibration tables from the initialization firmware. We do this
* only once at first boot. Called from a process context.
*/
static int
{
int error;
if (error != 0)
return error;
/* Wait at most two seconds for calibration to complete. */
return (IWN_FAIL);
return (IWN_SUCCESS);
}
/*
* Send calibration results to the runtime firmware. These results were
* obtained on first boot from the initialization firmware.
*/
static int
{
continue; /* No results available. */
if (error != 0) {
"!could not send calibration result");
return error;
}
}
return 0;
}
static int
{
#ifdef notyet
/* Enable WiMAX coexistence for combo adapters. */
sizeof iwn6050_wimax_events);
} else
#endif
{
/* Disable WiMAX coexistence. */
}
}
static int
{
if (sc->eeprom_temp != 0)
else
}
static int
{
if (sc->eeprom_rawtemp != 0) {
} else {
}
}
/*
* This function is called after the runtime firmware notifies us of its
* readiness (called in a process context).
*/
static int
{
return error;
/* Clear TX scheduler state in SRAM. */
IWN4965_SCHED_CTX_LEN / sizeof (uint32_t));
/* Set physical address of TX scheduler rings (1KB aligned). */
/* Disable chain mode for all our 16 queues. */
/* Set scheduler window size. */
/* Set scheduler frame limit. */
IWN_SCHED_LIMIT << 16);
}
/* Enable interrupts for all our 16 queues. */
/* Identify TX FIFO rings (0-7). */
/* Mark TX rings (4 EDCA + cmd + 2 HCCA) as active. */
}
return 0;
}
/*
* This function is called after the initialization or runtime firmware
* notifies us of its readiness (called in a process context).
*/
static int
{
/* Switch to using ICT interrupt mode. */
return error;
/* Clear TX scheduler state in SRAM. */
IWN5000_SCHED_CTX_LEN / sizeof (uint32_t));
/* Set physical address of TX scheduler rings (1KB aligned). */
/* Enable chain mode for all queues, except command queue. */
/* Set scheduler window size and frame limit. */
}
/* Enable interrupts for all our 20 queues. */
/* Identify TX FIFO rings (0-7). */
/* Mark TX rings (4 EDCA + cmd + 2 HCCA) as active. */
}
/* Configure WiMAX coexistence for combo adapters. */
if (error != 0) {
"!could not configure WiMAX coexistence");
return error;
}
/* Perform crystal calibration. */
if (error != 0) {
"!crystal calibration failed");
return error;
}
}
/* Query calibration from the initialization firmware. */
"!could not query calibration");
return error;
}
/*
* We have the calibration results now, reboot with the
* runtime firmware (call ourselves recursively!)
*/
} else {
/* Send calibration results to runtime firmware. */
}
return error;
}
/*
* The firmware boot code is small and is intended to be copied directy into
* the NIC internal memory (no DMA transfer).
*/
static int
{
return error;
/* Copy microcode image into NIC memory. */
/*LINTED: E_PTR_BAD_CAST_ALIGN*/
/* Start boot load now. */
/* Wait for transfer to complete. */
break;
DELAY(10);
}
if (ntries == 1000) {
"!could not load boot firmware");
return ETIMEDOUT;
}
/* Enable boot after power up. */
return 0;
}
static int
{
int error;
/* Copy initialization sections into pre-allocated DMA-safe memory. */
/* Tell adapter where to find initialization sections. */
return error;
/* Load firmware boot code. */
if (error != 0) {
"!could not load boot firmware");
return error;
}
/* Now press "execute". */
/* Wait at most one second for first alive notification. */
"!timeout waiting for adapter to initialize");
return (IWN_FAIL);
}
}
/* Retrieve current temperature for initial TX power calibration. */
/* Copy runtime sections into pre-allocated DMA-safe memory. */
/* Tell adapter where to find runtime sections. */
return error;
return 0;
}
static int
{
int error;
/* Copy firmware section into pre-allocated DMA-safe memory. */
return error;
/* Kick Flow Handler to start DMA transfer. */
/* Wait at most five seconds for FH DMA transfer to complete. */
return (IWN_FAIL);
}
return (IWN_SUCCESS);
}
static int
{
int error;
/* Load the initialization firmware on first boot only. */
if (error != 0) {
"!could not load firmware %s section", ".text");
return error;
}
if (error != 0) {
"!could not load firmware %s section", ".data");
return error;
}
/* Now press "execute". */
return 0;
}
/*
* Extract text and data sections from a legacy firmware image.
*/
static int
{
/*LINTED: E_PTR_BAD_CAST_ALIGN*/
/* Check firmware API version. */
"!bad firmware, need API version >=2");
return EINVAL;
}
/* Skip build number (version 2 header). */
hdrlen += 4;
ptr++;
}
return EINVAL;
}
/* Check that all firmware sections fit. */
return EINVAL;
}
/* Get pointers to firmware sections. */
return 0;
}
/*
* Extract text and data sections from a TLV firmware image.
*/
static int
{
return EINVAL;
}
return EINVAL;
}
/*
* Select the closest supported alternative that is less than
* or equal to the specified one.
*/
alt--; /* Downgrade. */
/* Parse type-length-value fields. */
"!firmware too short: %lld bytes",
return EINVAL;
}
/* Skip other alternatives. */
IWN_DBG("skipping other alternative");
goto next;
}
case IWN_FW_TLV_MAIN_TEXT:
break;
case IWN_FW_TLV_MAIN_DATA:
break;
case IWN_FW_TLV_INIT_TEXT:
break;
case IWN_FW_TLV_INIT_DATA:
break;
case IWN_FW_TLV_BOOT_TEXT:
break;
case IWN_FW_TLV_ENH_SENS:
if (len != 0) {
"!TLV type %d has invalid size %u",
goto next;
}
break;
case IWN_FW_TLV_PHY_CALIB:
"!TLV type %d has invalid size %u",
goto next;
}
}
break;
case IWN_FW_TLV_FLAGS:
break;
break;
break;
default:
break;
}
next: /* TLV fields are 32-bit aligned. */
}
return 0;
}
static int
{
int error;
/*
* Some PHY calibration commands are firmware-dependent; these
* are the default values that will be overridden if
* necessary.
*/
/* Initialize for error returns */
/* Open firmware image. */
return error;
}
(void) firmware_close(fwh);
return EINVAL;
}
/* Read the firmware. */
(void) firmware_close(fwh);
if (error != 0) {
goto out;
}
/* Retrieve text and data sections. */
/*LINTED: E_PTR_BAD_CAST_ALIGN*/
else
if (error != 0) {
"!could not read firmware sections");
goto out;
}
/* Make sure text and data sections fit in hardware memory. */
"!firmware sections too large");
goto out;
}
/* We can proceed with loading the firmware. */
return 0;
out:
}
static int
{
int ntries;
/* Set "initialization complete" bit. */
/* Wait for clock stabilization. */
return 0;
DELAY(10);
}
"!timeout waiting for clock stabilization");
return ETIMEDOUT;
}
static int
{
int error;
/* Disable L0s exit timer (NMI bug workaround). */
/* Don't wait for ICH L0s (ICH bug workaround). */
/* Set FH wait threshold to max (HW bug under stress workaround). */
/* Enable HAP INTA to move adapter from L1a to L0s. */
/* Retrieve PCIe Active State Power Management (ASPM). */
/* Workaround for HW instability in PCIe L0->L0s->L1 transition. */
else
/* Wait for clock stabilization before accessing prph. */
return error;
return error;
/* Enable DMA and BSM (Bootstrap State Machine). */
} else {
/* Enable DMA. */
}
DELAY(20);
/* Disable L1-Active. */
return 0;
}
static void
{
int ntries;
/* Stop busmaster DMA activity. */
return;
DELAY(10);
}
"!timeout waiting for master");
}
static void
{
/* Reset the entire device. */
DELAY(10);
/* Clear "initialization complete" bit. */
}
static int
{
/*
* 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_*.
*/
}
return 0;
}
static int
{
int error;
}
return error;
/*
* Select first Switching Voltage Regulator (1.32V) to
* solve a stability issue related to noisy DC2DC line
* in the silicon of 1000 Series.
*/
}
/* Use internal power amplifier only. */
}
/* Indicate that ROM calibration version is >=6. */
}
return 0;
}
/*
* Take NIC ownership over Intel Active Management Technology (AMT).
*/
static int
{
int ntries;
/* Check if hardware is ready. */
return 0;
DELAY(10);
}
/* Hardware not ready, force into ready state. */
break;
DELAY(10);
}
if (ntries == 15000)
return ETIMEDOUT;
/* Hardware should be ready now. */
return 0;
DELAY(10);
}
return ETIMEDOUT;
}
static int
{
/* Clear pending interrupts. */
"!could not power ON adapter");
return error;
}
/* Select VMAIN power source. */
return error;
/* Perform adapter-specific initialization. */
return error;
/* Initialize RX ring. */
return error;
/* Set physical address of RX ring (256-byte aligned). */
/* Set physical address of RX status (16-byte aligned). */
/* Enable RX. */
#if IWN_RBUF_SIZE == 8192
#endif
IWN_FH_RX_CONFIG_IGN_RXF_EMPTY | /* HW bug workaround */
return error;
/* Initialize TX scheduler. */
/* Set physical address of "keep warm" page (16-byte aligned). */
/* Initialize TX rings. */
/* Set physical address of TX ring (256-byte aligned). */
}
/* Enable DMA channels. */
}
/* Clear "radio off" and "commands blocked" bits. */
/* Clear pending interrupts. */
/* Enable interrupt coalescing. */
/* Enable interrupts. */
/* _Really_ make sure "radio off" bit is cleared! */
/* Enable shadow registers. */
"!could not load firmware");
return error;
}
/* Wait at most one second for firmware alive notification. */
"!timeout waiting for adapter to initialize");
return (IWN_FAIL);
}
}
/* Do post-firmware initialization. */
}
static void
{
if (lock) {
}
/* Disable interrupts. */
/* Make sure we no longer hold the NIC lock. */
/* Stop TX scheduler. */
/* Stop all DMA channels. */
if (iwn_nic_lock(sc) == 0) {
break;
DELAY(10);
}
}
}
/* Stop RX ring. */
/* Reset all TX rings. */
if (iwn_nic_lock(sc) == 0) {
}
DELAY(5);
/* Power OFF adapter. */
if (lock) {
}
}
static int
{
int error;
goto out;
goto fail;
}
/* Check that the radio is not disabled by hardware switch. */
"!radio is disabled by hardware switch");
goto fail;
}
/* Read firmware images from the filesystem. */
goto fail;
}
/* Initialize interrupt mask to default value. */
/* Initialize hardware and upload firmware. */
if (error != 0) {
goto fail;
}
/* Configure adapter now that it is ready. */
goto fail;
}
out:
return 0;
fail:
return error;
}
/*
* Copyright (c) 2001 Atsushi Onoe
* Copyright (c) 2002, 2003 Sam Leffler, Errno Consulting
* Copyright (c) 2007-2009 Damien Bergamini
* All rights reserved.
*/
/*
* Add SSID element to a frame
*/
static uint8_t *
{
*frm++ = IEEE80211_ELEMID_SSID;
}
/*
* Add supported rates information element to a frame.
*/
static uint8_t *
{
*frm++ = IEEE80211_ELEMID_RATES;
if (nrates > IEEE80211_RATE_SIZE)
}
/*
* Add extended supported rates element to a frame, usually for 11g mode
*/
static uint8_t *
{
*frm++ = IEEE80211_ELEMID_XRATES;
}
return (frm);
}
/*
* XXX: Hack to set the current channel to the value advertised in beacons or
* probe responses. Only used during AP detection.
* XXX: Duplicated from if_iwi.c
*/
static void
struct iwn_rx_stat *stat)
{
return;
if (subtype != IEEE80211_FC0_SUBTYPE_BEACON &&
return;
return;
}
if (*frm == IEEE80211_ELEMID_DSPARMS)
#if IEEE80211_CHAN_MAX < 255
#endif
}
}
/*
* invoked by GLD to start or open NIC
*/
static int
{
if (err != IWN_SUCCESS) {
/*
* If initialization failed because the RF switch is off,
* return success anyway to make the 'plumb' succeed.
* The iwn_thread() tries to re-init background.
*/
return (IWN_SUCCESS);
}
return (err);
}
return (IWN_SUCCESS);
}
/*
* invoked by GLD to stop or down NIC
*/
static void
{
/*
* release buffer for calibration
*/
}
/*
* Module Loading Data & Entry Points
*/
"Intel WiFi Link 4965 and 1000/5000/6000 series driver",
};
};
int
_init(void)
{
int status;
sizeof (struct iwn_softc), 1);
if (status != DDI_SUCCESS)
return (status);
if (status != DDI_SUCCESS) {
}
return (status);
}
int
_fini(void)
{
int status;
if (status == DDI_SUCCESS) {
}
return (status);
}
int
{
}