Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files. =================================================================== RCS file: /ftp/cvs/cvsroot/src/sys/dev/pci/if_iwn.c,v rcsdiff: /ftp/cvs/cvsroot/src/sys/dev/pci/if_iwn.c,v: warning: Unknown phrases like `commitid ...;' are present. retrieving revision 1.37 retrieving revision 1.37.2.3 diff -u -p -r1.37 -r1.37.2.3 --- src/sys/dev/pci/if_iwn.c 2010/02/24 22:38:00 1.37 +++ src/sys/dev/pci/if_iwn.c 2011/03/05 20:53:41 1.37.2.3 @@ -1,9 +1,8 @@ -/* $NetBSD: if_iwn.c,v 1.37 2010/02/24 22:38:00 dyoung Exp $ */ -/* $OpenBSD: if_iwn.c,v 1.49 2009/03/29 21:53:52 sthen Exp $ */ +/* $NetBSD: if_iwn.c,v 1.37.2.3 2011/03/05 20:53:41 rmind Exp $ */ +/* $OpenBSD: if_iwn.c,v 1.96 2010/05/13 09:25:03 damien Exp $ */ /*- - * Copyright (c) 2007, 2008 - * Damien Bergamini + * Copyright (c) 2007-2010 Damien Bergamini * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -19,16 +18,21 @@ */ /* - * Driver for Intel Wireless WiFi Link 4965 and Intel WiFi Link 5000 Series - * 802.11 network adapters. + * Driver for Intel WiFi Link 4965 and 1000/5000/6000 Series 802.11 network + * adapters. */ #include -__KERNEL_RCSID(0, "$NetBSD: if_iwn.c,v 1.37 2010/02/24 22:38:00 dyoung Exp $"); +__KERNEL_RCSID(0, "$NetBSD: if_iwn.c,v 1.37.2.3 2011/03/05 20:53:41 rmind Exp $"); +#define IWN_USE_RBUF /* Use local storage for RX */ +#undef IWN_HWCRYPTO /* XXX does not even compile yet */ + +/* XXX Avoid sensor code (correct option for NetBSD too?) */ +#undef SMALL_KERNEL #include #include -#include +#include #include #include #include @@ -39,6 +43,8 @@ __KERNEL_RCSID(0, "$NetBSD: if_iwn.c,v 1 #include #include +#include + #include #include #include @@ -70,19 +76,6 @@ __KERNEL_RCSID(0, "$NetBSD: if_iwn.c,v 1 #include static const pci_product_id_t iwn_devices[] = { - PCI_PRODUCT_INTEL_PRO_WL_4965AGN_1, - PCI_PRODUCT_INTEL_PRO_WL_4965AGN_2, - PCI_PRODUCT_INTEL_PRO_WL_5100AGN_1, - PCI_PRODUCT_INTEL_PRO_WL_5100AGN_2, -#ifdef notyet - PCI_PRODUCT_INTEL_PRO_WL_5150AGN_1, - PCI_PRODUCT_INTEL_PRO_WL_5150AGN_2, -#endif - PCI_PRODUCT_INTEL_PRO_WL_5300AGN_1, - PCI_PRODUCT_INTEL_PRO_WL_5300AGN_2, - PCI_PRODUCT_INTEL_PRO_WL_5350AGN_1, - PCI_PRODUCT_INTEL_PRO_WL_5350AGN_2, -#ifdef notyet PCI_PRODUCT_INTEL_WIFI_LINK_4965_1, PCI_PRODUCT_INTEL_WIFI_LINK_4965_2, PCI_PRODUCT_INTEL_WIFI_LINK_5100_1, @@ -93,16 +86,28 @@ static const pci_product_id_t iwn_device PCI_PRODUCT_INTEL_WIFI_LINK_5300_2, PCI_PRODUCT_INTEL_WIFI_LINK_5350_1, PCI_PRODUCT_INTEL_WIFI_LINK_5350_2, - PCI_PRODUCT_INTEL_WIFI_LINK_6000_1, - PCI_PRODUCT_INTEL_WIFI_LINK_6000_2, - PCI_PRODUCT_INTEL_WIFI_LINK_6000_3, - PCI_PRODUCT_INTEL_WIFI_LINK_6000_4, - PCI_PRODUCT_INTEL_WIFI_LINK_6050_1, - PCI_PRODUCT_INTEL_WIFI_LINK_6050_2, - PCI_PRODUCT_INTEL_WIFI_LINK_6050_3, - PCI_PRODUCT_INTEL_WIFI_LINK_6050_4, PCI_PRODUCT_INTEL_WIFI_LINK_1000_1, PCI_PRODUCT_INTEL_WIFI_LINK_1000_2, + PCI_PRODUCT_INTEL_WIFI_LINK_6000_3X3_1, + PCI_PRODUCT_INTEL_WIFI_LINK_6000_3X3_2, + PCI_PRODUCT_INTEL_WIFI_LINK_6000_IPA_1, + PCI_PRODUCT_INTEL_WIFI_LINK_6000_IPA_2, + PCI_PRODUCT_INTEL_WIFI_LINK_6050_2X2_1, + PCI_PRODUCT_INTEL_WIFI_LINK_6050_2X2_2, + PCI_PRODUCT_INTEL_WIFI_LINK_6005_2X2_1, + PCI_PRODUCT_INTEL_WIFI_LINK_6005_2X2_2, +#ifdef notyet + /* + * XXX NetBSD: the 6005A replaces the two 6005, above + * (see OpenBSD rev 1.96). + */ + PCI_PRODUCT_INTEL_WIFI_LINK_6005A_2X2_1, + PCI_PRODUCT_INTEL_WIFI_LINK_6005A_2X2_2, + PCI_PRODUCT_INTEL_WIFI_LINK_6005B_1X1_1, + PCI_PRODUCT_INTEL_WIFI_LINK_6005B_1X1_2, + PCI_PRODUCT_INTEL_WIFI_LINK_6005B_2X2_1, + PCI_PRODUCT_INTEL_WIFI_LINK_6005B_2X2_2, + PCI_PRODUCT_INTEL_WIFI_LINK_6005B_2X2_3 #endif }; @@ -118,61 +123,71 @@ static const struct ieee80211_rateset iw static const struct ieee80211_rateset iwn_rateset_11g = { 12, { 2, 4, 11, 22, 12, 18, 24, 36, 48, 72, 96, 108 } }; - -static int iwn_match(device_t , struct cfdata *, void *); -static void iwn_attach(device_t , device_t, void *); -static int iwn_detach(device_t, int); - -const struct iwn_hal *iwn_hal_attach(struct iwn_softc *); +static int iwn_match(device_t , struct cfdata *, void *); +static void iwn_attach(device_t , device_t , void *); +const struct iwn_hal *iwn_hal_attach(struct iwn_softc *, pci_product_id_t pid); +#ifndef SMALL_KERNEL +static void iwn_sensor_attach(struct iwn_softc *); +#endif +static void iwn_radiotap_attach(struct iwn_softc *); +static int iwn_detach(device_t , int); +#if 0 +static void iwn_power(int, void *); +#endif +static bool iwn_resume(device_t, const pmf_qual_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 int iwn_read_prom_data(struct iwn_softc *, uint32_t, void *, int); -static void iwn_radiotap_attach(struct iwn_softc *); static int iwn_dma_contig_alloc(bus_dma_tag_t, struct iwn_dma_info *, - void **, bus_size_t, bus_size_t, int); + void **, bus_size_t, bus_size_t); 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 *); -static struct iwn_rbuf *iwn_alloc_rbuf(struct iwn_softc *); -static void iwn_free_rbuf(struct mbuf *, void *, size_t, void *); -static int iwn_alloc_rpool(struct iwn_softc *); -static void iwn_free_rpool(struct iwn_softc *); static int iwn_alloc_rx_ring(struct iwn_softc *, struct iwn_rx_ring *); static void iwn_reset_rx_ring(struct iwn_softc *, struct iwn_rx_ring *); static void iwn_free_rx_ring(struct iwn_softc *, struct iwn_rx_ring *); static int iwn_alloc_tx_ring(struct iwn_softc *, struct iwn_tx_ring *, - int, int); + int); static void iwn_reset_tx_ring(struct iwn_softc *, struct iwn_tx_ring *); static void iwn_free_tx_ring(struct iwn_softc *, struct iwn_tx_ring *); +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_channels(struct iwn_softc *, int, uint32_t); +static void iwn_read_eeprom_enhinfo(struct iwn_softc *); static struct ieee80211_node *iwn_node_alloc(struct ieee80211_node_table *); static void iwn_newassoc(struct ieee80211_node *, int); static int iwn_media_change(struct ifnet *); static int iwn_newstate(struct ieee80211com *, enum ieee80211_state, int); static void iwn_iter_func(void *, struct ieee80211_node *); static void iwn_calib_timeout(void *); -#if 0 -static int iwn_ccmp_decap(struct iwn_softc *, struct mbuf *, - struct ieee80211_key *); -#endif -static void iwn_rx_phy(struct iwn_softc *, struct iwn_rx_desc *); +static void iwn_rx_phy(struct iwn_softc *, struct iwn_rx_desc *, + struct iwn_rx_data *); static void iwn_rx_done(struct iwn_softc *, struct iwn_rx_desc *, struct iwn_rx_data *); +#ifndef IEEE80211_NO_HT +static void iwn_rx_compressed_ba(struct iwn_softc *, struct iwn_rx_desc *, + 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_desc *, struct iwn_rx_data *); static void iwn_rx_statistics(struct iwn_softc *, struct iwn_rx_desc *, - struct iwn_rx_data *); + struct iwn_rx_data *); static void iwn4965_tx_done(struct iwn_softc *, struct iwn_rx_desc *, - struct iwn_rx_data *); + struct iwn_rx_data *); static void iwn5000_tx_done(struct iwn_softc *, struct iwn_rx_desc *, - struct iwn_rx_data *); + struct iwn_rx_data *); static void iwn_tx_done(struct iwn_softc *, struct iwn_rx_desc *, int, uint8_t); static void iwn_cmd_done(struct iwn_softc *, struct iwn_rx_desc *); @@ -184,14 +199,15 @@ static void iwn4965_update_sched(struct uint16_t); static void iwn5000_update_sched(struct iwn_softc *, int, int, uint8_t, uint16_t); +#ifdef notyet static void iwn5000_reset_sched(struct iwn_softc *, int, int); +#endif static int iwn_tx(struct iwn_softc *, struct mbuf *, struct ieee80211_node *, int); static void iwn_start(struct ifnet *); static void iwn_watchdog(struct ifnet *); static int iwn_ioctl(struct ifnet *, u_long, void *); static int iwn_cmd(struct iwn_softc *, int, const void *, int, int); -static int iwn_wme_update(struct ieee80211com *); static int iwn4965_add_node(struct iwn_softc *, struct iwn_node_info *, int); static int iwn5000_add_node(struct iwn_softc *, struct iwn_node_info *, @@ -202,7 +218,7 @@ static int iwn_add_broadcast_node(struct static void iwn_set_led(struct iwn_softc *, uint8_t, uint8_t, uint8_t); static int iwn_set_critical_temp(struct iwn_softc *); static int iwn_set_timing(struct iwn_softc *, struct ieee80211_node *); -//static void iwn4965_power_calibration(struct iwn_softc *, int); +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 *); @@ -220,24 +236,27 @@ static int iwn5000_set_gains(struct iwn_ static void iwn_tune_sensitivity(struct iwn_softc *, const struct iwn_rx_stats *); static int iwn_send_sensitivity(struct iwn_softc *); -// XXX static int iwn_set_pslevel(struct iwn_softc *, int, int, int); +static int iwn_set_pslevel(struct iwn_softc *, int, int, int); static int iwn_config(struct iwn_softc *); static int iwn_scan(struct iwn_softc *, uint16_t); static int iwn_auth(struct iwn_softc *); static int iwn_run(struct iwn_softc *); -#ifdef notyet +#ifdef IWN_HWCRYPTO +static int iwn_set_key(struct ieee80211com *, struct ieee80211_node *, + struct ieee80211_key *); static void iwn_delete_key(struct ieee80211com *, struct ieee80211_node *, 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, uint16_t); + struct ieee80211_node *, uint8_t); static void iwn_ampdu_rx_stop(struct ieee80211com *, - struct ieee80211_node *, uint8_t, uint16_t); + struct ieee80211_node *, uint8_t); static int iwn_ampdu_tx_start(struct ieee80211com *, - struct ieee80211_node *, uint8_t, uint16_t); + struct ieee80211_node *, uint8_t); static void iwn_ampdu_tx_stop(struct ieee80211com *, - struct ieee80211_node *, uint8_t, uint16_t); + struct ieee80211_node *, uint8_t); static void iwn4965_ampdu_tx_start(struct iwn_softc *, struct ieee80211_node *, uint8_t, uint16_t); static void iwn4965_ampdu_tx_stop(struct iwn_softc *, @@ -249,6 +268,7 @@ static void iwn5000_ampdu_tx_stop(struct #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 iwn4965_post_alive(struct iwn_softc *); static int iwn5000_post_alive(struct iwn_softc *); static int iwn4965_load_bootcode(struct iwn_softc *, const uint8_t *, @@ -256,23 +276,43 @@ static int iwn4965_load_bootcode(struct static int iwn4965_load_firmware(struct iwn_softc *); static int iwn5000_load_firmware_section(struct iwn_softc *, uint32_t, const uint8_t *, int); +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 iwn5000_load_firmware(struct iwn_softc *); static int iwn_read_firmware(struct iwn_softc *); static int iwn_clock_wait(struct iwn_softc *); -static int iwn4965_apm_init(struct iwn_softc *); -static int iwn5000_apm_init(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_hw_stop(struct iwn_softc *); static int iwn_init(struct ifnet *); static void iwn_stop(struct ifnet *, int); + +/* XXX MCLGETI alternative */ +static struct mbuf *MCLGETIalt(struct iwn_softc *, int, + struct ifnet *, u_int); +#ifdef IWN_USE_RBUF +static struct iwn_rbuf *iwn_alloc_rbuf(struct iwn_softc *); +static void iwn_free_rbuf(struct mbuf *, void *, size_t, void *); +static int iwn_alloc_rpool(struct iwn_softc *); +static void iwn_free_rpool(struct iwn_softc *); +#endif + +/* XXX needed by iwn_scan */ +static u_int8_t *ieee80211_add_ssid(u_int8_t *, const u_int8_t *, u_int); +static u_int8_t *ieee80211_add_rates(u_int8_t *, + const struct ieee80211_rateset *); +static u_int8_t *ieee80211_add_xrates(u_int8_t *, + const struct ieee80211_rateset *); + static void iwn_fix_channel(struct ieee80211com *, struct mbuf *); -static bool iwn_resume(device_t, const pmf_qual_t *); -#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) @@ -281,15 +321,11 @@ int iwn_debug = 0; #define DPRINTF(x) #define DPRINTFN(n, x) #endif -#ifdef IWN_DEBUG -static void iwn4965_print_power_group(struct iwn_softc *, int); -#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, @@ -303,8 +339,8 @@ static const struct iwn_hal iwn4965_hal iwn4965_ampdu_tx_start, iwn4965_ampdu_tx_stop, #endif - &iwn4965_sensitivity_limits, IWN4965_NTXQUEUES, + IWN4965_NDMACHNLS, IWN4965_ID_BROADCAST, IWN4965_RXONSZ, IWN4965_SCHEDSZ, @@ -318,7 +354,6 @@ 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, @@ -332,8 +367,8 @@ static const struct iwn_hal iwn5000_hal iwn5000_ampdu_tx_start, iwn5000_ampdu_tx_stop, #endif - &iwn5000_sensitivity_limits, IWN5000_NTXQUEUES, + IWN5000_NDMACHNLS, IWN5000_ID_BROADCAST, IWN5000_RXONSZ, IWN5000_SCHEDSZ, @@ -344,8 +379,7 @@ static const struct iwn_hal iwn5000_hal }; CFATTACH_DECL_NEW(iwn, sizeof(struct iwn_softc), iwn_match, iwn_attach, - iwn_detach, NULL); - + iwn_detach, NULL); static int iwn_match(device_t parent, cfdata_t match __unused, void *aux) @@ -375,11 +409,14 @@ iwn_attach(device_t parent __unused, dev char devinfo[256]; pci_intr_handle_t ih; pcireg_t memtype, reg; - int i, error, revision; + int i, error; + int revision; sc->sc_dev = self; sc->sc_pct = pa->pa_pc; sc->sc_pcitag = pa->pa_tag; + sc->sc_dmat = pa->pa_dmat; + mutex_init(&sc->sc_mtx, MUTEX_DEFAULT, IPL_NONE); callout_init(&sc->calib_to, 0); callout_setfunc(&sc->calib_to, iwn_calib_timeout, sc); @@ -390,12 +427,12 @@ iwn_attach(device_t parent __unused, dev /* * Get the offset of the PCI Express Capability Structure in PCI - * Configuration Space (the vendor driver hard-codes it as E0h.) + * Configuration Space. */ error = pci_get_capability(sc->sc_pct, sc->sc_pcitag, PCI_CAP_PCIEXPRESS, &sc->sc_cap_off, NULL); if (error == 0) { - aprint_error_dev(self, "PCIe capability structure not found!\n"); + aprint_error(": PCIe capability structure not found!\n"); return; } @@ -404,42 +441,33 @@ iwn_attach(device_t parent __unused, dev reg &= ~0xff00; pci_conf_write(sc->sc_pct, sc->sc_pcitag, 0x40, reg); - /* enable bus-mastering */ + /* Enable bus-mastering and hardware bug workaround. */ + /* XXX verify the bus-mastering is really needed (not in OpenBSD) */ reg = pci_conf_read(sc->sc_pct, sc->sc_pcitag, PCI_COMMAND_STATUS_REG); reg |= PCI_COMMAND_MASTER_ENABLE; + if (reg & PCI_COMMAND_INTERRUPT_DISABLE) { + DPRINTF(("PCIe INTx Disable set\n")); + reg &= ~PCI_COMMAND_INTERRUPT_DISABLE; + } pci_conf_write(sc->sc_pct, sc->sc_pcitag, PCI_COMMAND_STATUS_REG, reg); - /* map the register window */ 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, &sc->sc_sh, NULL, &sc->sc_sz); if (error != 0) { - aprint_error_dev(self, "could not map memory space\n"); + aprint_error(": can't map mem space\n"); return; } -#if 1 - sc->sc_dmat = pa->pa_dmat; -#else - /* XXX may not be needed */ - if (bus_dmatag_subregion(pa->pa_dmat, 0, 3 << 30, - &(sc->sc_dmat), BUS_DMA_NOWAIT) != 0) { - aprint_error_dev(self, - "WARNING: failed to restrict dma range, " - "falling back to parent bus dma range\n"); - sc->sc_dmat = pa->pa_dmat; - } -#endif /* Install interrupt handler. */ if (pci_intr_map(pa, &ih) != 0) { - aprint_error_dev(self, "could not map interrupt\n"); + aprint_error(": can't 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); - if (sc->sc_ih == NULL) { - aprint_error_dev(self, "could not establish interrupt"); + aprint_error(": can't establish interrupt"); if (intrstr != NULL) aprint_error(" at %s", intrstr); aprint_error("\n"); @@ -448,77 +476,83 @@ iwn_attach(device_t parent __unused, dev aprint_normal_dev(self, "interrupting at %s\n", intrstr); /* Attach Hardware Abstraction Layer. */ - if ((hal = iwn_hal_attach(sc)) == NULL) + hal = iwn_hal_attach(sc, PCI_PRODUCT(pa->pa_id)); + if (hal == NULL) return; - /* Power ON adapter. */ - if ((error = hal->apm_init(sc)) != 0) { - aprint_error_dev(self, "could not power ON adapter\n"); + if ((error = iwn_hw_prepare(sc)) != 0) { + aprint_error(": hardware not ready\n"); return; } /* Read MAC address, channels, etc from EEPROM. */ if ((error = iwn_read_eeprom(sc)) != 0) { - aprint_error_dev(self, "could not read EEPROM\n"); + aprint_error(": could not read EEPROM\n"); return; } /* Allocate DMA memory for firmware transfers. */ if ((error = iwn_alloc_fwmem(sc)) != 0) { - aprint_error_dev(self, - "could not allocate memory for firmware\n"); + aprint_error(": could not allocate memory for firmware\n"); return; } /* Allocate "Keep Warm" page. */ if ((error = iwn_alloc_kw(sc)) != 0) { - aprint_error_dev(self, "could not allocate keep warm page\n"); + aprint_error(": could not allocate keep warm page\n"); goto fail1; } + /* Allocate ICT table for 5000 Series. */ + if (sc->hw_type != IWN_HW_REV_TYPE_4965 && + (error = iwn_alloc_ict(sc)) != 0) { + aprint_error(": could not allocate ICT table\n"); + goto fail2; + } + /* Allocate TX scheduler "rings". */ if ((error = iwn_alloc_sched(sc)) != 0) { - aprint_error_dev(self, - "could not allocate TX scheduler rings\n"); - goto fail2; + aprint_error(": could not allocate TX scheduler rings\n"); + goto fail3; } +#ifdef IWN_USE_RBUF /* Allocate RX buffers. */ if ((error = iwn_alloc_rpool(sc)) != 0) { aprint_error_dev(self, "could not allocate RX buffers\n"); goto fail3; } +#endif /* Allocate TX rings (16 on 4965AGN, 20 on 5000.) */ for (i = 0; i < hal->ntxqs; i++) { - struct iwn_tx_ring *txq = &sc->txq[i]; - error = iwn_alloc_tx_ring(sc, txq, IWN_TX_RING_COUNT, i); - if (error != 0) { - aprint_error_dev(self, - "could not allocate TX ring %d\n", i); + if ((error = iwn_alloc_tx_ring(sc, &sc->txq[i], i)) != 0) { + aprint_error(": could not allocate TX ring %d\n", i); goto fail4; } } /* Allocate RX ring. */ - if (iwn_alloc_rx_ring(sc, &sc->rxq) != 0) { - aprint_error_dev(self, "could not allocate RX ring\n"); + if ((error = iwn_alloc_rx_ring(sc, &sc->rxq)) != 0) { + aprint_error(": could not allocate RX ring\n"); goto fail4; } - /* Power OFF adapter. */ - iwn_apm_stop(sc); /* Clear pending interrupts. */ IWN_WRITE(sc, IWN_INT, 0xffffffff); - aprint_normal_dev(self, "MIMO %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; - - /* Set the state of the RF kill switch */ - sc->sc_radio = (IWN_READ(sc, IWN_GP_CNTRL) & IWN_GP_CNTRL_RFKILL); + /* Count the number of available chains. */ + sc->ntxchains = + ((sc->txchainmask >> 2) & 1) + + ((sc->txchainmask >> 1) & 1) + + ((sc->txchainmask >> 0) & 1); + sc->nrxchains = + ((sc->rxchainmask >> 2) & 1) + + ((sc->rxchainmask >> 1) & 1) + + ((sc->rxchainmask >> 0) & 1); + aprint_normal_dev(self, "MIMO %dT%dR, %.4s, address %s\n", + sc->ntxchains, sc->nrxchains, sc->eeprom_domain, + ether_sprintf(ic->ic_myaddr)); ic->ic_ifp = ifp; ic->ic_phytype = IEEE80211_T_OFDM; /* not only, but not used */ @@ -526,6 +560,8 @@ iwn_attach(device_t parent __unused, dev ic->ic_state = IEEE80211_S_INIT; /* Set device capabilities. */ + /* XXX OpenBSD has IEEE80211_C_WEP, IEEE80211_C_RSN, + * and IEEE80211_C_PMGT too. */ ic->ic_caps = IEEE80211_C_IBSS | /* IBSS mode support */ IEEE80211_C_WPA | /* 802.11i */ @@ -535,25 +571,47 @@ iwn_attach(device_t parent __unused, dev IEEE80211_C_SHPREAMBLE | /* short preamble supported */ IEEE80211_C_WME; /* 802.11e */ - /* Set supported rates. */ +#ifndef IEEE80211_NO_HT + /* Set HT capabilities. */ + ic->ic_htcaps = +#if IWN_RBUF_SIZE == 8192 + IEEE80211_HTCAP_AMSDU7935 | +#endif + IEEE80211_HTCAP_CBW20_40 | + IEEE80211_HTCAP_SGI20 | + IEEE80211_HTCAP_SGI40; + if (sc->hw_type != IWN_HW_REV_TYPE_4965) + ic->ic_htcaps |= IEEE80211_HTCAP_GF; + if (sc->hw_type == IWN_HW_REV_TYPE_6050) + ic->ic_htcaps |= IEEE80211_HTCAP_SMPS_DYN; + else + ic->ic_htcaps |= IEEE80211_HTCAP_SMPS_DIS; +#endif /* !IEEE80211_NO_HT */ + + /* Set supported legacy rates. */ ic->ic_sup_rates[IEEE80211_MODE_11B] = iwn_rateset_11b; ic->ic_sup_rates[IEEE80211_MODE_11G] = iwn_rateset_11g; if (sc->sc_flags & IWN_FLAG_HAS_5GHZ) { ic->ic_sup_rates[IEEE80211_MODE_11A] = iwn_rateset_11a; } +#ifndef IEEE80211_NO_HT + /* Set supported HT rates. */ + ic->ic_sup_mcs[0] = 0xff; + if (sc->nrxchains > 1) /* MCS 0-7 */ + ic->ic_sup_mcs[1] = 0xff; /* MCS 7-15 */ + if (sc->nrxchains > 2) + ic->ic_sup_mcs[2] = 0xff; /* MCS 16-23 */ +#endif /* IBSS channel undefined for now. */ ic->ic_ibss_chan = &ic->ic_channels[0]; - memset(ic->ic_des_essid, 0, IEEE80211_NWID_LEN); - ic->ic_des_esslen = 0; - ifp->if_softc = sc; ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; - ifp->if_stop = iwn_stop; ifp->if_init = iwn_init; ifp->if_ioctl = iwn_ioctl; ifp->if_start = iwn_start; + ifp->if_stop = iwn_stop; ifp->if_watchdog = iwn_watchdog; IFQ_SET_READY(&ifp->if_snd); memcpy(ifp->if_xname, device_xname(self), IFNAMSIZ); @@ -562,12 +620,11 @@ iwn_attach(device_t parent __unused, dev ieee80211_ifattach(ic); ic->ic_node_alloc = iwn_node_alloc; ic->ic_newassoc = iwn_newassoc; - ic->ic_wme.wme_update = iwn_wme_update; -#ifdef notyet - ic->ic_updateedca = iwn_updateedca; - ic->ic_set_key = iwn_set_key; - ic->ic_delete_key = iwn_delete_key; +#ifdef IWN_HWCRYPTO + ic->ic_crypto.cs_key_set = iwn_set_key; + ic->ic_crypto.cs_key_delete = iwn_delete_key; #endif + ic->ic_wme.wme_update = iwn_wme_update; #ifndef IEEE80211_NO_HT ic->ic_ampdu_rx_start = iwn_ampdu_rx_start; ic->ic_ampdu_rx_stop = iwn_ampdu_rx_stop; @@ -583,13 +640,22 @@ iwn_attach(device_t parent __unused, dev sc->amrr.amrr_min_success_threshold = 1; sc->amrr.amrr_max_success_threshold = 15; +#ifndef SMALL_KERNEL + iwn_sensor_attach(sc); +#endif + iwn_radiotap_attach(sc); + + /* + * XXX for NetBSD, OpenBSD timeout_set replaced by + * callout_init and callout_setfunc, above. + */ + if (pmf_device_register(self, NULL, iwn_resume)) pmf_class_network_register(self, ifp); else aprint_error_dev(self, "couldn't establish power handler\n"); - iwn_radiotap_attach(sc); - + /* XXX NetBSD add call to ieee80211_announce for dmesg. */ ieee80211_announce(ic); return; @@ -597,136 +663,140 @@ iwn_attach(device_t parent __unused, dev /* Free allocated memory if something failed during attachment. */ fail4: while (--i >= 0) iwn_free_tx_ring(sc, &sc->txq[i]); +#ifdef IWN_USE_RBUF iwn_free_rpool(sc); -fail3: iwn_free_sched(sc); +#endif + iwn_free_sched(sc); +fail3: if (sc->ict != NULL) + iwn_free_ict(sc); fail2: iwn_free_kw(sc); fail1: iwn_free_fwmem(sc); } -static int -iwn_detach(device_t self, int flags __unused) -{ - struct iwn_softc *sc = device_private(self); - struct ifnet *ifp = sc->sc_ic.ic_ifp; - int ac; - - iwn_stop(ifp, 1); - if (ifp != NULL) - bpf_ops->bpf_detach(ifp); - ieee80211_ifdetach(&sc->sc_ic); - if (ifp != NULL) - if_detach(ifp); - - for (ac = 0; ac < sc->sc_hal->ntxqs; ac++) - iwn_free_tx_ring(sc, &sc->txq[ac]); - iwn_free_rx_ring(sc, &sc->rxq); - iwn_free_rpool(sc); - iwn_free_sched(sc); - - if (sc->sc_ih != NULL) { - pci_intr_disestablish(sc->sc_pct, sc->sc_ih); - sc->sc_ih = NULL; - } - - bus_space_unmap(sc->sc_st, sc->sc_sh, sc->sc_sz); - - return 0; -} - const struct iwn_hal * -iwn_hal_attach(struct iwn_softc *sc) +iwn_hal_attach(struct iwn_softc *sc, pci_product_id_t pid) { 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->fwname = "iwlwifi-4965-1.ucode"; - sc->critical_temp = IWN_CTOK(110); - sc->txantmsk = IWN_ANT_A | IWN_ANT_B; - sc->rxantmsk = IWN_ANT_ABC; - sc->ntxchains = 2; - sc->nrxchains = 3; + sc->limits = &iwn4965_sensitivity_limits; + sc->fwname = "iwlwifi-4965-2.ucode"; + sc->txchainmask = IWN_ANT_AB; + sc->rxchainmask = IWN_ANT_ABC; break; case IWN_HW_REV_TYPE_5100: sc->sc_hal = &iwn5000_hal; - sc->fwname = "iwlwifi-5000-1.ucode"; - sc->critical_temp = 110; - sc->txantmsk = IWN_ANT_B; - sc->rxantmsk = IWN_ANT_A | IWN_ANT_B; - sc->ntxchains = 1; - sc->nrxchains = 2; + sc->limits = &iwn5000_sensitivity_limits; + sc->fwname = "iwlwifi-5000-2.ucode"; + sc->txchainmask = IWN_ANT_B; + sc->rxchainmask = IWN_ANT_AB; break; case IWN_HW_REV_TYPE_5150: sc->sc_hal = &iwn5000_hal; - sc->fwname = "iwlwifi-5150-1.ucode"; - /* NB: critical temperature will be read from EEPROM. */ - sc->txantmsk = IWN_ANT_A; - sc->rxantmsk = IWN_ANT_A | IWN_ANT_B; - sc->ntxchains = 1; - sc->nrxchains = 2; + sc->limits = &iwn5150_sensitivity_limits; + sc->fwname = "iwlwifi-5150-2.ucode"; + sc->txchainmask = IWN_ANT_A; + sc->rxchainmask = IWN_ANT_AB; break; case IWN_HW_REV_TYPE_5300: case IWN_HW_REV_TYPE_5350: sc->sc_hal = &iwn5000_hal; - sc->fwname = "iwlwifi-5000-1.ucode"; - sc->critical_temp = 110; - sc->txantmsk = sc->rxantmsk = IWN_ANT_ABC; - sc->ntxchains = sc->nrxchains = 3; + sc->limits = &iwn5000_sensitivity_limits; + sc->fwname = "iwlwifi-5000-2.ucode"; + sc->txchainmask = IWN_ANT_ABC; + sc->rxchainmask = IWN_ANT_ABC; break; -#ifdef notyet case IWN_HW_REV_TYPE_1000: sc->sc_hal = &iwn5000_hal; - sc->fwname = "iwn-1000"; - sc->critical_temp = 110; - sc->txantmsk = IWN_ANT_A; - sc->rxantmsk = IWN_ANT_A | IWN_ANT_B; - sc->ntxchains = 1; - sc->nrxchains = 2; + sc->limits = &iwn1000_sensitivity_limits; + sc->fwname = "iwlwifi-1000-3.ucode"; + sc->txchainmask = IWN_ANT_A; + sc->rxchainmask = IWN_ANT_AB; break; case IWN_HW_REV_TYPE_6000: sc->sc_hal = &iwn5000_hal; - sc->fwname = "iwn-6000"; - sc->critical_temp = 110; - sc->txantmsk = IWN_ANT_ABC; - sc->rxantmsk = IWN_ANT_ABC; - sc->ntxchains = 3; - sc->nrxchains = 3; + sc->limits = &iwn6000_sensitivity_limits; + sc->fwname = "iwlwifi-6000-4.ucode"; + switch (pid) { + case PCI_PRODUCT_INTEL_WIFI_LINK_6000_IPA_1: + case PCI_PRODUCT_INTEL_WIFI_LINK_6000_IPA_2: + sc->sc_flags |= IWN_FLAG_INTERNAL_PA; + sc->txchainmask = IWN_ANT_BC; + sc->rxchainmask = IWN_ANT_BC; + break; + default: + sc->txchainmask = IWN_ANT_ABC; + sc->rxchainmask = IWN_ANT_ABC; + break; + } break; case IWN_HW_REV_TYPE_6050: sc->sc_hal = &iwn5000_hal; - sc->fwname = "iwn-6050"; - sc->critical_temp = 110; - sc->txantmsk = IWN_ANT_ABC; - sc->rxantmsk = IWN_ANT_ABC; - sc->ntxchains = 3; - sc->nrxchains = 3; + sc->limits = &iwn6000_sensitivity_limits; + sc->fwname = "iwlwifi-6050-2.ucode"; + sc->txchainmask = IWN_ANT_AB; + sc->rxchainmask = IWN_ANT_AB; + break; + case IWN_HW_REV_TYPE_6005: + sc->sc_hal = &iwn5000_hal; + sc->limits = &iwn6000_sensitivity_limits; + sc->fwname = "iwlwifi-6005-2.ucode"; + sc->txchainmask = IWN_ANT_AB; + sc->rxchainmask = IWN_ANT_AB; break; -#endif default: - printf(": adapter type %d not supported\n", sc->hw_type); + aprint_normal(": adapter type %d not supported\n", sc->hw_type); return NULL; } return sc->sc_hal; } -#if 0 +#ifndef SMALL_KERNEL /* - * Attach the adapter's on-board thermal sensor to the sensors framework. + * Attach the adapter on-board thermal sensor to the sensors framework. */ -void +static void 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; + int error; + + sc->sc_sensor.units = ENVSYS_STEMP; +#if 0 + /* XXX something like this ought to work */ + sc->sc_sensor.flags = ENVSYS_FMONLIMITS | ENVSYS_FMONNOTSUPP; + sc->sc_sensor.limits.sel_critmax = IWN_CTOK(110); +#endif + (void)strlcpy(sc->sc_sensor.desc, + "temperature", sizeof(sc->sc_sensor.desc)); + /* Temperature is not valid unless interface is up. */ - sc->sensor.value = 0; - sc->sensor.flags = SENSOR_FINVALID; - sensor_attach(&sc->sensordev, &sc->sensor); - sensordev_install(&sc->sensordev); + sc->sc_sensor.value_cur = 0; + sc->sc_sensor.state = ENVSYS_SINVALID; + + sc->sc_sme = sysmon_envsys_create(); + + /* Initialize sensor */ + if (sysmon_envsys_sensor_attach(sc->sc_sme, &sc->sc_sensor)) { + sysmon_envsys_destroy(sc->sc_sme); + return; + } + + /* + * Hook into the System Monitor. + */ + sc->sc_sme->sme_name = device_xname(sc->sc_dev); + sc->sc_sme->sme_flags = SME_DISABLE_REFRESH; + + if ((error = sysmon_envsys_register(sc->sc_sme)) != 0) { + aprint_error_dev(sc->sc_dev, + "unable to register with sysmon (%d)\n", error); + sysmon_envsys_destroy(sc->sc_sme); + return; + } } -#endif /* 0 */ +#endif /* * Attach the interface to 802.11 radiotap. @@ -736,8 +806,8 @@ iwn_radiotap_attach(struct iwn_softc *sc { struct ifnet *ifp = sc->sc_ic.ic_ifp; - bpf_ops->bpf_attach(ifp, DLT_IEEE802_11_RADIO, - sizeof(struct ieee80211_frame) + IEEE80211_RADIOTAP_HDRLEN, + bpf_attach2(ifp, DLT_IEEE802_11_RADIO, + sizeof (struct ieee80211_frame) + IEEE80211_RADIOTAP_HDRLEN, &sc->sc_drvbpf); sc->sc_rxtap_len = sizeof sc->sc_rxtapu; @@ -749,86 +819,85 @@ iwn_radiotap_attach(struct iwn_softc *sc sc->sc_txtap.wt_ihdr.it_present = htole32(IWN_TX_RADIOTAP_PRESENT); } -#if 0 /* XXX */ -/* - * Build a beacon frame that the firmware will broadcast periodically in - * IBSS or HostAP modes. - */ static int -iwn_setup_beacon(struct iwn_softc *sc, struct ieee80211_node *ni) +iwn_detach(device_t self, int flags __unused) { - struct ieee80211com *ic = &sc->sc_ic; - struct iwn_tx_ring *ring = &sc->txq[4]; - struct iwn_tx_desc *desc; - struct iwn_tx_data *data; - struct iwn_tx_cmd *cmd; - struct iwn_cmd_beacon *bcn; - struct ieee80211_beacon_offsets bo; - struct mbuf *m0; - bus_addr_t paddr; - int error; + struct iwn_softc *sc = device_private(self); + struct ifnet *ifp = sc->sc_ic.ic_ifp; + int qid; - desc = &ring->desc[ring->cur]; - data = &ring->data[ring->cur]; - m0 = ieee80211_beacon_alloc(ic, ni, &bo); - if (m0 == NULL) { - aprint_error_dev(sc->sc_dev, "could not allocate beacon frame\n" -); - return ENOMEM; - } - cmd = &ring->cmd[ring->cur]; - cmd->code = IWN_CMD_SET_BEACON; - cmd->flags = 0; - cmd->qid = ring->qid; - cmd->idx = ring->cur; + callout_stop(&sc->calib_to); - bcn = (struct iwn_cmd_beacon *)cmd->data; - memset(bcn, 0, sizeof (struct iwn_cmd_beacon)); - bcn->id = sc->sc_hal->broadcast_id; - bcn->lifetime = htole32(IWN_LIFETIME_INFINITE); - bcn->len = htole16(m0->m_pkthdr.len); -#if 0 - XXX - bcn->rate = (ic->ic_curmode == IEEE80211_MODE_11A) ? - iwn_plcp_signal(12) : iwn_plcp_signal(2); + /* Uninstall interrupt handler. */ + if (sc->sc_ih != NULL) + pci_intr_disestablish(sc->sc_pct, sc->sc_ih); + + /* Free DMA resources. */ + iwn_free_rx_ring(sc, &sc->rxq); + for (qid = 0; qid < sc->sc_hal->ntxqs; qid++) + iwn_free_tx_ring(sc, &sc->txq[qid]); +#ifdef IWN_USE_RBUF + iwn_free_rpool(sc); #endif - bcn->flags2 = 0x2; /* RATE_MCS_CCK_MSK */ - bcn->flags = htole32(IWN_TX_AUTO_SEQ | IWN_TX_INSERT_TSTAMP; - // XXX | IWN_TX_USE_NODE_RATE); + iwn_free_sched(sc); + iwn_free_kw(sc); + if (sc->ict != NULL) + iwn_free_ict(sc); + iwn_free_fwmem(sc); - /* save and trim IEEE802.11 header */ - m_copydata(m0, 0, sizeof (struct ieee80211_frame), (void *)&bcn->wh); - m_adj(m0, sizeof (struct ieee80211_frame)); + bus_space_unmap(sc->sc_st, sc->sc_sh, sc->sc_sz); - /* assume beacon frame is contiguous */ - error = bus_dmamap_load_mbuf(sc->sc_dmat, data->map, m0, - BUS_DMA_READ | BUS_DMA_NOWAIT); - if (error) { - aprint_error_dev(sc->sc_dev, "could not map beacon\n"); - m_freem(m0); - return error; - } +#ifndef SMALL_KERNEL + /* Detach the thermal sensor. */ + sysmon_envsys_sensor_detach(sc->sc_sme, &sc->sc_sensor); + sysmon_envsys_destroy(sc->sc_sme); +#endif - data->m = m0; + ieee80211_ifdetach(&sc->sc_ic); + if_detach(ifp); - /* first scatter/gather segment is used by the beacon command */ - paddr = ring->cmd_dma.paddr + ring->cur * sizeof (struct iwn_tx_cmd); + return 0; +} - IWN_SET_DESC_NSEGS(desc, 2); - IWN_SET_DESC_SEG(desc, 0, paddr , 4 + sizeof(struct iwn_cmd_beacon)); - IWN_SET_DESC_SEG(desc, 1, data->map->dm_segs[0].ds_addr, - data->map->dm_segs[1].ds_len); +#if 0 +/* + * XXX Investigate if clearing the PCI retry timeout could eliminate + * the repeated scan calls. Also the calls to if_init and if_start + * are similar to the effect of adding the call to ifioctl_common . + */ +static void +iwn_power(int why, void *arg) +{ + struct iwn_softc *sc = arg; + struct ifnet *ifp; + pcireg_t reg; + int s; - bus_dmamap_sync(sc->sc_dmat, data->map, 0, - data->map->dm_mapsize /* calc? */, BUS_DMASYNC_PREWRITE); + if (why != PWR_RESUME) + return; - /* kick cmd ring */ - ring->cur = (ring->cur + 1) % IWN_TX_RING_COUNT; - IWN_WRITE(sc, IWN_TX_WIDX, ring->qid << 8 | ring->cur); - return 0; + /* 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; + if (ifp->if_flags & IFF_UP) { + ifp->if_init(ifp); + if (ifp->if_flags & IFF_RUNNING) + ifp->if_start(ifp); + } + splx(s); } #endif +static bool +iwn_resume(device_t dv, const pmf_qual_t *qual) +{ + return true; +} + static int iwn_nic_lock(struct iwn_softc *sc) { @@ -858,6 +927,7 @@ static __inline uint32_t iwn_prph_read(struct iwn_softc *sc, uint32_t addr) { IWN_WRITE(sc, IWN_PRPH_RADDR, IWN_PRPH_DWORD | addr); + IWN_BARRIER_READ_WRITE(sc); return IWN_READ(sc, IWN_PRPH_RDATA); } @@ -865,6 +935,7 @@ static __inline void iwn_prph_write(struct iwn_softc *sc, uint32_t addr, uint32_t data) { IWN_WRITE(sc, IWN_PRPH_WADDR, IWN_PRPH_DWORD | addr); + IWN_BARRIER_WRITE(sc); IWN_WRITE(sc, IWN_PRPH_WDATA, data); } @@ -892,6 +963,7 @@ static __inline uint32_t iwn_mem_read(struct iwn_softc *sc, uint32_t addr) { IWN_WRITE(sc, IWN_MEM_RADDR, addr); + IWN_BARRIER_READ_WRITE(sc); return IWN_READ(sc, IWN_MEM_RDATA); } @@ -899,6 +971,7 @@ static __inline void iwn_mem_write(struct iwn_softc *sc, uint32_t addr, uint32_t data) { IWN_WRITE(sc, IWN_MEM_WADDR, addr); + IWN_BARRIER_WRITE(sc); IWN_WRITE(sc, IWN_MEM_WDATA, data); } @@ -958,17 +1031,72 @@ iwn_eeprom_unlock(struct iwn_softc *sc) IWN_CLRBITS(sc, IWN_HW_IF_CONFIG, IWN_HW_IF_CONFIG_EEPROM_LOCKED); } +/* + * 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 +iwn_init_otprom(struct iwn_softc *sc) +{ + uint16_t prev = 0, base, next; + int count, error; + + /* Wait for clock stabilization before accessing prph. */ + if ((error = iwn_clock_wait(sc)) != 0) + return error; + + if ((error = iwn_nic_lock(sc)) != 0) + return error; + iwn_prph_setbits(sc, IWN_APMG_PS, IWN_APMG_PS_RESET_REQ); + DELAY(5); + iwn_prph_clrbits(sc, IWN_APMG_PS, IWN_APMG_PS_RESET_REQ); + iwn_nic_unlock(sc); + + /* Set auto clock gate disable bit for HW with OTP shadow RAM. */ + if (sc->hw_type != IWN_HW_REV_TYPE_1000) { + IWN_SETBITS(sc, IWN_DBG_LINK_PWR_MGMT, + IWN_RESET_LINK_PWR_MGMT_DIS); + } + IWN_CLRBITS(sc, IWN_EEPROM_GP, IWN_EEPROM_GP_IF_OWNER); + /* Clear ECC status. */ + IWN_SETBITS(sc, IWN_OTP_GP, + IWN_OTP_GP_ECC_CORR_STTS | IWN_OTP_GP_ECC_UNCORR_STTS); + + /* + * Find the block before last block (contains the EEPROM image) + * for HW without OTP shadow RAM. + */ + if (sc->hw_type == IWN_HW_REV_TYPE_1000) { + /* Switch to absolute addressing mode. */ + IWN_CLRBITS(sc, IWN_OTP_GP, IWN_OTP_GP_RELATIVE_ACCESS); + base = 0; + for (count = 0; count < IWN1000_OTP_NBLOCKS; count++) { + error = iwn_read_prom_data(sc, base, &next, 2); + if (error != 0) + return error; + if (next == 0) /* End of linked-list. */ + break; + prev = base; + base = le16toh(next); + } + if (count == 0 || count == IWN1000_OTP_NBLOCKS) + return EIO; + /* Skip "next" word. */ + sc->prom_base = prev + 1; + } + return 0; +} + static int iwn_read_prom_data(struct iwn_softc *sc, uint32_t addr, void *data, int count) { uint8_t *out = data; - uint32_t val; + uint32_t val, tmp; int ntries; + addr += sc->prom_base; 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) @@ -976,9 +1104,24 @@ iwn_read_prom_data(struct iwn_softc *sc, DELAY(5); } if (ntries == 10) { - aprint_error_dev(sc->sc_dev, "could not read EEPROM\n"); + aprint_error_dev(sc->sc_dev, + "timeout reading ROM at 0x%x\n", addr); return ETIMEDOUT; } + if (sc->sc_flags & IWN_FLAG_HAS_OTPROM) { + /* OTPROM, check for ECC errors. */ + tmp = IWN_READ(sc, IWN_OTP_GP); + if (tmp & IWN_OTP_GP_ECC_UNCORR_STTS) { + aprint_error_dev(sc->sc_dev, + "OTPROM ECC error at 0x%x\n", addr); + return EIO; + } + if (tmp & IWN_OTP_GP_ECC_CORR_STTS) { + /* Correctable ECC error, clear bit. */ + IWN_SETBITS(sc, IWN_OTP_GP, + IWN_OTP_GP_ECC_CORR_STTS); + } + } *out++ = val >> 16; if (count > 1) *out++ = val >> 24; @@ -988,30 +1131,34 @@ iwn_read_prom_data(struct iwn_softc *sc, static 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) + bus_size_t size, bus_size_t alignment) { int nsegs, error; dma->tag = tag; dma->size = size; - error = bus_dmamap_create(tag, size, 1, size, 0, flags, &dma->map); + error = bus_dmamap_create(tag, size, 1, size, 0, BUS_DMA_NOWAIT, + &dma->map); if (error != 0) goto fail; error = bus_dmamem_alloc(tag, size, alignment, 0, &dma->seg, 1, &nsegs, - flags); + BUS_DMA_NOWAIT); /* XXX OpenBSD adds BUS_DMA_ZERO */ if (error != 0) goto fail; - error = bus_dmamem_map(tag, &dma->seg, 1, size, &dma->vaddr, flags); + error = bus_dmamem_map(tag, &dma->seg, 1, size, &dma->vaddr, + BUS_DMA_NOWAIT); /* XXX OpenBSD adds BUS_DMA_COHERENT */ if (error != 0) goto fail; - error = bus_dmamap_load(tag, dma->map, dma->vaddr, size, NULL, flags); + error = bus_dmamap_load(tag, dma->map, dma->vaddr, size, NULL, + BUS_DMA_NOWAIT); if (error != 0) goto fail; + /* XXX Presumably needed because of missing BUS_DMA_ZERO, above. */ memset(dma->vaddr, 0, size); bus_dmamap_sync(tag, dma->map, 0, size, BUS_DMASYNC_PREWRITE); @@ -1045,14 +1192,9 @@ iwn_dma_contig_free(struct iwn_dma_info static int iwn_alloc_sched(struct iwn_softc *sc) { - int error; /* TX scheduler rings must be aligned on a 1KB boundary. */ - error = iwn_dma_contig_alloc(sc->sc_dmat, &sc->sched_dma, - (void **)&sc->sched, sc->sc_hal->schedsz, 1024, BUS_DMA_NOWAIT); - if (error != 0) - aprint_error_dev(sc->sc_dev, - "could not allocate shared area DMA memory\n"); - return error; + return iwn_dma_contig_alloc(sc->sc_dmat, &sc->sched_dma, + (void **)&sc->sched, sc->sc_hal->schedsz, 1024); } static void @@ -1064,9 +1206,9 @@ iwn_free_sched(struct iwn_softc *sc) static int iwn_alloc_kw(struct iwn_softc *sc) { - /* "Keep Warm" page must be aligned on a 16-byte boundary. */ + /* "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); + 4096); } static void @@ -1076,17 +1218,25 @@ iwn_free_kw(struct iwn_softc *sc) } static int +iwn_alloc_ict(struct iwn_softc *sc) +{ + /* ICT table must be aligned on a 4KB boundary. */ + return iwn_dma_contig_alloc(sc->sc_dmat, &sc->ict_dma, + (void **)&sc->ict, IWN_ICT_SIZE, 4096); +} + +static void +iwn_free_ict(struct iwn_softc *sc) +{ + iwn_dma_contig_free(&sc->ict_dma); +} + +static int iwn_alloc_fwmem(struct iwn_softc *sc) { - int error; /* Must be aligned on a 16-byte boundary. */ - error = iwn_dma_contig_alloc(sc->sc_dmat, &sc->fw_dma, NULL, - sc->sc_hal->fwsz, 16, BUS_DMA_NOWAIT); - if (error != 0) { - aprint_error_dev(sc->sc_dev, - "could not allocate firmware transfer area DMA memory\n"); - } - return error; + return iwn_dma_contig_alloc(sc->sc_dmat, &sc->fw_dma, NULL, + sc->sc_hal->fwsz, 16); } static void @@ -1095,93 +1245,18 @@ iwn_free_fwmem(struct iwn_softc *sc) iwn_dma_contig_free(&sc->fw_dma); } -static struct iwn_rbuf * -iwn_alloc_rbuf(struct iwn_softc *sc) +static int +iwn_alloc_rx_ring(struct iwn_softc *sc, struct iwn_rx_ring *ring) { - struct iwn_rbuf *rbuf; - mutex_enter(&sc->rxq.freelist_mtx); - - rbuf = SLIST_FIRST(&sc->rxq.freelist); - if (rbuf != NULL) { - SLIST_REMOVE_HEAD(&sc->rxq.freelist, next); - sc->rxq.nb_free_entries --; - } - mutex_exit(&sc->rxq.freelist_mtx); - return rbuf; -} - -/* - * This is called automatically by the network stack when the mbuf to which - * our RX buffer is attached is freed. - */ -static void -iwn_free_rbuf(struct mbuf* m, void *buf, size_t size, void *arg) -{ - struct iwn_rbuf *rbuf = arg; - struct iwn_softc *sc = rbuf->sc; - - /* Put the RX buffer back in the free list. */ - mutex_enter(&sc->rxq.freelist_mtx); - SLIST_INSERT_HEAD(&sc->rxq.freelist, rbuf, next); - mutex_exit(&sc->rxq.freelist_mtx); - - sc->rxq.nb_free_entries ++; - if (__predict_true(m != NULL)) - pool_cache_put(mb_cache, m); -} - -static int -iwn_alloc_rpool(struct iwn_softc *sc) -{ - struct iwn_rx_ring *ring = &sc->rxq; - struct iwn_rbuf *rbuf; - int i, error; - - mutex_init(&ring->freelist_mtx, MUTEX_DEFAULT, IPL_NET); - - /* 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); - if (error != 0) { - aprint_error_dev(sc->sc_dev, - "could not allocate RX buffers DMA memory\n"); - return error; - } - /* ...and split it into chunks of IWN_RBUF_SIZE bytes. */ - SLIST_INIT(&ring->freelist); - for (i = 0; i < IWN_RBUF_COUNT; i++) { - rbuf = &ring->rbuf[i]; - - rbuf->sc = sc; /* Backpointer for callbacks. */ - rbuf->vaddr = (void *)((vaddr_t)ring->buf_dma.vaddr + i * IWN_RBUF_SIZE); - rbuf->paddr = ring->buf_dma.paddr + i * IWN_RBUF_SIZE; - - SLIST_INSERT_HEAD(&ring->freelist, rbuf, next); - } - ring->nb_free_entries = IWN_RBUF_COUNT; - return 0; -} - -static void -iwn_free_rpool(struct iwn_softc *sc) -{ - iwn_dma_contig_free(&sc->rxq.buf_dma); -} - -static int -iwn_alloc_rx_ring(struct iwn_softc *sc, struct iwn_rx_ring *ring) -{ - struct iwn_rx_data *data; bus_size_t size; - struct iwn_rbuf *rbuf; int i, error; ring->cur = 0; /* Allocate RX descriptors (256-byte aligned.) */ - size = IWN_RX_RING_COUNT * sizeof (struct iwn_rx_desc); + size = IWN_RX_RING_COUNT * sizeof (uint32_t); error = iwn_dma_contig_alloc(sc->sc_dmat, &ring->desc_dma, - (void **)&ring->desc, size, 256, BUS_DMA_NOWAIT); + (void **)&ring->desc, size, 256); if (error != 0) { aprint_error_dev(sc->sc_dev, "could not allocate RX ring DMA memory\n"); @@ -1190,8 +1265,7 @@ iwn_alloc_rx_ring(struct iwn_softc *sc, /* 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); + (void **)&ring->stat, sizeof (struct iwn_rx_status), 16); if (error != 0) { aprint_error_dev(sc->sc_dev, "could not allocate RX status DMA memory\n"); @@ -1202,40 +1276,31 @@ iwn_alloc_rx_ring(struct iwn_softc *sc, * Allocate and map RX buffers. */ for (i = 0; i < IWN_RX_RING_COUNT; i++) { - data = &ring->data[i]; + struct iwn_rx_data *data = &ring->data[i]; error = bus_dmamap_create(sc->sc_dmat, IWN_RBUF_SIZE, 1, - IWN_RBUF_SIZE, 0, BUS_DMA_NOWAIT, &data->map); + IWN_RBUF_SIZE, 0, BUS_DMA_NOWAIT | BUS_DMA_ALLOCNOW, + &data->map); if (error != 0) { aprint_error_dev(sc->sc_dev, "could not create RX buf DMA map\n"); goto fail; } - MGETHDR(data->m, M_DONTWAIT, MT_DATA); + + data->m = MCLGETIalt(sc, M_DONTWAIT, NULL, IWN_RBUF_SIZE); if (data->m == NULL) { aprint_error_dev(sc->sc_dev, "could not allocate RX mbuf\n"); - error = ENOMEM; - goto fail; - } - if ((rbuf = iwn_alloc_rbuf(sc)) == NULL) { - m_freem(data->m); - data->m = NULL; - aprint_error_dev(sc->sc_dev, - "could not allocate RX buffer\n"); - error = ENOMEM; + error = ENOBUFS; goto fail; } - /* Attach RX buffer to mbuf header. */ - MEXTADD(data->m, rbuf->vaddr, IWN_RBUF_SIZE, 0, iwn_free_rbuf, - rbuf); - data->m->m_flags |= M_EXT_RW; + error = bus_dmamap_load(sc->sc_dmat, data->map, - data->m->m_ext.ext_buf, IWN_RBUF_SIZE, NULL, - BUS_DMA_NOWAIT); + mtod(data->m, void *), IWN_RBUF_SIZE, NULL, + BUS_DMA_NOWAIT | BUS_DMA_READ); if (error != 0) { - aprint_error_dev(sc->sc_dev, "can't not map mbuf" - " (error %d)\n", error); + aprint_error_dev(sc->sc_dev, + "can't not map mbuf (error %d)\n", error); goto fail; } @@ -1243,8 +1308,8 @@ iwn_alloc_rx_ring(struct iwn_softc *sc, ring->desc[i] = htole32(data->map->dm_segs[0].ds_addr >> 8); } - bus_dmamap_sync(sc->sc_dmat, ring->desc_dma.map, - 0, ring->desc_dma.size, BUS_DMASYNC_PREWRITE); + bus_dmamap_sync(sc->sc_dmat, ring->desc_dma.map, 0, size, + BUS_DMASYNC_PREWRITE); return 0; @@ -1294,22 +1359,20 @@ iwn_free_rx_ring(struct iwn_softc *sc, s } static int -iwn_alloc_tx_ring(struct iwn_softc *sc, struct iwn_tx_ring *ring, int count, - int qid) +iwn_alloc_tx_ring(struct iwn_softc *sc, struct iwn_tx_ring *ring, int qid) { bus_addr_t paddr; - struct iwn_tx_data *data; - int i, error, size; + bus_size_t size; + int i, error; ring->qid = qid; - ring->count = count; ring->queued = 0; ring->cur = 0; /* Allocate TX descriptors (256-byte aligned.) */ - size = count * sizeof (struct iwn_tx_desc); + 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, 256, BUS_DMA_NOWAIT); + (void **)&ring->desc, size, 256); if (error != 0) { aprint_error_dev(sc->sc_dev, "could not allocate TX ring DMA memory\n"); @@ -1323,9 +1386,9 @@ iwn_alloc_tx_ring(struct iwn_softc *sc, if (qid > 4) return 0; - size = count * sizeof (struct iwn_tx_cmd); + 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); + (void **)&ring->cmd, size, 4); if (error != 0) { aprint_error_dev(sc->sc_dev, "could not allocate TX cmd DMA memory\n"); @@ -1333,9 +1396,8 @@ iwn_alloc_tx_ring(struct iwn_softc *sc, } paddr = ring->cmd_dma.paddr; - - for (i = 0; i < count; i++) { - data = &ring->data[i]; + 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; @@ -1359,23 +1421,10 @@ fail: iwn_free_tx_ring(sc, ring); static void iwn_reset_tx_ring(struct iwn_softc *sc, struct iwn_tx_ring *ring) { - struct iwn_tx_data *data; - uint32_t tmp; - int i, ntries; + int i; - 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); - } - for (i = 0; i < ring->count; i++) { - data = &ring->data[i]; + for (i = 0; i < IWN_TX_RING_COUNT; i++) { + struct iwn_tx_data *data = &ring->data[i]; if (data->m != NULL) { bus_dmamap_sync(sc->sc_dmat, data->map, 0, @@ -1397,29 +1446,50 @@ iwn_reset_tx_ring(struct iwn_softc *sc, static void iwn_free_tx_ring(struct iwn_softc *sc, struct iwn_tx_ring *ring) { - struct iwn_tx_data *data; int i; iwn_dma_contig_free(&ring->desc_dma); iwn_dma_contig_free(&ring->cmd_dma); - if (ring->data != NULL) { - for (i = 0; i < ring->count; i++) { - data = &ring->data[i]; - - if (data->m != NULL) { - bus_dmamap_sync(sc->sc_dmat, data->map, 0, - data->map->dm_mapsize, BUS_DMASYNC_POSTWRITE); - bus_dmamap_unload(sc->sc_dmat, data->map); - m_freem(data->m); - } - if (data->map != NULL) - bus_dmamap_destroy(sc->sc_dmat, data->map); + for (i = 0; i < IWN_TX_RING_COUNT; i++) { + struct iwn_tx_data *data = &ring->data[i]; + + if (data->m != NULL) { + bus_dmamap_sync(sc->sc_dmat, data->map, 0, + data->map->dm_mapsize, BUS_DMASYNC_POSTWRITE); + bus_dmamap_unload(sc->sc_dmat, data->map); + m_freem(data->m); } - free(ring->data, M_DEVBUF); + if (data->map != NULL) + bus_dmamap_destroy(sc->sc_dmat, data->map); } } +static void +iwn5000_ict_reset(struct iwn_softc *sc) +{ + /* Disable interrupts. */ + IWN_WRITE(sc, IWN_INT_MASK, 0); + + /* Reset ICT table. */ + memset(sc->ict, 0, IWN_ICT_SIZE); + sc->ict_cur = 0; + + /* Set physical address of ICT table (4KB aligned.) */ + DPRINTF(("enabling ICT\n")); + IWN_WRITE(sc, IWN_DRAM_INT_TBL, IWN_DRAM_INT_TBL_ENABLE | + IWN_DRAM_INT_TBL_WRAP_CHECK | sc->ict_dma.paddr >> 12); + + /* Enable periodic RX interrupt. */ + sc->int_mask |= IWN_INT_RX_PERIODIC; + /* Switch to ICT interrupt mode in driver. */ + sc->sc_flags |= IWN_FLAG_USE_ICT; + + /* Re-enable interrupts. */ + IWN_WRITE(sc, IWN_INT, 0xffffffff); + IWN_WRITE(sc, IWN_INT_MASK, sc->int_mask); +} + static int iwn_read_eeprom(struct iwn_softc *sc) { @@ -1428,15 +1498,37 @@ iwn_read_eeprom(struct iwn_softc *sc) uint16_t val; int error; - if ((IWN_READ(sc, IWN_EEPROM_GP) & 0x6) == 0) { - aprint_error_dev(sc->sc_dev, "bad EEPROM signature\n"); + /* Check whether adapter has an EEPROM or an OTPROM. */ + if (sc->hw_type >= IWN_HW_REV_TYPE_1000 && + (IWN_READ(sc, IWN_OTP_GP) & IWN_OTP_GP_DEV_SEL_OTP)) + sc->sc_flags |= IWN_FLAG_HAS_OTPROM; + DPRINTF(("%s found\n", (sc->sc_flags & IWN_FLAG_HAS_OTPROM) ? + "OTPROM" : "EEPROM")); + + /* Adapter has to be powered on for EEPROM access to work. */ + if ((error = iwn_apm_init(sc)) != 0) { + aprint_error_dev(sc->sc_dev, + "could not power ON adapter\n"); + return error; + } + + if ((IWN_READ(sc, IWN_EEPROM_GP) & 0x7) == 0) { + aprint_error_dev(sc->sc_dev, + "bad ROM signature\n"); return EIO; } if ((error = iwn_eeprom_lock(sc)) != 0) { aprint_error_dev(sc->sc_dev, - "could not lock EEPROM (error=%d)\n", error); + "could not lock ROM (error=%d)\n", error); return error; } + if (sc->sc_flags & IWN_FLAG_HAS_OTPROM) { + if ((error = iwn_init_otprom(sc)) != 0) { + aprint_error_dev(sc->sc_dev, + "could not initialize OTPROM\n"); + return error; + } + } iwn_read_prom_data(sc, IWN_EEPROM_RFCFG, &val, 2); sc->rfcfg = le16toh(val); @@ -1448,6 +1540,8 @@ iwn_read_eeprom(struct iwn_softc *sc) /* Read adapter-specific information from EEPROM. */ hal->read_eeprom(sc); + iwn_apm_stop(sc); /* Power OFF adapter. */ + iwn_eeprom_unlock(sc); return 0; } @@ -1505,12 +1599,12 @@ iwn4965_print_power_group(struct iwn_sof 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); + aprint_normal("===band %d===\n", i); + aprint_normal("chan lo=%d, chan hi=%d\n", band->lo, band->hi); + aprint_normal("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 " + aprint_normal("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, @@ -1518,10 +1612,10 @@ iwn4965_print_power_group(struct iwn_sof chans[0].samples[c][j].pa_det); } } - printf("chan2 num=%d\n", chans[1].num); + aprint_normal("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 " + aprint_normal("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, @@ -1535,7 +1629,8 @@ iwn4965_print_power_group(struct iwn_sof static void iwn5000_read_eeprom(struct iwn_softc *sc) { - int32_t temp, volt, delta; + struct iwn5000_eeprom_calib_hdr hdr; + int32_t temp, volt; uint32_t base, addr; uint16_t val; int i; @@ -1552,18 +1647,26 @@ iwn5000_read_eeprom(struct iwn_softc *sc iwn_read_eeprom_channels(sc, i, addr); } + /* Read enhanced TX power information for 6000 Series. */ + if (sc->hw_type >= IWN_HW_REV_TYPE_6000) + iwn_read_eeprom_enhinfo(sc); + iwn_read_prom_data(sc, IWN5000_EEPROM_CAL, &val, 2); base = le16toh(val); + iwn_read_prom_data(sc, base, &hdr, sizeof hdr); + DPRINTF(("calib version=%u pa type=%u voltage=%u\n", + hdr.version, hdr.pa_type, le16toh(hdr.volt))); + sc->calib_ver = hdr.version; + if (sc->hw_type == IWN_HW_REV_TYPE_5150) { - /* Compute critical temperature (in Kelvin.) */ + /* Compute temperature offset. */ iwn_read_prom_data(sc, base + IWN5000_EEPROM_TEMP, &val, 2); temp = le16toh(val); iwn_read_prom_data(sc, base + IWN5000_EEPROM_VOLT, &val, 2); volt = le16toh(val); - delta = temp - (volt / -5); - sc->critical_temp = (IWN_CTOK(110) - delta) * -5; - DPRINTF(("temp=%d volt=%d delta=%dK\n", - temp, volt, delta)); + sc->temp_off = temp - (volt / -5); + DPRINTF(("temp=%d volt=%d offset=%dK\n", + temp, volt, sc->temp_off)); } else { /* Read crystal calibration. */ iwn_read_prom_data(sc, base + IWN5000_EEPROM_CRYSTAL, @@ -1629,14 +1732,46 @@ iwn_read_eeprom_channels(struct iwn_soft } } -/*ARGUSED*/ -static struct ieee80211_node * -iwn_node_alloc(struct ieee80211_node_table *nt __unused) +static void +iwn_read_eeprom_enhinfo(struct iwn_softc *sc) { - struct iwn_node *wn; + struct iwn_eeprom_enhinfo enhinfo[35]; + uint16_t val, base; + int8_t maxpwr; + int i; + + iwn_read_prom_data(sc, IWN5000_EEPROM_REG, &val, 2); + base = le16toh(val); + iwn_read_prom_data(sc, base + IWN6000_EEPROM_ENHINFO, + enhinfo, sizeof enhinfo); + + memset(sc->enh_maxpwr, 0, sizeof sc->enh_maxpwr); + for (i = 0; i < __arraycount(enhinfo); i++) { + if (enhinfo[i].chan == 0 || enhinfo[i].reserved != 0) + continue; /* Skip invalid entries. */ + + maxpwr = 0; + if (sc->txchainmask & IWN_ANT_A) + maxpwr = MAX(maxpwr, enhinfo[i].chain[0]); + if (sc->txchainmask & IWN_ANT_B) + maxpwr = MAX(maxpwr, enhinfo[i].chain[1]); + if (sc->txchainmask & IWN_ANT_C) + maxpwr = MAX(maxpwr, enhinfo[i].chain[2]); + if (sc->ntxchains == 2) + maxpwr = MAX(maxpwr, enhinfo[i].mimo2); + else if (sc->ntxchains == 3) + maxpwr = MAX(maxpwr, enhinfo[i].mimo3); + maxpwr /= 2; /* Convert half-dBm to dBm. */ + + DPRINTF(("enhinfo %d, maxpwr=%d\n", i, maxpwr)); + sc->enh_maxpwr[i] = maxpwr; + } +} - wn = malloc(sizeof (struct iwn_node), M_80211_NODE, M_NOWAIT | M_ZERO); - return (struct ieee80211_node *)wn; +static struct ieee80211_node * +iwn_node_alloc(struct ieee80211_node_table *ic __unused) +{ + return malloc(sizeof (struct iwn_node), M_80211_NODE, M_NOWAIT | M_ZERO); } static void @@ -1648,6 +1783,8 @@ iwn_newassoc(struct ieee80211_node *ni, int ridx, i; ieee80211_amrr_node_init(&sc->amrr, &wn->amn); + /* Start at lowest available bit-rate, AMRR will raise. */ + ni->ni_txrate = 0; for (i = 0; i < ni->ni_rates.rs_nrates; i++) { rate = ni->ni_rates.rs_rates[i] & IEEE80211_RATE_VAL; @@ -1656,9 +1793,6 @@ iwn_newassoc(struct ieee80211_node *ni, if (iwn_rates[ridx].rate == rate) break; wn->ridx[i] = ridx; - /* Initial TX rate <= 24Mbps. */ - if (rate <= 48) - ni->ni_txrate = i; } } @@ -1703,12 +1837,19 @@ iwn_newstate(struct ieee80211com *ic, en switch (nstate) { case IEEE80211_S_SCAN: - if (sc->is_scanning) + /* XXX Do not abort a running scan. */ + if (sc->sc_flags & IWN_FLAG_SCANNING) { + if (ic->ic_state != nstate) + aprint_error_dev(sc->sc_dev, "scan request(%d) " + "while scanning(%d) ignored\n", nstate, + ic->ic_state); break; - - sc->is_scanning = true; + } + + /* XXX Not sure if call and flags are needed. */ ieee80211_node_table_reset(&ic->ic_scan); ic->ic_flags |= IEEE80211_F_SCAN | IEEE80211_F_ASCAN; + sc->sc_flags |= IWN_FLAG_SCANNING; /* Make the link LED blink while we're scanning. */ iwn_set_led(sc, IWN_LED_LINK, 10, 10); @@ -1747,7 +1888,7 @@ iwn_newstate(struct ieee80211com *ic, en break; case IEEE80211_S_INIT: - sc->is_scanning = false; + sc->sc_flags &= ~IWN_FLAG_SCANNING; sc->calib.state = IWN_CALIB_STATE_INIT; break; } @@ -1771,13 +1912,12 @@ iwn_calib_timeout(void *arg) struct ieee80211com *ic = &sc->sc_ic; int s; + s = splnet(); if (ic->ic_fixed_rate == -1) { - s = splnet(); if (ic->ic_opmode == IEEE80211_M_STA) iwn_iter_func(sc, ic->ic_bss); else ieee80211_iterate_nodes(&ic->ic_sta, iwn_iter_func, sc); - splx(s); } /* Force automatic TX power calibration every 60 secs. */ if (++sc->calib_cnt >= 120) { @@ -1788,74 +1928,25 @@ iwn_calib_timeout(void *arg) sizeof flags, 1); sc->calib_cnt = 0; } + splx(s); + /* Automatic rate control triggered every 500ms. */ callout_schedule(&sc->calib_to, hz/2); } -#if 0 -static int -iwn_ccmp_decap(struct iwn_softc *sc, struct mbuf *m, struct ieee80211_key *k) -{ - struct ieee80211_frame *wh; - uint64_t pn, *prsc; - uint8_t *ivp; - uint8_t tid; - int hdrlen; - - wh = mtod(m, struct ieee80211_frame *); - hdrlen = ieee80211_get_hdrlen(wh); - ivp = (uint8_t *)wh + hdrlen; - - /* Check that ExtIV bit is be set. */ - if (!(ivp[3] & IEEE80211_WEP_EXTIV)) { - DPRINTF(("CCMP decap ExtIV not set\n")); - return 1; - } - tid = ieee80211_has_qos(wh) ? - ieee80211_get_qos(wh) & IEEE80211_QOS_TID : 0; - prsc = &k->k_rsc[tid]; - - /* Extract the 48-bit PN from the CCMP header. */ - pn = (uint64_t)ivp[0] | - (uint64_t)ivp[1] << 8 | - (uint64_t)ivp[4] << 16 | - (uint64_t)ivp[5] << 24 | - (uint64_t)ivp[6] << 32 | - (uint64_t)ivp[7] << 40; - if (pn <= *prsc) { - /* - * Not necessarily a replayed frame since we did not check - * the sequence number of the 802.11 header yet. - */ - DPRINTF(("CCMP replayed\n")); - return 1; - } - /* Update last seen packet number. */ - *prsc = pn; - - /* Clear Protected bit and strip IV. */ - wh->i_fc[1] &= ~IEEE80211_FC1_PROTECTED; - memmove(mtod(m, char *) + IEEE80211_CCMP_HDRLEN, wh, hdrlen); - m_adj(m, IEEE80211_CCMP_HDRLEN); - /* Strip MIC. */ - m_adj(m, -IEEE80211_CCMP_MICLEN); - return 0; -} -#endif - /* * Process an RX_PHY firmware notification. This is usually immediately * followed by an MPDU_RX_DONE notification. */ -void -iwn_rx_phy(struct iwn_softc *sc, struct iwn_rx_desc *desc) +static void +iwn_rx_phy(struct iwn_softc *sc, struct iwn_rx_desc *desc, + struct iwn_rx_data *data) { struct iwn_rx_stat *stat = (struct iwn_rx_stat *)(desc + 1); DPRINTFN(2, ("received PHY stats\n")); - bus_dmamap_sync(sc->sc_dmat, sc->rxq.buf_dma.map, - (vaddr_t)stat - (vaddr_t)sc->rxq.buf_dma.vaddr, sizeof (*stat), - BUS_DMASYNC_POSTREAD); + bus_dmamap_sync(sc->sc_dmat, data->map, sizeof (*desc), + sizeof (*stat), BUS_DMASYNC_POSTREAD); /* Save RX statistics, they will be used on MPDU_RX_DONE. */ memcpy(&sc->last_rx_stat, stat, sizeof (*stat)); @@ -1866,7 +1957,7 @@ iwn_rx_phy(struct iwn_softc *sc, struct * 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. */ -void +static void iwn_rx_done(struct iwn_softc *sc, struct iwn_rx_desc *desc, struct iwn_rx_data *data) { @@ -1874,14 +1965,13 @@ iwn_rx_done(struct iwn_softc *sc, struct struct ieee80211com *ic = &sc->sc_ic; struct ifnet *ifp = ic->ic_ifp; struct iwn_rx_ring *ring = &sc->rxq; - struct iwn_rbuf *rbuf; struct ieee80211_frame *wh; struct ieee80211_node *ni; struct mbuf *m, *m1; struct iwn_rx_stat *stat; - char * head; + char *head; uint32_t flags; - int len, rssi, error; + int error, len, rssi; if (desc->type == IWN_MPDU_RX_DONE) { /* Check for prior RX_PHY notification. */ @@ -1899,13 +1989,13 @@ iwn_rx_done(struct iwn_softc *sc, struct BUS_DMASYNC_POSTREAD); if (stat->cfg_phy_len > IWN_STAT_MAXLEN) { - aprint_error_dev(sc->sc_dev, "invalid RX statistic header\n"); + aprint_error_dev(sc->sc_dev, + "invalid RX statistic header\n"); ifp->if_ierrors++; return; } if (desc->type == IWN_MPDU_RX_DONE) { - struct iwn_rx_mpdu *mpdu = - (struct iwn_rx_mpdu *)(desc + 1); + struct iwn_rx_mpdu *mpdu = (struct iwn_rx_mpdu *)(desc + 1); head = (char *)(mpdu + 1); len = le16toh(mpdu->len); } else { @@ -1922,45 +2012,30 @@ iwn_rx_done(struct iwn_softc *sc, struct return; } /* Discard frames that are too short. */ - if (len < sizeof (struct ieee80211_frame)) { + if (len < sizeof (*wh)) { DPRINTF(("frame too short: %d\n", len)); ic->ic_stats.is_rx_tooshort++; ifp->if_ierrors++; return; } - /* - * See comment in if_wpi.c:wpi_rx_intr() about locking - * nb_free_entries here. In short: it's not required. - */ - MGETHDR(m1, M_DONTWAIT, MT_DATA); + m1 = MCLGETIalt(sc, M_DONTWAIT, NULL, IWN_RBUF_SIZE); if (m1 == NULL) { ic->ic_stats.is_rx_nobuf++; ifp->if_ierrors++; return; } - if (sc->rxq.nb_free_entries <= 0) { - ic->ic_stats.is_rx_nobuf++; - ifp->if_ierrors++; - m_freem(m1); - return; - } - rbuf = iwn_alloc_rbuf(sc); - /* Attach RX buffer to mbuf header. */ - MEXTADD(m1, rbuf->vaddr, IWN_RBUF_SIZE, 0, iwn_free_rbuf, - rbuf); - m1->m_flags |= M_EXT_RW; bus_dmamap_unload(sc->sc_dmat, data->map); - error = bus_dmamap_load(sc->sc_dmat, data->map, m1->m_ext.ext_buf, - IWN_RBUF_SIZE, NULL, BUS_DMA_NOWAIT); + error = bus_dmamap_load(sc->sc_dmat, data->map, mtod(m1, void *), + IWN_RBUF_SIZE, NULL, BUS_DMA_NOWAIT | BUS_DMA_READ); if (error != 0) { m_freem(m1); /* Try to reload the old mbuf. */ error = bus_dmamap_load(sc->sc_dmat, data->map, - data->m->m_ext.ext_buf, IWN_RBUF_SIZE, NULL, - BUS_DMA_NOWAIT); + mtod(data->m, void *), IWN_RBUF_SIZE, NULL, + BUS_DMA_NOWAIT | BUS_DMA_READ); if (error != 0) { panic("%s: could not load old RX mbuf", device_xname(sc->sc_dev)); @@ -1974,9 +2049,9 @@ iwn_rx_done(struct iwn_softc *sc, struct ifp->if_ierrors++; return; } + m = data->m; data->m = m1; - /* Update RX descriptor. */ ring->desc[ring->cur] = htole32(data->map->dm_segs[0].ds_addr >> 8); bus_dmamap_sync(sc->sc_dmat, ring->desc_dma.map, @@ -1990,39 +2065,14 @@ iwn_rx_done(struct iwn_softc *sc, struct /* Grab a reference to the source node. */ wh = mtod(m, struct ieee80211_frame *); - ni = ieee80211_find_rxnode(ic,(struct ieee80211_frame_min *)wh); + ni = ieee80211_find_rxnode(ic, (struct ieee80211_frame_min *)wh); -#if 0 - rxi.rxi_flags = 0; - if ((wh->i_fc[1] & IEEE80211_FC1_PROTECTED) && - !IEEE80211_IS_MULTICAST(wh->i_addr1) && - (ni->ni_flags & IEEE80211_NODE_RXPROT) && - ni->ni_pairwise_key.k_cipher == IEEE80211_CIPHER_CCMP) { - if ((flags & IWN_RX_CIPHER_MASK) != IWN_RX_CIPHER_CCMP) { - ic->ic_stats.is_ccmp_dec_errs++; - ifp->if_ierrors++; - return; - } - /* Check whether decryption was successful or not. */ - if ((desc->type == IWN_MPDU_RX_DONE && - (flags & (IWN_RX_MPDU_DEC | IWN_RX_MPDU_MIC_OK)) != - (IWN_RX_MPDU_DEC | IWN_RX_MPDU_MIC_OK)) || - (desc->type != IWN_MPDU_RX_DONE && - (flags & IWN_RX_DECRYPT_MASK) != IWN_RX_DECRYPT_OK)) { - DPRINTF(("CCMP decryption failed 0x%x\n", flags)); - ic->ic_stats.is_ccmp_dec_errs++; - ifp->if_ierrors++; - return; - } - if (iwn_ccmp_decap(sc, m, &ni->ni_pairwise_key) != 0) { - ifp->if_ierrors++; - return; - } - rxi.rxi_flags |= IEEE80211_RXI_HWDEC; - } -#endif + /* XXX OpenBSD adds decryption here (see also comments in iwn_tx). */ + /* NetBSD does decryption in ieee80211_input. */ rssi = hal->get_rssi(stat); + + /* XXX Added for NetBSD: scans never stop without it */ if (ic->ic_state == IEEE80211_S_SCAN) iwn_fix_channel(ic, m); @@ -2058,7 +2108,7 @@ iwn_rx_done(struct iwn_softc *sc, struct default: tap->wr_rate = 0; } - bpf_ops->bpf_mtap2(sc->sc_drvbpf, tap, sc->sc_rxtap_len, m); + bpf_mtap2(sc->sc_drvbpf, tap, sc->sc_rxtap_len, m); } /* Send the frame to the 802.11 layer. */ @@ -2068,11 +2118,28 @@ iwn_rx_done(struct iwn_softc *sc, struct ieee80211_free_node(ni); } +#ifndef IEEE80211_NO_HT +/* Process an incoming Compressed BlockAck. */ +static void +iwn_rx_compressed_ba(struct iwn_softc *sc, struct iwn_rx_desc *desc, + struct iwn_rx_data *data) +{ + struct iwn_compressed_ba *ba = (struct iwn_compressed_ba *)(desc + 1); + struct iwn_tx_ring *txq; + + bus_dmamap_sync(sc->sc_dmat, data->map, sizeof (*desc), sizeof (*ba), + BUS_DMASYNC_POSTREAD); + + txq = &sc->txq[le16toh(ba->qid)]; + /* XXX TBD */ +} +#endif + /* * Process a CALIBRATION_RESULT notification sent by the initialization * firmware on response to a CMD_CALIB_CONFIG command (5000 only.) */ -void +static void iwn5000_rx_calib_results(struct iwn_softc *sc, struct iwn_rx_desc *desc, struct iwn_rx_data *data) { @@ -2080,7 +2147,7 @@ iwn5000_rx_calib_results(struct iwn_soft int len, idx = -1; /* Runtime firmware should not send such a notification. */ - if (!(sc->sc_flags & IWN_FLAG_FIRST_BOOT)) + if (sc->sc_flags & IWN_FLAG_CALIB_DONE) return; len = (le32toh(desc->len) & 0x3fff) - 4; @@ -2089,7 +2156,8 @@ iwn5000_rx_calib_results(struct iwn_soft switch (calib->code) { case IWN5000_PHY_CALIB_DC: - if (sc->hw_type == IWN_HW_REV_TYPE_5150) + if (sc->hw_type == IWN_HW_REV_TYPE_5150 || + sc->hw_type == IWN_HW_REV_TYPE_6050) idx = 0; break; case IWN5000_PHY_CALIB_LO: @@ -2098,8 +2166,9 @@ iwn5000_rx_calib_results(struct iwn_soft case IWN5000_PHY_CALIB_TX_IQ: idx = 2; break; - case IWN5000_PHY_CALIB_TX_IQ_PERD: - if (sc->hw_type != IWN_HW_REV_TYPE_5150) + case IWN5000_PHY_CALIB_TX_IQ_PERIODIC: + if (sc->hw_type < IWN_HW_REV_TYPE_6000 && + sc->hw_type != IWN_HW_REV_TYPE_5150) idx = 3; break; case IWN5000_PHY_CALIB_BASE_BAND: @@ -2124,46 +2193,6 @@ iwn5000_rx_calib_results(struct iwn_soft memcpy(sc->calibcmd[idx].buf, calib, len); } - -/* - * 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 -iwn_fix_channel(struct ieee80211com *ic, struct mbuf *m) -{ - struct ieee80211_frame *wh; - uint8_t subtype; - uint8_t *frm, *efrm; - - wh = mtod(m, struct ieee80211_frame *); - - if ((wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) != IEEE80211_FC0_TYPE_MGT) - return; - - subtype = wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK; - - if (subtype != IEEE80211_FC0_SUBTYPE_BEACON && - subtype != IEEE80211_FC0_SUBTYPE_PROBE_RESP) - return; - - frm = (uint8_t *)(wh + 1); - efrm = mtod(m, uint8_t *) + m->m_len; - - frm += 12; /* skip tstamp, bintval and capinfo fields */ - while (frm < efrm) { - if (*frm == IEEE80211_ELEMID_DSPARMS) -#if IEEE80211_CHAN_MAX < 255 - if (frm[2] <= IEEE80211_CHAN_MAX) -#endif - ic->ic_curchan = &ic->ic_channels[frm[2]]; - - frm += frm[1] + 2; - } -} - - /* * Process an RX_STATISTICS or BEACON_STATISTICS firmware notification. * The latter is sent by the firmware after each received beacon. @@ -2172,9 +2201,11 @@ static void iwn_rx_statistics(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 iwn_calib_state *calib = &sc->calib; struct iwn_stats *stats = (struct iwn_stats *)(desc + 1); + int temp; /* Ignore statistics received during a scan. */ if (ic->ic_state != IEEE80211_S_RUN) @@ -2186,7 +2217,6 @@ iwn_rx_statistics(struct iwn_softc *sc, DPRINTFN(3, ("received statistics (cmd=%d)\n", desc->type)); sc->calib_cnt = 0; /* Reset TX power calibration timeout. */ -#if 0 /* Test if temperature has changed. */ if (stats->general.temp != sc->rawtemp) { /* Convert "raw" temperature to degC. */ @@ -2194,15 +2224,16 @@ iwn_rx_statistics(struct iwn_softc *sc, temp = hal->get_temperature(sc); DPRINTFN(2, ("temperature=%dC\n", temp)); +#ifndef SMALL_KERNEL /* Update temperature sensor. */ - sc->sensor.value = IWN_CTOMUK(temp); - sc->sensor.flags &= ~SENSOR_FINVALID; + sc->sc_sensor.value_cur = IWN_CTOMUK(temp); + sc->sc_sensor.state = ENVSYS_SVALID; +#endif /* Update TX power if need be (4965AGN only.) */ if (sc->hw_type == IWN_HW_REV_TYPE_4965) iwn4965_power_calibration(sc, temp); } -#endif if (desc->type != IWN_BEACON_STATISTICS) return; /* Reply to a statistics request. */ @@ -2233,7 +2264,7 @@ iwn4965_tx_done(struct iwn_softc *sc, st bus_dmamap_sync(sc->sc_dmat, data->map, sizeof (*desc), sizeof (*stat), BUS_DMASYNC_POSTREAD); - iwn_tx_done(sc, desc, stat->retrycnt, le32toh(stat->status) & 0xff); + iwn_tx_done(sc, desc, stat->ackfailcnt, le32toh(stat->status) & 0xff); } static void @@ -2242,29 +2273,32 @@ iwn5000_tx_done(struct iwn_softc *sc, st { struct iwn5000_tx_stat *stat = (struct iwn5000_tx_stat *)(desc + 1); +#ifdef notyet /* Reset TX scheduler slot. */ iwn5000_reset_sched(sc, desc->qid & 0xf, desc->idx); +#endif bus_dmamap_sync(sc->sc_dmat, data->map, sizeof (*desc), sizeof (*stat), BUS_DMASYNC_POSTREAD); - iwn_tx_done(sc, desc, stat->retrycnt, le16toh(stat->status) & 0xff); + iwn_tx_done(sc, desc, stat->ackfailcnt, le16toh(stat->status) & 0xff); } /* * Adapter-independent backend for TX_DONE firmware notifications. */ static void -iwn_tx_done(struct iwn_softc *sc, struct iwn_rx_desc *desc, int retrycnt, +iwn_tx_done(struct iwn_softc *sc, struct iwn_rx_desc *desc, int ackfailcnt, uint8_t status) { - struct ifnet *ifp = sc->sc_ic.ic_ifp; - struct iwn_tx_ring *ring = &sc->txq[desc->qid & 0xf]; + struct ieee80211com *ic = &sc->sc_ic; + struct ifnet *ifp = ic->ic_ifp; + struct iwn_tx_ring *ring = &sc->txq[desc->qid & 0xf]; struct iwn_tx_data *data = &ring->data[desc->idx]; struct iwn_node *wn = (struct iwn_node *)data->ni; /* Update rate control statistics. */ wn->amn.amn_txcnt++; - if (retrycnt > 0) + if (ackfailcnt > 0) wn->amn.amn_retrycnt++; if (status != 1 && status != 2) @@ -2286,7 +2320,7 @@ iwn_tx_done(struct iwn_softc *sc, struct sc->qfullmsk &= ~(1 << ring->qid); if (sc->qfullmsk == 0 && (ifp->if_flags & IFF_OACTIVE)) { ifp->if_flags &= ~IFF_OACTIVE; - iwn_start(ifp); + (*ifp->if_start)(ifp); } } } @@ -2321,87 +2355,23 @@ iwn_cmd_done(struct iwn_softc *sc, struc * Process an INT_FH_RX or INT_SW_RX interrupt. */ static void -iwn_microcode_ready(struct iwn_softc *sc, struct iwn_ucode_info *uc) -{ - - /* The microcontroller is ready */ - DPRINTF(("microcode alive notification version=%d.%d " - "subtype=%x alive=%x\n", uc->major, uc->minor, - uc->subtype, le32toh(uc->valid))); - - - if (le32toh(uc->valid) != 1) { - aprint_error_dev(sc->sc_dev, "microcontroller initialization " - "failed\n"); - return; - } - if (uc->subtype == IWN_UCODE_INIT) { - /* save microcontroller's report */ - memcpy(&sc->ucode_info, uc, sizeof (*uc)); - } - /* Save the address of the error log in SRAM. */ - sc->errptr = le32toh(uc->errptr); -} - -static void iwn_notif_intr(struct iwn_softc *sc) { struct ieee80211com *ic = &sc->sc_ic; struct ifnet *ifp = ic->ic_ifp; - struct iwn_rx_data *data; - struct iwn_rx_desc *desc; uint16_t hw; bus_dmamap_sync(sc->sc_dmat, sc->rxq.stat_dma.map, 0, sc->rxq.stat_dma.size, BUS_DMASYNC_POSTREAD); hw = le16toh(sc->rxq.stat->closed_count) & 0xfff; - - /* - * If the radio is disabled then down the interface and stop - * processing - scan the queue for a microcode load command - * result. It is the only thing that we can do with the radio - * off. - */ - if (!sc->sc_radio) { - while (sc->rxq.cur != hw) { - data = &sc->rxq.data[sc->rxq.cur]; - desc = (void *)data->m->m_ext.ext_buf; - bus_dmamap_sync(sc->sc_dmat, data->map, 0, - sizeof(*desc), BUS_DMASYNC_POSTREAD); - if (desc->type == IWN_UC_READY) { - iwn_microcode_ready(sc, - (struct iwn_ucode_info *)(desc + 1)); - } else if (desc->type == IWN_STATE_CHANGED) { - uint32_t *status = (uint32_t *)(desc + 1); - - /* enabled/disabled notification */ - DPRINTF(("state changed to %x\n", - le32toh(*status))); - - sc->sc_radio = !(le32toh(*status) & 1); - } - - sc->rxq.cur = (sc->rxq.cur + 1) % IWN_RX_RING_COUNT; - } - - if (!sc->sc_radio) { - iwn_stop(ifp, 1); - } - - return; - } - - bus_dmamap_sync(sc->sc_dmat, sc->rxq.stat_dma.map, - 0, sc->rxq.stat_dma.size, BUS_DMASYNC_POSTREAD); - - hw = le16toh(sc->rxq.stat->closed_count) & 0xfff; while (sc->rxq.cur != hw) { - data = &sc->rxq.data[sc->rxq.cur]; - desc = (void *)data->m->m_ext.ext_buf; + struct iwn_rx_data *data = &sc->rxq.data[sc->rxq.cur]; + struct iwn_rx_desc *desc; bus_dmamap_sync(sc->sc_dmat, data->map, 0, sizeof (*desc), BUS_DMASYNC_POSTREAD); + desc = mtod(data->m, struct iwn_rx_desc *); DPRINTFN(4, ("notification qid=%d idx=%d flags=%x type=%d\n", desc->qid & 0xf, desc->idx, desc->flags, desc->type)); @@ -2411,7 +2381,7 @@ iwn_notif_intr(struct iwn_softc *sc) switch (desc->type) { case IWN_RX_PHY: - iwn_rx_phy(sc, desc); + iwn_rx_phy(sc, desc, data); break; case IWN_RX_DONE: /* 4965AGN only. */ @@ -2419,7 +2389,12 @@ iwn_notif_intr(struct iwn_softc *sc) /* An 802.11 frame has been received. */ iwn_rx_done(sc, desc, data); break; - +#ifndef IEEE80211_NO_HT + case IWN_RX_COMPRESSED_BA: + /* A Compressed BlockAck has been received. */ + iwn_rx_compressed_ba(sc, desc, data); + break; +#endif case IWN_TX_DONE: /* An 802.11 frame has been transmitted. */ sc->sc_hal->tx_done(sc, desc, data); @@ -2450,8 +2425,28 @@ iwn_notif_intr(struct iwn_softc *sc) } case IWN_UC_READY: { - iwn_microcode_ready(sc, - (struct iwn_ucode_info *)(desc + 1)); + struct iwn_ucode_info *uc = + (struct iwn_ucode_info *)(desc + 1); + + /* The microcontroller is ready. */ + bus_dmamap_sync(sc->sc_dmat, data->map, sizeof (*desc), + sizeof (*uc), BUS_DMASYNC_POSTREAD); + DPRINTF(("microcode alive notification version=%d.%d " + "subtype=%x alive=%x\n", uc->major, uc->minor, + uc->subtype, le32toh(uc->valid))); + + if (le32toh(uc->valid) != 1) { + aprint_error_dev(sc->sc_dev, + "microcontroller initialization " + "failed\n"); + break; + } + if (uc->subtype == IWN_UCODE_INIT) { + /* Save microcontroller report. */ + memcpy(&sc->ucode_info, uc, sizeof (*uc)); + } + /* Save the address of the error log in SRAM. */ + sc->errptr = le32toh(uc->errptr); break; } case IWN_STATE_CHANGED: @@ -2463,13 +2458,12 @@ iwn_notif_intr(struct iwn_softc *sc) sizeof (*status), BUS_DMASYNC_POSTREAD); DPRINTF(("state changed to %x\n", le32toh(*status))); - sc->sc_radio = !(le32toh(*status) & 1); - if (le32toh(*status) & 1) { /* The radio button has to be pushed. */ aprint_error_dev(sc->sc_dev, "Radio transmitter is off\n"); /* Turn the interface down. */ + ifp->if_flags &= ~IFF_UP; iwn_stop(ifp, 1); return; /* No further processing. */ } @@ -2508,7 +2502,7 @@ iwn_notif_intr(struct iwn_softc *sc) if (iwn_scan(sc, IEEE80211_CHAN_5GHZ) == 0) break; } - sc->is_scanning = false; + sc->sc_flags &= ~IWN_FLAG_SCANNING; ieee80211_end_scan(ic); break; } @@ -2517,6 +2511,7 @@ iwn_notif_intr(struct iwn_softc *sc) break; case IWN5000_CALIBRATION_DONE: + sc->sc_flags |= IWN_FLAG_CALIB_DONE; wakeup(sc); break; } @@ -2542,7 +2537,7 @@ iwn_wakeup_intr(struct iwn_softc *sc) /* Wakeup RX and TX rings. */ IWN_WRITE(sc, IWN_FH_RX_WPTR, sc->rxq.cur & ~7); - for (qid = 0; qid < 6; qid++) { + for (qid = 0; qid < sc->sc_hal->ntxqs; qid++) { struct iwn_tx_ring *ring = &sc->txq[qid]; IWN_WRITE(sc, IWN_HBUS_TARG_WRPTR, qid << 8 | ring->cur); } @@ -2553,13 +2548,16 @@ iwn_wakeup_intr(struct iwn_softc *sc) * we can't debug the firmware because it is neither open source nor free, it * can help us to identify certain classes of problems. */ -void +static void iwn_fatal_intr(struct iwn_softc *sc) { const struct iwn_hal *hal = sc->sc_hal; struct iwn_fw_dump dump; int i; + /* Force a complete recalibration on next init. */ + sc->sc_flags &= ~IWN_FLAG_CALIB_DONE; + /* Check that the error log address is valid. */ if (sc->errptr < IWN_FW_DATA_BASE || sc->errptr + sizeof (dump) > @@ -2579,35 +2577,34 @@ iwn_fatal_intr(struct iwn_softc *sc) iwn_nic_unlock(sc); if (dump.valid == 0) { - aprint_error_dev(sc->sc_dev, "firmware error log is empty\n"); + aprint_error_dev(sc->sc_dev, + "firmware error log is empty\n"); return; } - printf("firmware error log:\n"); -#if 0 - printf(" error type = \"%s\" (0x%08X)\n", - (dump.id < nitems(iwn_fw_errmsg)) ? + aprint_error("firmware error log:\n"); + aprint_error(" error type = \"%s\" (0x%08X)\n", + (dump.id < __arraycount(iwn_fw_errmsg)) ? iwn_fw_errmsg[dump.id] : "UNKNOWN", dump.id); -#endif - printf(" program counter = 0x%08X\n", dump.pc); - printf(" source line = 0x%08X\n", dump.src_line); - printf(" error data = 0x%08X%08X\n", + aprint_error(" program counter = 0x%08X\n", dump.pc); + aprint_error(" source line = 0x%08X\n", dump.src_line); + aprint_error(" error data = 0x%08X%08X\n", dump.error_data[0], dump.error_data[1]); - printf(" branch link = 0x%08X%08X\n", + aprint_error(" branch link = 0x%08X%08X\n", dump.branch_link[0], dump.branch_link[1]); - printf(" interrupt link = 0x%08X%08X\n", + aprint_error(" interrupt link = 0x%08X%08X\n", dump.interrupt_link[0], dump.interrupt_link[1]); - printf(" time = %u\n", dump.time[0]); + aprint_error(" time = %u\n", dump.time[0]); /* Dump driver status (TX and RX rings) while we're here. */ - printf("driver status:\n"); + aprint_error("driver status:\n"); for (i = 0; i < hal->ntxqs; i++) { struct iwn_tx_ring *ring = &sc->txq[i]; - printf(" tx ring %2d: qid=%-2d cur=%-3d queued=%-3d\n", + aprint_error(" tx ring %2d: qid=%-2d cur=%-3d queued=%-3d\n", i, ring->qid, ring->cur, ring->queued); } - printf(" rx ring: cur=%d\n", sc->rxq.cur); - printf(" 802.11 state %d\n", sc->sc_ic.ic_state); + aprint_error(" rx ring: cur=%d\n", sc->rxq.cur); + aprint_error(" 802.11 state %d\n", sc->sc_ic.ic_state); } static int @@ -2615,49 +2612,83 @@ iwn_intr(void *arg) { struct iwn_softc *sc = arg; struct ifnet *ifp = sc->sc_ic.ic_ifp; - uint32_t r1, r2; + uint32_t r1, r2, tmp; /* Disable interrupts. */ - IWN_WRITE(sc, IWN_MASK, 0); - - r1 = IWN_READ(sc, IWN_INT); - r2 = IWN_READ(sc, IWN_FH_INT); + IWN_WRITE(sc, IWN_INT_MASK, 0); + /* Read interrupts from ICT (fast) or from registers (slow). */ + if (sc->sc_flags & IWN_FLAG_USE_ICT) { + tmp = 0; + while (sc->ict[sc->ict_cur] != 0) { + tmp |= sc->ict[sc->ict_cur]; + sc->ict[sc->ict_cur] = 0; /* Acknowledge. */ + sc->ict_cur = (sc->ict_cur + 1) % IWN_ICT_COUNT; + } + tmp = le32toh(tmp); + if (tmp == 0xffffffff) /* Shouldn't happen. */ + tmp = 0; + else if (tmp & 0xc0000) /* Workaround a HW bug. */ + tmp |= 0x8000; + r1 = (tmp & 0xff00) << 16 | (tmp & 0xff); + r2 = 0; /* Unused. */ + } else { + r1 = IWN_READ(sc, IWN_INT); + if (r1 == 0xffffffff || (r1 & 0xfffffff0) == 0xa5a5a5a0) + return 0; /* Hardware gone! */ + r2 = IWN_READ(sc, IWN_FH_INT); + } if (r1 == 0 && r2 == 0) { if (ifp->if_flags & IFF_UP) - IWN_WRITE(sc, IWN_MASK, IWN_INT_MASK); + IWN_WRITE(sc, IWN_INT_MASK, sc->int_mask); return 0; /* Interrupt not for us. */ } - if (r1 == 0xffffffff || (r1 & 0xfffffff0) == 0xa5a5a5a0) - return 0; /* Hardware gone! */ /* Acknowledge interrupts. */ IWN_WRITE(sc, IWN_INT, r1); - IWN_WRITE(sc, IWN_FH_INT, r2); + if (!(sc->sc_flags & IWN_FLAG_USE_ICT)) + IWN_WRITE(sc, IWN_FH_INT, r2); if (r1 & IWN_INT_RF_TOGGLED) { - uint32_t tmp = IWN_READ(sc, IWN_GP_CNTRL); - aprint_error_dev(sc->sc_dev, "RF switch: radio %s\n", + tmp = IWN_READ(sc, IWN_GP_CNTRL); + aprint_error_dev(sc->sc_dev, + "RF switch: radio %s\n", (tmp & IWN_GP_CNTRL_RFKILL) ? "enabled" : "disabled"); - sc->sc_radio = (tmp & IWN_GP_CNTRL_RFKILL); } if (r1 & IWN_INT_CT_REACHED) { - aprint_error_dev(sc->sc_dev, "critical temperature reached!\n"); - /* XXX Reduce TX power? */ + aprint_error_dev(sc->sc_dev, + "critical temperature reached!\n"); } if (r1 & (IWN_INT_SW_ERR | IWN_INT_HW_ERR)) { - aprint_error_dev(sc->sc_dev, "fatal firmware error\n"); + aprint_error_dev(sc->sc_dev, + "fatal firmware error\n"); /* Dump firmware error log and stop. */ iwn_fatal_intr(sc); - iwn_stop(sc->sc_ic.ic_ifp, 1); + ifp->if_flags &= ~IFF_UP; + iwn_stop(ifp, 1); return 1; } - if ((r1 & (IWN_INT_FH_RX | IWN_INT_SW_RX)) || - (r2 & IWN_FH_INT_RX)) - iwn_notif_intr(sc); + if ((r1 & (IWN_INT_FH_RX | IWN_INT_SW_RX | IWN_INT_RX_PERIODIC)) || + (r2 & IWN_FH_INT_RX)) { + if (sc->sc_flags & IWN_FLAG_USE_ICT) { + if (r1 & (IWN_INT_FH_RX | IWN_INT_SW_RX)) + IWN_WRITE(sc, IWN_FH_INT, IWN_FH_INT_RX); + IWN_WRITE_1(sc, IWN_INT_PERIODIC, + IWN_INT_PERIODIC_DIS); + iwn_notif_intr(sc); + if (r1 & (IWN_INT_FH_RX | IWN_INT_SW_RX)) { + IWN_WRITE_1(sc, IWN_INT_PERIODIC, + IWN_INT_PERIODIC_ENA); + } + } else + iwn_notif_intr(sc); + } - if ((r1 & IWN_INT_FH_TX) || (r2 & IWN_FH_INT_TX)) + if ((r1 & IWN_INT_FH_TX) || (r2 & IWN_FH_INT_TX)) { + if (sc->sc_flags & IWN_FLAG_USE_ICT) + IWN_WRITE(sc, IWN_FH_INT, IWN_FH_INT_TX); wakeup(sc); /* FH DMA transfer completed. */ + } if (r1 & IWN_INT_ALIVE) wakeup(sc); /* Firmware is alive. */ @@ -2667,7 +2698,7 @@ iwn_intr(void *arg) /* Re-enable interrupts. */ if (ifp->if_flags & IFF_UP) - IWN_WRITE(sc, IWN_MASK, IWN_INT_MASK); + IWN_WRITE(sc, IWN_INT_MASK, sc->int_mask); return 1; } @@ -2685,7 +2716,8 @@ iwn4965_update_sched(struct iwn_softc *s *w = htole16(len + 8); bus_dmamap_sync(sc->sc_dmat, sc->sched_dma.map, (char *)(void *)w - (char *)(void *)sc->sched_dma.vaddr, - sizeof (uint16_t), BUS_DMASYNC_PREWRITE); + sizeof (uint16_t), + BUS_DMASYNC_PREWRITE); if (idx < IWN_SCHED_WINSZ) { *(w + IWN_TX_RING_COUNT) = *w; bus_dmamap_sync(sc->sc_dmat, sc->sched_dma.map, @@ -2714,6 +2746,7 @@ iwn5000_update_sched(struct iwn_softc *s } } +#ifdef notyet static void iwn5000_reset_sched(struct iwn_softc *sc, int qid, int idx) { @@ -2731,6 +2764,7 @@ iwn5000_reset_sched(struct iwn_softc *sc sizeof (uint16_t), BUS_DMASYNC_PREWRITE); } } +#endif static int iwn_tx(struct iwn_softc *sc, struct mbuf *m, struct ieee80211_node *ni, int ac) @@ -2746,55 +2780,71 @@ iwn_tx(struct iwn_softc *sc, struct mbuf const struct iwn_rate *rinfo; struct ieee80211_frame *wh; struct ieee80211_key *k = NULL; - const struct chanAccParams *cap; struct mbuf *m1; uint32_t flags; u_int hdrlen; bus_dma_segment_t *seg; - uint8_t ridx, txant, type; - int i, totlen, error, pad, noack; + uint8_t tid, ridx, txant, type; + int i, totlen, error, pad; + + const struct chanAccParams *cap; + int noack; + int hdrlen2; wh = mtod(m, struct ieee80211_frame *); + hdrlen = ieee80211_anyhdrsize(wh); type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK; - /* JAF XXX two lines above were not in wpi. check we don't duplicate this */ + hdrlen2 = (IEEE80211_QOS_HAS_SEQ(wh)) ? + sizeof (struct ieee80211_qosframe) : + sizeof (struct ieee80211_frame); + + if (hdrlen != hdrlen2) + aprint_error_dev(sc->sc_dev, "hdrlen error (%d != %d)\n", + hdrlen, hdrlen2); + /* XXX OpenBSD sets a different tid when using QOS */ + tid = 0; if (IEEE80211_QOS_HAS_SEQ(wh)) { - hdrlen = sizeof (struct ieee80211_qosframe); cap = &ic->ic_wme.wme_chanParams; noack = cap->cap_wmeParams[ac].wmep_noackPolicy; - } else { - hdrlen = sizeof (struct ieee80211_frame); - noack = 0; - } - - if (wh->i_fc[1] & IEEE80211_FC1_WEP) { - k = ieee80211_crypto_encap(ic, ni, m); - if (k == NULL) { - m_freem(m); - return ENOBUFS; - } - /* packet header may have moved, reset our local pointer */ - wh = mtod(m, struct ieee80211_frame *); } + else + noack = 0; ring = &sc->txq[ac]; desc = &ring->desc[ring->cur]; data = &ring->data[ring->cur]; /* Choose a TX rate index. */ - if (type == IEEE80211_FC0_TYPE_MGT) { - /* mgmt frames are sent at the lowest available bit-rate */ + if (IEEE80211_IS_MULTICAST(wh->i_addr1) || + type != IEEE80211_FC0_TYPE_DATA) { ridx = (ic->ic_curmode == IEEE80211_MODE_11A) ? IWN_RIDX_OFDM6 : IWN_RIDX_CCK1; - } else { - if (ic->ic_fixed_rate != -1) { - ridx = sc->fixed_ridx; - } else - ridx = wn->ridx[ni->ni_txrate]; - } + } else if (ic->ic_fixed_rate != -1) { + ridx = sc->fixed_ridx; + } else + ridx = wn->ridx[ni->ni_txrate]; rinfo = &iwn_rates[ridx]; + /* Encrypt the frame if need be. */ + /* + * XXX For now, NetBSD swaps the encryption and bpf sections + * in order to match old code and other drivers. Tests with + * tcpdump indicates that the order is irrelevant, however, + * as bpf produces unencrypted data for both ordering choices. + */ + if (wh->i_fc[1] & IEEE80211_FC1_WEP) { + k = ieee80211_crypto_encap(ic, ni, m); + if (k == NULL) { + m_freem(m); + return ENOBUFS; + } + /* Packet header may have moved, reset our local pointer. */ + wh = mtod(m, struct ieee80211_frame *); + } + totlen = m->m_pkthdr.len; + if (sc->sc_drvbpf != NULL) { struct iwn_tx_radiotap_header *tap = &sc->sc_txtap; @@ -2806,29 +2856,9 @@ iwn_tx(struct iwn_softc *sc, struct mbuf if (wh->i_fc[1] & IEEE80211_FC1_WEP) tap->wt_flags |= IEEE80211_RADIOTAP_F_WEP; - bpf_ops->bpf_mtap2(sc->sc_drvbpf, tap, sc->sc_txtap_len, m); + bpf_mtap2(sc->sc_drvbpf, tap, sc->sc_txtap_len, m); } - totlen = m->m_pkthdr.len; - - /* Encrypt the frame if need be. */ -#ifdef IEEE80211_FC1_PROTECTED - if (wh->i_fc[1] & IEEE80211_FC1_PROTECTED) { - /* Retrieve key for TX. */ - k = ieee80211_get_txkey(ic, wh, ni); - if (k->k_cipher != IEEE80211_CIPHER_CCMP) { - /* Do software encryption. */ - if ((m = ieee80211_encrypt(ic, m, k)) == NULL) - return ENOBUFS; - /* 802.11 header may have moved. */ - wh = mtod(m, struct ieee80211_frame *); - totlen = m->m_pkthdr.len; - - } else /* HW appends CCMP MIC. */ - totlen += IEEE80211_CCMP_HDRLEN; - } -#endif - /* Prepare TX firmware command. */ cmd = &ring->cmd[ring->cur]; cmd->code = IWN_CMD_TX_DATA; @@ -2841,17 +2871,19 @@ iwn_tx(struct iwn_softc *sc, struct mbuf tx->scratch = 0; /* clear "scratch" area */ flags = 0; - if (!noack && !IEEE80211_IS_MULTICAST(wh->i_addr1)) { - flags |= IWN_TX_NEED_ACK; - } else if (m->m_pkthdr.len + IEEE80211_CRC_LEN > ic->ic_rtsthreshold) - flags |= IWN_TX_NEED_RTS | IWN_TX_FULL_TXOP; + if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) { + /* Unicast frame, check if an ACK is expected. */ + if (!noack) + flags |= IWN_TX_NEED_ACK; + } #ifdef notyet + /* XXX NetBSD does not define IEEE80211_FC0_SUBTYPE_BAR */ 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. */ -#endif +#endif if (wh->i_fc[1] & IEEE80211_FC1_MORE_FRAG) flags |= IWN_TX_MORE_FRAG; /* Cannot happen yet. */ @@ -2889,18 +2921,17 @@ iwn_tx(struct iwn_softc *sc, struct mbuf #ifndef IEEE80211_STA_ONLY /* Tell HW to set timestamp in probe responses. */ - if ((subtype == IEEE80211_FC0_SUBTYPE_PROBE_RESP) || - (subtype == IEEE80211_FC0_SUBTYPE_PROBE_REQ)) + /* XXX NetBSD rev 1.11 added probe requests here but */ + /* probe requests do not take timestamps (from Bergamini). */ + if (subtype == IEEE80211_FC0_SUBTYPE_PROBE_RESP) flags |= IWN_TX_INSERT_TSTAMP; #endif + /* XXX NetBSD rev 1.11 and 1.20 added AUTH/DAUTH and RTS/CTS */ + /* changes here. These are not needed (from Bergamini). */ if (subtype == IEEE80211_FC0_SUBTYPE_ASSOC_REQ || - subtype == IEEE80211_FC0_SUBTYPE_REASSOC_REQ || - subtype == IEEE80211_FC0_SUBTYPE_AUTH || - subtype == IEEE80211_FC0_SUBTYPE_DEAUTH) { - flags &= ~IWN_TX_NEED_RTS; - flags |= IWN_TX_NEED_CTS; + subtype == IEEE80211_FC0_SUBTYPE_REASSOC_REQ) tx->timeout = htole16(3); - } else + else tx->timeout = htole16(2); } else tx->timeout = htole16(0); @@ -2912,19 +2943,8 @@ iwn_tx(struct iwn_softc *sc, struct mbuf } else pad = 0; -#if 0 - if (type == IEEE80211_FC0_TYPE_CTL) { - uint8_t subtype = wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK; - - /* tell h/w to set timestamp in probe responses */ - if (subtype == 0x0080) /* linux says this is "back request" */ - /* linux says (1 << 6) is IMM_BA_RSP_MASK */ - flags |= (IWN_TX_NEED_ACK | (1 << 6)); - } -#endif - tx->len = htole16(totlen); - tx->tid = 0/* tid */; + tx->tid = tid; tx->rts_ntries = 60; tx->data_ntries = 15; tx->lifetime = htole32(IWN_LIFETIME_INFINITE); @@ -2934,7 +2954,7 @@ iwn_tx(struct iwn_softc *sc, struct mbuf /* Group or management frame. */ tx->linkq = 0; /* XXX Alternate between antenna A and B? */ - txant = IWN_LSB(sc->txantmsk); + txant = IWN_LSB(sc->txchainmask); tx->rflags |= IWN_RFLAG_ANT(txant); } else { tx->linkq = ni->ni_rates.rs_nrates - ni->ni_txrate - 1; @@ -2945,51 +2965,23 @@ iwn_tx(struct iwn_softc *sc, struct mbuf tx->hiaddr = IWN_HIADDR(data->scratch_paddr); /* Copy 802.11 header in TX command. */ + /* XXX NetBSD changed this in rev 1.20 */ memcpy(((uint8_t *)tx) + sizeof(*tx), wh, hdrlen); /* Trim 802.11 header. */ - m_adj(m, hdrlen); - tx->security = 0; - -#ifdef notyet - if (k != NULL && k->k_cipher == IEEE80211_CIPHER_CCMP) { - /* Trim 802.11 header and prepend CCMP IV. */ - m_adj(m, hdrlen - IEEE80211_CCMP_HDRLEN); - ivp = mtod(m, uint8_t *); - k->k_tsc++; - ivp[0] = k->k_tsc; - ivp[1] = k->k_tsc >> 8; - ivp[2] = 0; - ivp[3] = k->k_id << 6 | IEEE80211_WEP_EXTIV; - ivp[4] = k->k_tsc >> 16; - ivp[5] = k->k_tsc >> 24; - ivp[6] = k->k_tsc >> 32; - ivp[7] = k->k_tsc >> 40; - - tx->security = IWN_CIPHER_CCMP; - /* XXX flags |= IWN_TX_AMPDU_CCMP; */ - memcpy(tx->key, k->k_key, k->k_len); - - /* TX scheduler includes CCMP MIC len w/5000 Series. */ - if (sc->hw_type != IWN_HW_REV_TYPE_4965) - totlen += IEEE80211_CCMP_MICLEN; - } else { - /* Trim 802.11 header. */ - m_adj(m, hdrlen); - tx->security = 0; - } -#endif + m_adj(m, hdrlen); + tx->security = 0; tx->flags = htole32(flags); error = bus_dmamap_load_mbuf(sc->sc_dmat, data->map, m, - BUS_DMA_WRITE | BUS_DMA_NOWAIT); - if (error != 0 && error != EFBIG) { - aprint_error_dev(sc->sc_dev, "could not map mbuf (error %d)\n", - error); - m_freem(m); - return error; - } + BUS_DMA_NOWAIT | BUS_DMA_WRITE); if (error != 0) { + if (error != EFBIG) { + aprint_error_dev(sc->sc_dev, + "can't map mbuf (error %d)\n", error); + m_freem(m); + return error; + } /* Too many DMA segments, linearize mbuf. */ MGETHDR(m1, M_DONTWAIT, MT_DATA); if (m1 == NULL) { @@ -3010,10 +3002,10 @@ iwn_tx(struct iwn_softc *sc, struct mbuf m = m1; error = bus_dmamap_load_mbuf(sc->sc_dmat, data->map, m, - BUS_DMA_WRITE | BUS_DMA_NOWAIT); + BUS_DMA_NOWAIT | BUS_DMA_WRITE); if (error != 0) { aprint_error_dev(sc->sc_dev, - "could not map mbuf (error %d)\n", error); + "can't map mbuf (error %d)\n", error); m_freem(m); return error; } @@ -3049,8 +3041,10 @@ iwn_tx(struct iwn_softc *sc, struct mbuf (char *)(void *)desc - (char *)(void *)ring->desc_dma.vaddr, sizeof (*desc), BUS_DMASYNC_PREWRITE); +#ifdef notyet /* Update TX scheduler. */ hal->update_sched(sc, ring->qid, ring->cur, tx->id, totlen); +#endif /* Kick TX ring. */ ring->cur = (ring->cur + 1) % IWN_TX_RING_COUNT; @@ -3073,15 +3067,7 @@ iwn_start(struct ifnet *ifp) struct mbuf *m; int ac; - DPRINTFN(5, ("iwn_start enter\n")); - - /* - * net80211 may still try to send management frames even if the - * IFF_RUNNING flag is not set... Also, don't bother if the radio - * is not enabled. - */ - if (((ifp->if_flags & (IFF_RUNNING | IFF_OACTIVE)) != IFF_RUNNING) || - !sc->sc_radio) + if ((ifp->if_flags & (IFF_RUNNING | IFF_OACTIVE)) != IFF_RUNNING) return; for (;;) { @@ -3123,19 +3109,20 @@ iwn_start(struct ifnet *ifp) continue; } - /* no QoS encapsulation for EAPOL frames */ + /* No QoS encapsulation for EAPOL frames. */ ac = (eh->ether_type != htons(ETHERTYPE_PAE)) ? M_WME_GETAC(m) : WME_AC_BE; - if (ifp->if_bpf != NULL) - bpf_ops->bpf_mtap(ifp->if_bpf, m); + + bpf_mtap(ifp, m); + if ((m = ieee80211_encap(ic, m, ni)) == NULL) { ieee80211_free_node(ni); ifp->if_oerrors++; continue; } sendit: - if (ic->ic_rawbpf != NULL) - bpf_ops->bpf_mtap(ic->ic_rawbpf, m); + bpf_mtap3(ic->ic_rawbpf, m); + if (iwn_tx(sc, m, ni, ac) != 0) { ieee80211_free_node(ni); ifp->if_oerrors++; @@ -3156,7 +3143,9 @@ iwn_watchdog(struct ifnet *ifp) if (sc->sc_tx_timer > 0) { if (--sc->sc_tx_timer == 0) { - aprint_error_dev(sc->sc_dev, "device timeout\n"); + aprint_error_dev(sc->sc_dev, + "device timeout\n"); + ifp->if_flags &= ~IFF_UP; iwn_stop(ifp, 1); ifp->if_oerrors++; return; @@ -3168,37 +3157,31 @@ iwn_watchdog(struct ifnet *ifp) } static int -iwn_ioctl(struct ifnet *ifp, u_long cmd, void* data) +iwn_ioctl(struct ifnet *ifp, u_long cmd, void *data) { -#define IS_RUNNING(ifp) \ - ((ifp->if_flags & IFF_UP) && (ifp->if_flags & IFF_RUNNING)) - struct iwn_softc *sc = ifp->if_softc; struct ieee80211com *ic = &sc->sc_ic; + struct ifaddr *ifa; + const struct sockaddr *sa; int s, error = 0; s = splnet(); switch (cmd) { case SIOCSIFADDR: + ifa = (struct ifaddr *)data; + ifp->if_flags |= IFF_UP; +#ifdef INET + if (ifa->ifa_addr->sa_family == AF_INET) + arp_ifinit(&ic->ic_ac, ifa); +#endif /* FALLTHROUGH */ case SIOCSIFFLAGS: + /* XXX Added as it is in every NetBSD driver */ if ((error = ifioctl_common(ifp, cmd, data)) != 0) break; if (ifp->if_flags & IFF_UP) { - /* - * resync the radio state just in case we missed - * and event. - */ - sc->sc_radio = - (IWN_READ(sc, IWN_GP_CNTRL) & IWN_GP_CNTRL_RFKILL); - - if (!sc->sc_radio) { - error = EBUSY; /* XXX not really but same as els -ewhere in driver */ - if (ifp->if_flags & IFF_RUNNING) - iwn_stop(ifp, 1); - } else if (!(ifp->if_flags & IFF_RUNNING)) + if (!(ifp->if_flags & IFF_RUNNING)) error = iwn_init(ifp); } else { if (ifp->if_flags & IFF_RUNNING) @@ -3208,30 +3191,14 @@ ewhere in driver */ case SIOCADDMULTI: case SIOCDELMULTI: - /* XXX no h/w multicast filter? --dyoung */ - if ((error = ether_ioctl(ifp, cmd, data)) == ENETRESET) { - /* setup multicast filter, etc */ - error = 0; - } - break; + sa = ifreq_getaddr(SIOCADDMULTI, (struct ifreq *)data); + error = (cmd == SIOCADDMULTI) ? + ether_addmulti(sa, &sc->sc_ec) : + ether_delmulti(sa, &sc->sc_ec); -#if 0 - case SIOCS80211POWER: - error = ieee80211_ioctl(ifp, cmd, data); - if (error != ENETRESET) - break; - if (ic->ic_state == IEEE80211_S_RUN && - sc->calib.state == IWN_CALIB_STATE_RUN) { - if (ic->ic_flags & IEEE80211_F_PMGTON) - error = iwn_set_pslevel(sc, 0, 3, 0); - else /* back to CAM */ - error = iwn_set_pslevel(sc, 0, 0, 0); - } else { - /* Defer until transition to IWN_CALIB_STATE_RUN. */ + if (error == ENETRESET) error = 0; - } break; -#endif default: error = ieee80211_ioctl(ic, cmd, data); @@ -3239,15 +3206,15 @@ ewhere in driver */ if (error == ENETRESET) { error = 0; - if (IS_RUNNING(ifp) && - (ic->ic_roaming != IEEE80211_ROAMING_MANUAL)) { + if ((ifp->if_flags & (IFF_UP | IFF_RUNNING)) == + (IFF_UP | IFF_RUNNING)) { iwn_stop(ifp, 0); error = iwn_init(ifp); } } + splx(s); return error; -#undef IS_RUNNING } /* @@ -3256,7 +3223,6 @@ ewhere in driver */ static 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; @@ -3285,7 +3251,7 @@ iwn_cmd(struct iwn_softc *sc, int code, } cmd = mtod(m, struct iwn_tx_cmd *); error = bus_dmamap_load(sc->sc_dmat, data->map, cmd, totlen, - NULL, BUS_DMA_NOWAIT); + NULL, BUS_DMA_NOWAIT | BUS_DMA_WRITE); if (error != 0) { m_freem(m); return error; @@ -3319,8 +3285,11 @@ iwn_cmd(struct iwn_softc *sc, int code, (char *)(void *)desc - (char *)(void *)ring->desc_dma.vaddr, sizeof (*desc), BUS_DMASYNC_PREWRITE); +#ifdef notyet /* Update TX scheduler. */ - hal->update_sched(sc, ring->qid, ring->cur, 0, 0); + sc->sc_hal->update_sched(sc, ring->qid, ring->cur, 0, 0); +#endif + DPRINTFN(4, ("iwn_cmd %d size=%d %s\n", code, size, async ? " (async)" : "")); /* Kick command ring. */ ring->cur = (ring->cur + 1) % IWN_TX_RING_COUNT; @@ -3330,48 +3299,6 @@ iwn_cmd(struct iwn_softc *sc, int code, } static int -iwn_add_node(struct iwn_softc *sc, struct ieee80211_node *ni, bool broadcast, - bool async, uint32_t htflags) -{ - const struct iwn_hal *hal = sc->sc_hal; - struct iwn_node_info node; - int error; - - error = 0; - - memset(&node, 0, sizeof node); - if (broadcast == true) { - IEEE80211_ADDR_COPY(node.macaddr, etherbroadcastaddr); - node.id = hal->broadcast_id; - DPRINTF(("adding broadcast node\n")); - } else { - IEEE80211_ADDR_COPY(node.macaddr, ni->ni_macaddr); - node.id = IWN_ID_BSS; - node.htflags = htole32(htflags); - DPRINTF(("adding BSS node\n")); - } - if ((error = hal->add_node(sc, &node, async)) != 0) { - aprint_error_dev(sc->sc_dev, "could not add %s node\n", - (broadcast == 1)? "broadcast" : "BSS"); - return error; - } - DPRINTF(("setting link quality for node %d\n", node.id)); - if ((error = iwn_set_link_quality(sc, ni)) != 0) { - aprint_error_dev(sc->sc_dev, - "could not setup MRR for %s node\n", - (broadcast == 1)? "broadcast" : "BSS"); - return error; - } - if ((error = iwn_init_sensitivity(sc)) != 0) { - aprint_error_dev(sc->sc_dev, "could not set sensitivity\n"); - return error; - } - - return error; -} - - -static int iwn4965_add_node(struct iwn_softc *sc, struct iwn_node_info *node, int async) { struct iwn4965_node_info hnode; @@ -3408,13 +3335,13 @@ iwn_set_link_quality(struct iwn_softc *s int i, txrate; /* Use the first valid TX antenna. */ - txant = IWN_LSB(sc->txantmsk); + txant = IWN_LSB(sc->txchainmask); memset(&linkq, 0, sizeof linkq); linkq.id = wn->id; linkq.antmsk_1stream = txant; - linkq.antmsk_2stream = IWN_ANT_A | IWN_ANT_B; - linkq.ampdu_max = 64; + linkq.antmsk_2stream = IWN_ANT_AB; + linkq.ampdu_max = 31; linkq.ampdu_threshold = 3; linkq.ampdu_limit = htole16(4000); /* 4ms */ @@ -3453,12 +3380,12 @@ iwn_add_broadcast_node(struct iwn_softc return error; /* Use the first valid TX antenna. */ - txant = IWN_LSB(sc->txantmsk); + txant = IWN_LSB(sc->txchainmask); memset(&linkq, 0, sizeof linkq); linkq.id = hal->broadcast_id; linkq.antmsk_1stream = txant; - linkq.antmsk_2stream = IWN_ANT_A | IWN_ANT_B; + linkq.antmsk_2stream = IWN_ANT_AB; linkq.ampdu_max = 64; linkq.ampdu_threshold = 3; linkq.ampdu_limit = htole16(4000); /* 4ms */ @@ -3477,31 +3404,6 @@ iwn_add_broadcast_node(struct iwn_softc return iwn_cmd(sc, IWN_CMD_LINK_QUALITY, &linkq, sizeof linkq, async); } -#ifdef notyet -static void -iwn_updateedca(struct ieee80211com *ic) -{ -#define IWN_EXP2(x) ((1 << (x)) - 1) /* CWmin = 2^ECWmin - 1 */ - struct iwn_softc *sc = ic->ic_softc; - struct iwn_edca_params cmd; - int aci; - - memset(&cmd, 0, sizeof cmd); - cmd.flags = htole32(IWN_EDCA_UPDATE); - for (aci = 0; aci < EDCA_NUM_AC; aci++) { - const struct ieee80211_edca_ac_params *ac = - &ic->ic_edca_ac[aci]; - cmd.ac[aci].aifsn = ac->ac_aifsn; - cmd.ac[aci].cwmin = htole16(IWN_EXP2(ac->ac_ecwmin)); - cmd.ac[aci].cwmax = htole16(IWN_EXP2(ac->ac_ecwmax)); - cmd.ac[aci].txoplimit = - htole16(IEEE80211_TXOP_TO_US(ac->ac_txoplimit)); - } - (void)iwn_cmd(sc, IWN_CMD_EDCA_PARAMS, &cmd, sizeof cmd, 1); -#undef IWN_EXP2 -} -#endif - static void iwn_set_led(struct iwn_softc *sc, uint8_t which, uint8_t off, uint8_t on) { @@ -3518,18 +3420,26 @@ iwn_set_led(struct iwn_softc *sc, uint8_ } /* - * Set the critical temperature at which the firmware will notify us. + * Set the critical temperature at which the firmware will stop the radio + * and notify us. */ static int iwn_set_critical_temp(struct iwn_softc *sc) { struct iwn_critical_temp crit; + int32_t temp; IWN_WRITE(sc, IWN_UCODE_GP1_CLR, IWN_UCODE_GP1_CTEMP_STOP_RF); + if (sc->hw_type == IWN_HW_REV_TYPE_5150) + temp = (IWN_CTOK(110) - sc->temp_off) * -5; + else if (sc->hw_type == IWN_HW_REV_TYPE_4965) + temp = IWN_CTOK(110); + else + temp = 110; memset(&crit, 0, sizeof crit); - crit.tempR = htole32(sc->critical_temp); - DPRINTF(("setting critical temperature to %u\n", sc->critical_temp)); + crit.tempR = htole32(temp); + DPRINTF(("setting critical temperature to %d\n", temp)); return iwn_cmd(sc, IWN_CMD_SET_CRITICAL_TEMP, &crit, sizeof crit, 0); } @@ -3549,14 +3459,12 @@ iwn_set_timing(struct iwn_softc *sc, str mod = le64toh(cmd.tstamp) % val; cmd.binitval = htole32((uint32_t)(val - mod)); - DPRINTF(("timing bintval=%u, tstamp=%llu, init=%u\n", - ni->ni_intval, (unsigned long long)le64toh(cmd.tstamp), - (uint32_t)(val - mod))); + DPRINTF(("timing bintval=%u, tstamp=%zu, init=%u\n", + ni->ni_intval, le64toh(cmd.tstamp), (uint32_t)(val - mod))); return iwn_cmd(sc, IWN_CMD_TIMING, &cmd, sizeof cmd, 1); } -#if 0 static void iwn4965_power_calibration(struct iwn_softc *sc, int temp) { @@ -3568,7 +3476,6 @@ iwn4965_power_calibration(struct iwn_sof (void)iwn4965_set_txpower(sc, 1); } } -#endif /* * Set TX power for current channel (each rate has its own power settings). @@ -3624,7 +3531,7 @@ iwn4965_set_txpower(struct iwn_softc *sc DPRINTF(("voltage compensation=%d (UCODE=%d, EEPROM=%d)\n", vdiff, le32toh(uc->volt), sc->eeprom_voltage)); - /* Get channel's attenuation group. */ + /* Get channel attenuation group. */ if (chan <= 20) /* 1-20 */ grp = 4; else if (chan <= 43) /* 34-43 */ @@ -3637,11 +3544,13 @@ iwn4965_set_txpower(struct iwn_softc *sc grp = 3; DPRINTF(("chan %d, attenuation group=%d\n", chan, grp)); - /* Get channel's sub-band. */ + /* Get channel sub-band. */ for (i = 0; i < IWN_NBANDS; i++) if (sc->bands[i].lo != 0 && sc->bands[i].lo <= chan && chan <= sc->bands[i].hi) break; + if (i == IWN_NBANDS) /* Can't happen in real-life. */ + return EINVAL; chans = sc->bands[i].chans; DPRINTF(("chan %d sub-band=%d\n", chan, i)); @@ -3667,6 +3576,7 @@ iwn4965_set_txpower(struct iwn_softc *sc "EEPROM=%d)\n", tdiff, sc->temp, temp)); for (ridx = 0; ridx <= IWN_RIDX_MAX; ridx++) { + /* Convert dBm to half-dBm. */ maxchpwr = sc->maxpwr[chan] * 2; if ((ridx / 8) & 1) maxchpwr -= 6; /* MIMO 2T: -3dB */ @@ -3683,7 +3593,7 @@ iwn4965_set_txpower(struct iwn_softc *sc else pwr -= 10; /* Others: -5dB */ - /* Do not exceed channel's max TX power. */ + /* Do not exceed channel max TX power. */ if (pwr > maxchpwr) pwr = maxchpwr; @@ -3743,7 +3653,7 @@ iwn4965_get_rssi(const struct iwn_rx_sta uint8_t mask, agc; int rssi; - mask = (le16toh(phy->antenna) >> 4) & 0x7; + mask = (le16toh(phy->antenna) >> 4) & IWN_ANT_ABC; agc = (le16toh(phy->agc) >> 7) & 0x7f; rssi = 0; @@ -3822,12 +3732,19 @@ iwn4965_get_temperature(struct iwn_softc static int iwn5000_get_temperature(struct iwn_softc *sc) { + int32_t temp; + /* * Temperature is not used by the driver for 5000 Series because * TX power calibration is handled by firmware. We export it to * users through the sensor framework though. */ - return le32toh(sc->rawtemp); + temp = le32toh(sc->rawtemp); + if (sc->hw_type == IWN_HW_REV_TYPE_5150) { + temp = (temp / -5) + sc->temp_off; + temp = IWN_KTOC(temp); + } + return temp; } /* @@ -3846,13 +3763,13 @@ iwn_init_sensitivity(struct iwn_softc *s calib->state = IWN_CALIB_STATE_INIT; calib->cck_state = IWN_CCK_STATE_HIFA; /* Set initial correlation values. */ - calib->ofdm_x1 = hal->limits->min_ofdm_x1; - calib->ofdm_mrc_x1 = hal->limits->min_ofdm_mrc_x1; - calib->ofdm_x4 = 90; - calib->ofdm_mrc_x4 = hal->limits->min_ofdm_mrc_x4; + calib->ofdm_x1 = sc->limits->min_ofdm_x1; + calib->ofdm_mrc_x1 = sc->limits->min_ofdm_mrc_x1; + calib->ofdm_x4 = sc->limits->min_ofdm_x4; + calib->ofdm_mrc_x4 = sc->limits->min_ofdm_mrc_x4; calib->cck_x4 = 125; - calib->cck_mrc_x4 = hal->limits->min_cck_mrc_x4; - calib->energy_cck = hal->limits->energy_cck; + calib->cck_mrc_x4 = sc->limits->min_cck_mrc_x4; + calib->energy_cck = sc->limits->energy_cck; /* Write initial sensitivity. */ if ((error = iwn_send_sensitivity(sc)) != 0) @@ -3896,26 +3813,29 @@ iwn_collect_noise(struct iwn_softc *sc, val = MAX(calib->rssi[2], val); /* Determine which antennas are connected. */ - sc->antmsk = 0; + sc->chainmask = sc->rxchainmask; for (i = 0; i < 3; i++) - if (val - calib->rssi[i] <= 15 * 20) - sc->antmsk |= 1 << i; + if (val - calib->rssi[i] > 15 * 20) + sc->chainmask &= ~(1 << i); + DPRINTF(("RX chains mask: theoretical=0x%x, actual=0x%x\n", + sc->rxchainmask, sc->chainmask)); + /* If none of the TX antennas are connected, keep at least one. */ - if ((sc->antmsk & sc->txantmsk) == 0) - sc->antmsk |= IWN_LSB(sc->txantmsk); + if ((sc->chainmask & sc->txchainmask) == 0) + sc->chainmask |= IWN_LSB(sc->txchainmask); (void)hal->set_gains(sc); calib->state = IWN_CALIB_STATE_RUN; #ifdef notyet /* XXX Disable RX chains with no antennas connected. */ - sc->rxon.rxchain = htole16(IWN_RXCHAIN_SEL(sc->antmsk)); - (void)iwn_cmd(sc, IWN_CMD_CONFIGURE, &sc->rxon, hal->rxonsz, 1); + sc->rxon.rxchain = htole16(IWN_RXCHAIN_SEL(sc->chainmask)); + (void)iwn_cmd(sc, IWN_CMD_RXON, &sc->rxon, hal->rxonsz, 1); +#endif /* Enable power-saving mode if requested by user. */ if (sc->sc_ic.ic_flags & IEEE80211_F_PMGTON) (void)iwn_set_pslevel(sc, 0, 3, 1); -#endif } static int @@ -3935,10 +3855,6 @@ iwn5000_init_gains(struct iwn_softc *sc) { struct iwn_phy_calib cmd; - if (sc->hw_type == IWN_HW_REV_TYPE_6000 || - sc->hw_type == IWN_HW_REV_TYPE_6050) - return 0; - memset(&cmd, 0, sizeof cmd); cmd.code = IWN5000_PHY_CALIB_RESET_NOISE_GAIN; cmd.ngroups = 1; @@ -3957,14 +3873,14 @@ iwn4965_set_gains(struct iwn_softc *sc) /* 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)) + if (sc->chainmask & (1 << i)) noise = MIN(calib->noise[i], noise); memset(&cmd, 0, sizeof cmd); cmd.code = IWN4965_PHY_CALIB_DIFF_GAIN; /* Set differential gains for connected antennas. */ for (i = 0; i < 3; i++) { - if (sc->antmsk & (1 << i)) { + if (sc->chainmask & (1 << i)) { /* Compute attenuation (in unit of 1.5dB). */ delta = (noise - (int32_t)calib->noise[i]) / 30; /* NB: delta <= 0 */ @@ -3975,7 +3891,7 @@ iwn4965_set_gains(struct iwn_softc *sc) } } DPRINTF(("setting differential gains Ant A/B/C: %x/%x/%x (%x)\n", - cmd.gain[0], cmd.gain[1], cmd.gain[2], sc->antmsk)); + cmd.gain[0], cmd.gain[1], cmd.gain[2], sc->chainmask)); return iwn_cmd(sc, IWN_CMD_PHY_CALIB, &cmd, sizeof cmd, 1); } @@ -3984,30 +3900,31 @@ iwn5000_set_gains(struct iwn_softc *sc) { struct iwn_calib_state *calib = &sc->calib; struct iwn_phy_calib_gain cmd; - int i, delta; + int i, ant, div, delta; - if (sc->hw_type == IWN_HW_REV_TYPE_6000 || - sc->hw_type == IWN_HW_REV_TYPE_6050) - return 0; + /* We collected 20 beacons and !=6050 need a 1.5 factor. */ + div = (sc->hw_type == IWN_HW_REV_TYPE_6050) ? 20 : 30; memset(&cmd, 0, sizeof cmd); cmd.code = IWN5000_PHY_CALIB_NOISE_GAIN; cmd.ngroups = 1; cmd.isvalid = 1; - /* Set differential gains for antennas B and C. */ - for (i = 1; i < 3; i++) { - if (sc->antmsk & (1 << i)) { - /* The delta is relative to antenna A. */ - delta = ((int32_t)calib->noise[0] - - (int32_t)calib->noise[i]) / 30; + /* Get first available RX antenna as referential. */ + ant = IWN_LSB(sc->rxchainmask); + /* Set differential gains for other antennas. */ + for (i = ant + 1; i < 3; i++) { + if (sc->chainmask & (1 << i)) { + /* The delta is relative to antenna "ant". */ + delta = ((int32_t)calib->noise[ant] - + (int32_t)calib->noise[i]) / div; /* Limit to [-4.5dB,+4.5dB]. */ cmd.gain[i - 1] = MIN(abs(delta), 3); if (delta < 0) cmd.gain[i - 1] |= 1 << 2; /* sign bit */ } } - DPRINTF(("setting differential gains Ant B/C: %x/%x (%x)\n", - cmd.gain[0], cmd.gain[1], sc->antmsk)); + DPRINTF(("setting differential gains: %x/%x (%x)\n", + cmd.gain[0], cmd.gain[1], sc->chainmask)); return iwn_cmd(sc, IWN_CMD_PHY_CALIB, &cmd, sizeof cmd, 1); } @@ -4035,8 +3952,7 @@ iwn_tune_sensitivity(struct iwn_softc *s needs_update = 1; \ } - const struct iwn_hal *hal = sc->sc_hal; - const struct iwn_sensitivity_limits *limits = hal->limits; + const struct iwn_sensitivity_limits *limits = sc->limits; struct iwn_calib_state *calib = &sc->calib; uint32_t val, rxena, fa; uint32_t energy[3], energy_min; @@ -4165,7 +4081,6 @@ iwn_tune_sensitivity(struct iwn_softc *s static 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; @@ -4176,7 +4091,7 @@ iwn_send_sensitivity(struct iwn_softc *s cmd.corr_ofdm_mrc_x1 = htole16(calib->ofdm_mrc_x1); cmd.corr_ofdm_x4 = htole16(calib->ofdm_x4); cmd.corr_ofdm_mrc_x4 = htole16(calib->ofdm_mrc_x4); - cmd.energy_ofdm = htole16(hal->limits->energy_ofdm); + cmd.energy_ofdm = htole16(sc->limits->energy_ofdm); cmd.energy_ofdm_th = htole16(62); /* CCK modulation. */ cmd.corr_cck_x4 = htole16(calib->cck_x4); @@ -4193,7 +4108,6 @@ iwn_send_sensitivity(struct iwn_softc *s return iwn_cmd(sc, IWN_CMD_SET_SENSITIVITY, &cmd, sizeof cmd, 1); } -#if 0 /* * Set STA mode power saving level (between 0 and 5). * Level 0 is CAM (Continuously Aware Mode), 5 is for maximum power saving. @@ -4203,7 +4117,7 @@ iwn_set_pslevel(struct iwn_softc *sc, in { struct iwn_pmgt_cmd cmd; const struct iwn_pmgt *pmgt; - uint32_t umax, skip_dtim; + uint32_t maxp, skip_dtim; pcireg_t reg; int i; @@ -4235,20 +4149,19 @@ iwn_set_pslevel(struct iwn_softc *sc, in skip_dtim = pmgt->skip_dtim; if (skip_dtim != 0) { cmd.flags |= htole16(IWN_PS_SLEEP_OVER_DTIM); - umax = pmgt->intval[4]; - if (umax == (uint32_t)-1) - umax = dtim * (skip_dtim + 1); - else if (umax > dtim) - umax = (umax / dtim) * dtim; + maxp = pmgt->intval[4]; + if (maxp == (uint32_t)-1) + maxp = dtim * (skip_dtim + 1); + else if (maxp > dtim) + maxp = (maxp / dtim) * dtim; } else - umax = dtim; + maxp = dtim; for (i = 0; i < 5; i++) - cmd.intval[i] = htole32(MIN(umax, pmgt->intval[i])); + cmd.intval[i] = htole32(MIN(maxp, 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); } -#endif static int iwn_config(struct iwn_softc *sc) @@ -4257,35 +4170,28 @@ iwn_config(struct iwn_softc *sc) struct ieee80211com *ic = &sc->sc_ic; struct ifnet *ifp = ic->ic_ifp; struct iwn_bluetooth bluetooth; + uint32_t txmask; uint16_t rxchain; int error; - struct iwn_pmgt_cmd power; - -#if 0 - /* Set power saving level to CAM during initialization. */ - if ((error = iwn_set_pslevel(sc, 0, 0, 0)) != 0) { - aprint_error_dev(sc->sc_dev, - "could not set power saving level\n"); - return error; - } -#else - /* set power mode */ - memset(&power, 0, sizeof power); - power.flags = htole16(/*IWN_POWER_CAM*/0 | 0x8); - DPRINTF(("setting power mode\n")); - error = iwn_cmd(sc, IWN_CMD_SET_POWER_MODE, &power, sizeof power, 0); - if (error != 0) { - aprint_error_dev(sc->sc_dev, "could not set power mode\n"); - return error; + /* Configure valid TX chains for 5000 Series. */ + if (sc->hw_type != IWN_HW_REV_TYPE_4965) { + txmask = htole32(sc->txchainmask); + DPRINTF(("configuring valid TX chains 0x%x\n", txmask)); + error = iwn_cmd(sc, IWN5000_CMD_TX_ANT_CONFIG, &txmask, + sizeof txmask, 0); + if (error != 0) { + aprint_error_dev(sc->sc_dev, + "could not configure valid TX chains\n"); + return error; + } } -#endif /* Configure bluetooth coexistence. */ memset(&bluetooth, 0, sizeof bluetooth); - bluetooth.flags = 3; - bluetooth.lead = 0xaa; - bluetooth.kill = 1; + bluetooth.flags = IWN_BT_COEX_CHAN_ANN | IWN_BT_COEX_BT_PRIO; + bluetooth.lead_time = IWN_BT_LEAD_TIME_DEF; + bluetooth.max_kill = IWN_BT_MAX_KILL_DEF; DPRINTF(("configuring bluetooth coexistence\n")); error = iwn_cmd(sc, IWN_CMD_BT_COEX, &bluetooth, sizeof bluetooth, 0); if (error != 0) { @@ -4294,13 +4200,12 @@ iwn_config(struct iwn_softc *sc) return error; } - /* Configure adapter. */ + /* Set mode, channel, RX filter and enable RX. */ memset(&sc->rxon, 0, sizeof (struct iwn_rxon)); IEEE80211_ADDR_COPY(ic->ic_myaddr, CLLADDR(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 = htole16(ieee80211_chan2ieee(ic, ic->ic_ibss_chan)); + 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); @@ -4322,33 +4227,30 @@ iwn_config(struct iwn_softc *sc) sc->rxon.ofdm_mask = 0xff; /* not yet negotiated */ sc->rxon.ht_single_mask = 0xff; sc->rxon.ht_dual_mask = 0xff; - rxchain = IWN_RXCHAIN_VALID(IWN_ANT_ABC) | IWN_RXCHAIN_IDLE_COUNT(2) | - IWN_RXCHAIN_MIMO_COUNT(2); + sc->rxon.ht_triple_mask = 0xff; + rxchain = + IWN_RXCHAIN_VALID(sc->rxchainmask) | + IWN_RXCHAIN_MIMO_COUNT(2) | + IWN_RXCHAIN_IDLE_COUNT(2); sc->rxon.rxchain = htole16(rxchain); DPRINTF(("setting configuration\n")); -#ifdef notdef - 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); -#endif - 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, 0); + error = iwn_cmd(sc, IWN_CMD_RXON, &sc->rxon, hal->rxonsz, 0); if (error != 0) { - aprint_error_dev(sc->sc_dev, "configure command failed\n"); + aprint_error_dev(sc->sc_dev, + "RXON command failed\n"); return error; } - /* Configuration has changed, set TX power accordingly. */ - if ((error = hal->set_txpower(sc, 0)) != 0) { - aprint_error_dev(sc->sc_dev, "could not set TX power\n"); + if ((error = iwn_add_broadcast_node(sc, 0)) != 0) { + aprint_error_dev(sc->sc_dev, + "could not add broadcast node\n"); return error; } - if ((error = iwn_add_broadcast_node(sc, 0)) != 0) { - aprint_error_dev(sc->sc_dev, "could not add broadcast node\n"); + /* Configuration has changed, set TX power accordingly. */ + if ((error = hal->set_txpower(sc, 0)) != 0) { + aprint_error_dev(sc->sc_dev, + "could not set TX power\n"); return error; } @@ -4357,6 +4259,13 @@ iwn_config(struct iwn_softc *sc) "could not set critical temperature\n"); return error; } + + /* Set power saving level to CAM during initialization. */ + if ((error = iwn_set_pslevel(sc, 0, 0, 0)) != 0) { + aprint_error_dev(sc->sc_dev, + "could not set power saving level\n"); + return error; + } return 0; } @@ -4366,15 +4275,15 @@ iwn_scan(struct iwn_softc *sc, uint16_t struct ieee80211com *ic = &sc->sc_ic; struct iwn_scan_hdr *hdr; struct iwn_cmd_data *tx; + struct iwn_scan_essid *essid; struct iwn_scan_chan *chan; struct ieee80211_frame *wh; struct ieee80211_rateset *rs; struct ieee80211_channel *c; - enum ieee80211_phymode mode; uint8_t *buf, *frm; uint16_t rxchain; uint8_t txant; - int buflen, error, nrates; + int buflen, error; buf = malloc(IWN_SCAN_MAXSZ, M_DEVBUF, M_NOWAIT | M_ZERO); if (buf == NULL) { @@ -4391,24 +4300,26 @@ iwn_scan(struct iwn_softc *sc, uint16_t hdr->quiet_threshold = htole16(1); /* min # of packets */ /* Select antennas for scanning. */ - rxchain = IWN_RXCHAIN_FORCE | IWN_RXCHAIN_VALID(IWN_ANT_ABC) | - IWN_RXCHAIN_MIMO(IWN_ANT_ABC); + rxchain = + IWN_RXCHAIN_VALID(sc->rxchainmask) | + IWN_RXCHAIN_FORCE_MIMO_SEL(sc->rxchainmask) | + IWN_RXCHAIN_DRIVER_FORCE; if ((flags & IEEE80211_CHAN_5GHZ) && sc->hw_type == IWN_HW_REV_TYPE_4965) { /* Ant A must be avoided in 5GHz because of an HW bug. */ - rxchain |= IWN_RXCHAIN_SEL(IWN_ANT_B | IWN_ANT_C); + rxchain |= IWN_RXCHAIN_FORCE_SEL(IWN_ANT_BC); } else /* Use all available RX antennas. */ - rxchain |= IWN_RXCHAIN_SEL(IWN_ANT_ABC); + rxchain |= IWN_RXCHAIN_FORCE_SEL(sc->rxchainmask); hdr->rxchain = htole16(rxchain); hdr->filter = htole32(IWN_FILTER_MULTICAST | IWN_FILTER_BEACON); - tx = &(hdr->tx_cmd); + tx = (struct iwn_cmd_data *)(hdr + 1); tx->flags = htole32(IWN_TX_AUTO_SEQ); tx->id = sc->sc_hal->broadcast_id; tx->lifetime = htole32(IWN_LIFETIME_INFINITE); if (flags & IEEE80211_CHAN_5GHZ) { - hdr->crc_threshold = htole16(1); + hdr->crc_threshold = 0xffff; /* Send probe requests at 6Mbps. */ tx->plcp = iwn_rates[IWN_RIDX_OFDM6].plcp; rs = &ic->ic_sup_rates[IEEE80211_MODE_11A]; @@ -4420,19 +4331,20 @@ iwn_scan(struct iwn_softc *sc, uint16_t rs = &ic->ic_sup_rates[IEEE80211_MODE_11G]; } /* Use the first valid TX antenna. */ - txant = IWN_LSB(sc->txantmsk); + txant = IWN_LSB(sc->txchainmask); tx->rflags |= IWN_RFLAG_ANT(txant); + essid = (struct iwn_scan_essid *)(tx + 1); if (ic->ic_des_esslen != 0) { - hdr->scan_essid[0].id = IEEE80211_ELEMID_SSID; - hdr->scan_essid[0].len = ic->ic_des_esslen; - memcpy(hdr->scan_essid[0].data, ic->ic_des_essid, ic->ic_des_esslen); + 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); } /* * Build a probe request frame. Most of the following code is a * copy & paste of what is done in net80211. */ - wh = &(hdr->wh); + 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; @@ -4442,30 +4354,15 @@ iwn_scan(struct iwn_softc *sc, uint16_t *(uint16_t *)&wh->i_dur[0] = 0; /* filled by HW */ *(uint16_t *)&wh->i_seq[0] = 0; /* filled by HW */ - frm = &(hdr->data[0]); - /* add empty SSID IE */ - *frm++ = IEEE80211_ELEMID_SSID; - *frm++ = 0; - - mode = ieee80211_chan2mode(ic, ic->ic_ibss_chan); - rs = &ic->ic_sup_rates[mode]; - - /* add supported rates IE */ - *frm++ = IEEE80211_ELEMID_RATES; - nrates = rs->rs_nrates; - if (nrates > IEEE80211_RATE_SIZE) - nrates = IEEE80211_RATE_SIZE; - *frm++ = nrates; - memcpy(frm, rs->rs_rates, nrates); - frm += nrates; - - if (rs->rs_nrates > IEEE80211_RATE_SIZE) { - nrates = rs->rs_nrates - IEEE80211_RATE_SIZE; - *frm++ = IEEE80211_ELEMID_XRATES; - *frm++ = nrates; - memcpy(frm, rs->rs_rates + IEEE80211_RATE_SIZE, nrates); - frm += nrates; - } + frm = (uint8_t *)(wh + 1); + frm = ieee80211_add_ssid(frm, NULL, 0); + frm = ieee80211_add_rates(frm, rs); +#ifndef IEEE80211_NO_HT + if (ic->ic_flags & IEEE80211_F_HTON) + frm = ieee80211_add_htcaps(frm, ic); +#endif + if (rs->rs_nrates > IEEE80211_RATE_SIZE) + frm = ieee80211_add_xrates(frm, rs); /* Set length of probe request. */ tx->len = htole16(frm - (uint8_t *)wh); @@ -4514,12 +4411,9 @@ iwn_auth(struct iwn_softc *sc) struct ieee80211_node *ni = ic->ic_bss; int error; - sc->calib.state = IWN_CALIB_STATE_INIT; - - /* Update adapter's configuration. */ - sc->rxon.associd = 0; + /* Update adapter configuration. */ IEEE80211_ADDR_COPY(sc->rxon.bssid, ni->ni_bssid); - sc->rxon.chan = htole16(ieee80211_chan2ieee(ic, ni->ni_chan)); + 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); @@ -4539,74 +4433,31 @@ iwn_auth(struct iwn_softc *sc) default: /* Assume 802.11b/g. */ sc->rxon.cck_mask = 0x0f; sc->rxon.ofdm_mask = 0x15; - break; - } -#if 1 - 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) { - aprint_error_dev(sc->sc_dev, "could not configure\n"); - return error; - } - - /* Configuration has changed, set TX power accordingly. */ - if ((error = hal->set_txpower(sc, 1)) != 0) { - aprint_error_dev(sc->sc_dev, "could not set TX power\n"); - return error; - } - /* - * Reconfiguring RXON clears the firmware's nodes table so we must - * add the broadcast node again. - */ - if ((error = iwn_add_broadcast_node(sc, 1)) != 0) { - aprint_error_dev(sc->sc_dev, "could not add broadcast node\n"); - return error; } -#else -/* iwn_enable_tsf(sc, ni);*/ - 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 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); + error = iwn_cmd(sc, IWN_CMD_RXON, &sc->rxon, hal->rxonsz, 1); if (error != 0) { - aprint_error_dev(sc->sc_dev, "could not configure\n"); + aprint_error_dev(sc->sc_dev, + "RXON command failed\n"); return error; } /* Configuration has changed, set TX power accordingly. */ if ((error = hal->set_txpower(sc, 1)) != 0) { - aprint_error_dev(sc->sc_dev, "could not set TX power\n"); + aprint_error_dev(sc->sc_dev, + "could not set TX power\n"); return error; } /* - * Reconfiguring RXON clears the firmware's nodes table so we must + * Reconfiguring RXON clears the firmware nodes table so we must * add the broadcast node again. */ if ((error = iwn_add_broadcast_node(sc, 1)) != 0) { - aprint_error_dev(sc->sc_dev, "could not add broadcast node\n"); - return error; - } - /* add BSS node */ - DPRINTF(("adding BSS node from auth\n")); - if ((error = iwn_add_node(sc, ni, false, true, 0)) != 0) - return error; - - if (ic->ic_opmode == IEEE80211_M_STA) { - /* fake a join to init the tx rate */ - iwn_newassoc(ni, 1); - } - - if ((error = iwn_init_sensitivity(sc)) != 0) { - aprint_error_dev(sc->sc_dev, "could not set sensitivity\n"); + aprint_error_dev(sc->sc_dev, + "could not add broadcast node\n"); return error; } -#endif return 0; } @@ -4616,6 +4467,7 @@ 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) { @@ -4624,11 +4476,12 @@ iwn_run(struct iwn_softc *sc) return 0; } if ((error = iwn_set_timing(sc, ni)) != 0) { - aprint_error_dev(sc->sc_dev, "could not set timing\n"); + aprint_error_dev(sc->sc_dev, + "could not set timing\n"); return error; } - /* Update adapter's configuration. */ + /* Update adapter 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); @@ -4638,7 +4491,7 @@ iwn_run(struct iwn_softc *sc) 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); + error = iwn_cmd(sc, IWN_CMD_RXON, &sc->rxon, hal->rxonsz, 1); if (error != 0) { aprint_error_dev(sc->sc_dev, "could not update configuration\n"); @@ -4647,7 +4500,8 @@ iwn_run(struct iwn_softc *sc) /* Configuration has changed, set TX power accordingly. */ if ((error = hal->set_txpower(sc, 1)) != 0) { - aprint_error_dev(sc->sc_dev, "could not set TX power\n"); + aprint_error_dev(sc->sc_dev, + "could not set TX power\n"); return error; } @@ -4656,49 +4510,43 @@ iwn_run(struct iwn_softc *sc) iwn_newassoc(ni, 1); /* Add BSS node. */ - iwn_add_node(sc, ni, false, true, 0); + memset(&node, 0, sizeof node); + 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) { + aprint_error_dev(sc->sc_dev, + "could not add BSS node\n"); + return error; + } + DPRINTF(("setting link quality for node %d\n", node.id)); + if ((error = iwn_set_link_quality(sc, ni)) != 0) { + aprint_error_dev(sc->sc_dev, + "could not setup link quality for node %d\n", node.id); + return error; + } + + if ((error = iwn_init_sensitivity(sc)) != 0) { + aprint_error_dev(sc->sc_dev, + "could not set sensitivity\n"); + return error; + } /* Start periodic calibration timer. */ sc->calib.state = IWN_CALIB_STATE_ASSOC; sc->calib_cnt = 0; - callout_schedule(&sc->calib_to, hz / 2); + callout_schedule(&sc->calib_to, hz/2); /* Link LED always on while associated. */ iwn_set_led(sc, IWN_LED_LINK, 0, 1); return 0; } -static int -iwn_wme_update(struct ieee80211com *ic) -{ -#define IWN_EXP2(v) htole16((1 << (v)) - 1) -#define IWN_USEC(v) htole16(IEEE80211_TXOP_TO_US(v)) - struct iwn_softc *sc = ic->ic_ifp->if_softc; - const struct wmeParams *wmep; - struct iwn_edca_params cmd; - int ac; - - /* don't override default WME values if WME is not actually enabled */ - if (!(ic->ic_flags & IEEE80211_F_WME)) - return 0; - cmd.flags = 0; - for (ac = 0; ac < WME_NUM_AC; ac++) { - wmep = &ic->ic_wme.wme_chanParams.cap_wmeParams[ac]; - cmd.ac[ac].aifsn = wmep->wmep_aifsn; - cmd.ac[ac].cwmin = IWN_EXP2(wmep->wmep_logcwmin); - cmd.ac[ac].cwmax = IWN_EXP2(wmep->wmep_logcwmax); - cmd.ac[ac].txoplimit = IWN_USEC(wmep->wmep_txopLimit); - - DPRINTF(("setting WME for queue %d aifsn=%d cwmin=%d cwmax=%d " - "txop=%d\n", ac, cmd.ac[ac].aifsn, - cmd.ac[ac].cwmin, - cmd.ac[ac].cwmax, cmd.ac[ac].txoplimit)); - } - return iwn_cmd(sc, IWN_CMD_EDCA_PARAMS, &cmd, sizeof cmd, 1); -#undef IWN_USEC -#undef IWN_EXP2 -} - -#if 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. @@ -4762,15 +4610,49 @@ iwn_delete_key(struct ieee80211com *ic, } #endif +/* XXX Added for NetBSD (copied from rev 1.39). */ + +static int +iwn_wme_update(struct ieee80211com *ic) +{ +#define IWN_EXP2(v) htole16((1 << (v)) - 1) +#define IWN_USEC(v) htole16(IEEE80211_TXOP_TO_US(v)) + struct iwn_softc *sc = ic->ic_ifp->if_softc; + const struct wmeParams *wmep; + struct iwn_edca_params cmd; + int ac; + + /* don't override default WME values if WME is not actually enabled */ + if (!(ic->ic_flags & IEEE80211_F_WME)) + return 0; + cmd.flags = 0; + for (ac = 0; ac < WME_NUM_AC; ac++) { + wmep = &ic->ic_wme.wme_chanParams.cap_wmeParams[ac]; + cmd.ac[ac].aifsn = wmep->wmep_aifsn; + cmd.ac[ac].cwmin = IWN_EXP2(wmep->wmep_logcwmin); + cmd.ac[ac].cwmax = IWN_EXP2(wmep->wmep_logcwmax); + cmd.ac[ac].txoplimit = IWN_USEC(wmep->wmep_txopLimit); + + DPRINTF(("setting WME for queue %d aifsn=%d cwmin=%d cwmax=%d " + "txop=%d\n", ac, cmd.ac[ac].aifsn, + cmd.ac[ac].cwmin, + cmd.ac[ac].cwmax, cmd.ac[ac].txoplimit)); + } + return iwn_cmd(sc, IWN_CMD_EDCA_PARAMS, &cmd, sizeof cmd, 1); +#undef IWN_USEC +#undef IWN_EXP2 +} + #ifndef IEEE80211_NO_HT /* - * This function is called by upper layer when a ADDBA request is received + * 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 iwn_ampdu_rx_start(struct ieee80211com *ic, struct ieee80211_node *ni, - uint8_t tid, uint16_t ssn) + uint8_t tid) { + struct ieee80211_rx_ba *ba = &ni->ni_rx_ba[tid]; struct iwn_softc *sc = ic->ic_softc; struct iwn_node *wn = (void *)ni; struct iwn_node_info node; @@ -4780,18 +4662,19 @@ iwn_ampdu_rx_start(struct ieee80211com * node.control = IWN_NODE_UPDATE; node.flags = IWN_FLAG_SET_ADDBA; node.addba_tid = tid; - node.addba_ssn = htole16(ssn); - DPRINTFN(2, ("ADDBA RA=%d TID=%d SSN=%d\n", wn->id, tid, ssn)); + node.addba_ssn = htole16(ba->ba_winstart); + DPRINTFN(2, ("ADDBA RA=%d TID=%d SSN=%d\n", wn->id, tid, + ba->ba_winstart)); return sc->sc_hal->add_node(sc, &node, 1); } /* * This function is called by upper layer on teardown of an HT-immediate - * Block Ack (eg. uppon receipt of a DELBA frame.) + * Block Ack agreement (eg. uppon receipt of a DELBA frame.) */ static void iwn_ampdu_rx_stop(struct ieee80211com *ic, struct ieee80211_node *ni, - uint8_t tid, uint16_t ssn) + uint8_t tid) { struct iwn_softc *sc = ic->ic_softc; struct iwn_node *wn = (void *)ni; @@ -4807,13 +4690,14 @@ iwn_ampdu_rx_stop(struct ieee80211com *i } /* - * This function is called by upper layer when a ADDBA response is received + * This function is called by upper layer when an ADDBA response is received * from another STA. */ static int iwn_ampdu_tx_start(struct ieee80211com *ic, struct ieee80211_node *ni, - uint8_t tid, uint16_t ssn) + uint8_t tid) { + struct ieee80211_tx_ba *ba = &ni->ni_tx_ba[tid]; struct iwn_softc *sc = ic->ic_softc; const struct iwn_hal *hal = sc->sc_hal; struct iwn_node *wn = (void *)ni; @@ -4833,20 +4717,21 @@ iwn_ampdu_tx_start(struct ieee80211com * if ((error = iwn_nic_lock(sc)) != 0) return error; - hal->ampdu_tx_start(sc, ni, tid, ssn); + hal->ampdu_tx_start(sc, ni, tid, ba->ba_winstart); iwn_nic_unlock(sc); return 0; } static void iwn_ampdu_tx_stop(struct ieee80211com *ic, struct ieee80211_node *ni, - uint8_t tid, uint16_t ssn) + uint8_t tid) { + struct ieee80211_tx_ba *ba = &ni->ni_tx_ba[tid]; struct iwn_softc *sc = ic->ic_softc; if (iwn_nic_lock(sc) != 0) return; - sc->sc_hal->ampdu_tx_stop(sc, tid, ssn); + sc->sc_hal->ampdu_tx_stop(sc, tid, ba->ba_winstart); iwn_nic_unlock(sc); } @@ -4865,11 +4750,11 @@ iwn4965_ampdu_tx_start(struct iwn_softc iwn_mem_write_2(sc, sc->sched_base + IWN4965_SCHED_TRANS_TBL(qid), wn->id << 4 | tid); - /* Enable chain mode for the queue. */ + /* Enable chain-building mode for the queue. */ iwn_prph_setbits(sc, IWN4965_SCHED_QCHAIN_SEL, 1 << qid); /* Set starting sequence number from the ADDBA request. */ - IWN_WRITE(sc, IWN_HBUS_TARG_WRPTR, ssn); + IWN_WRITE(sc, IWN_HBUS_TARG_WRPTR, qid << 8 | (ssn & 0xff)); iwn_prph_write(sc, IWN4965_SCHED_QUEUE_RDPTR(qid), ssn); /* Set scheduler window size. */ @@ -4898,7 +4783,7 @@ iwn4965_ampdu_tx_stop(struct iwn_softc * IWN4965_TXQ_STATUS_CHGACT); /* Set starting sequence number from the ADDBA request. */ - IWN_WRITE(sc, IWN_HBUS_TARG_WRPTR, ssn); + IWN_WRITE(sc, IWN_HBUS_TARG_WRPTR, qid << 8 | (ssn & 0xff)); iwn_prph_write(sc, IWN4965_SCHED_QUEUE_RDPTR(qid), ssn); /* Disable interrupts for the queue. */ @@ -4924,14 +4809,14 @@ iwn5000_ampdu_tx_start(struct iwn_softc iwn_mem_write_2(sc, sc->sched_base + IWN5000_SCHED_TRANS_TBL(qid), wn->id << 4 | tid); - /* Enable chain mode for the queue. */ + /* Enable chain-building mode for the queue. */ iwn_prph_setbits(sc, IWN5000_SCHED_QCHAIN_SEL, 1 << qid); /* Enable aggregation for the queue. */ iwn_prph_setbits(sc, IWN5000_SCHED_AGGR_SEL, 1 << qid); /* Set starting sequence number from the ADDBA request. */ - IWN_WRITE(sc, IWN_HBUS_TARG_WRPTR, ssn); + IWN_WRITE(sc, IWN_HBUS_TARG_WRPTR, qid << 8 | (ssn & 0xff)); iwn_prph_write(sc, IWN5000_SCHED_QUEUE_RDPTR(qid), ssn); /* Set scheduler window size and frame limit. */ @@ -4959,7 +4844,7 @@ iwn5000_ampdu_tx_stop(struct iwn_softc * iwn_prph_clrbits(sc, IWN5000_SCHED_AGGR_SEL, 1 << qid); /* Set starting sequence number from the ADDBA request. */ - IWN_WRITE(sc, IWN_HBUS_TARG_WRPTR, ssn); + IWN_WRITE(sc, IWN_HBUS_TARG_WRPTR, qid << 8 | (ssn & 0xff)); iwn_prph_write(sc, IWN5000_SCHED_QUEUE_RDPTR(qid), ssn); /* Disable interrupts for the queue. */ @@ -4969,7 +4854,7 @@ iwn5000_ampdu_tx_stop(struct iwn_softc * iwn_prph_write(sc, IWN5000_SCHED_QUEUE_STATUS(qid), IWN5000_TXQ_STATUS_INACTIVE | iwn_tid2fifo[tid]); } -#endif /* 0 */ +#endif /* !IEEE80211_NO_HT */ /* * Query calibration tables from the initialization firmware. We do this @@ -4992,7 +4877,9 @@ iwn5000_query_calibration(struct iwn_sof return error; /* Wait at most two seconds for calibration to complete. */ - return tsleep(sc, PCATCH, "iwncal", 2 * hz); + if (!(sc->sc_flags & IWN_FLAG_CALIB_DONE)) + error = tsleep(sc, PCATCH, "iwncal", 2 * hz); + return error; } /* @@ -5020,6 +4907,32 @@ iwn5000_send_calibration(struct iwn_soft return 0; } +static int +iwn5000_send_wimax_coex(struct iwn_softc *sc) +{ + struct iwn5000_wimax_coex wimax; + +#ifdef notyet + if (sc->hw_type == IWN_HW_REV_TYPE_6050) { + /* Enable WiMAX coexistence for combo adapters. */ + wimax.flags = + IWN_WIMAX_COEX_ASSOC_WA_UNMASK | + IWN_WIMAX_COEX_UNASSOC_WA_UNMASK | + IWN_WIMAX_COEX_STA_TABLE_VALID | + IWN_WIMAX_COEX_ENABLE; + memcpy(wimax.events, iwn6050_wimax_events, + sizeof iwn6050_wimax_events); + } else +#endif + { + /* Disable WiMAX coexistence. */ + wimax.flags = 0; + memset(wimax.events, 0, sizeof wimax.events); + } + DPRINTF(("Configuring WiMAX coexistence\n")); + return iwn_cmd(sc, IWN5000_CMD_WIMAX_COEX, &wimax, sizeof wimax, 0); +} + /* * This function is called after the runtime firmware notifies us of its * readiness (called in a process context.) @@ -5032,10 +4945,10 @@ iwn4965_post_alive(struct iwn_softc *sc) if ((error = iwn_nic_lock(sc)) != 0) return error; - /* Clear TX scheduler's state in SRAM. */ + /* Clear TX scheduler state in SRAM. */ sc->sched_base = iwn_prph_read(sc, IWN_SCHED_SRAM_ADDR); iwn_mem_set_region_4(sc, sc->sched_base + IWN4965_SCHED_CTX_OFF, 0, - IWN4965_SCHED_CTX_LEN); + IWN4965_SCHED_CTX_LEN / sizeof (uint32_t)); /* Set physical address of TX scheduler rings (1KB aligned.) */ iwn_prph_write(sc, IWN4965_SCHED_DRAM_ADDR, sc->sched_dma.paddr >> 10); @@ -5080,24 +4993,26 @@ iwn4965_post_alive(struct iwn_softc *sc) static int iwn5000_post_alive(struct iwn_softc *sc) { - struct iwn5000_wimax_coex wimax; int error, qid; + /* Switch to using ICT interrupt mode. */ + iwn5000_ict_reset(sc); + if ((error = iwn_nic_lock(sc)) != 0) return error; - /* Clear TX scheduler's state in SRAM. */ + /* Clear TX scheduler state in SRAM. */ sc->sched_base = iwn_prph_read(sc, IWN_SCHED_SRAM_ADDR); iwn_mem_set_region_4(sc, sc->sched_base + IWN5000_SCHED_CTX_OFF, 0, - IWN5000_SCHED_CTX_LEN); + IWN5000_SCHED_CTX_LEN / sizeof (uint32_t)); /* Set physical address of TX scheduler rings (1KB aligned.) */ iwn_prph_write(sc, IWN5000_SCHED_DRAM_ADDR, sc->sched_dma.paddr >> 10); IWN_SETBITS(sc, IWN_FH_TX_CHICKEN, IWN_FH_TX_CHICKEN_SCHED_RETRY); - /* Enable chain mode for all our 20 queues. */ - iwn_prph_write(sc, IWN5000_SCHED_QCHAIN_SEL, 0xfffff); + /* Enable chain mode for all queues, except command queue. */ + iwn_prph_write(sc, IWN5000_SCHED_QCHAIN_SEL, 0xfffef); iwn_prph_write(sc, IWN5000_SCHED_AGGR_SEL, 0); for (qid = 0; qid < IWN5000_NTXQUEUES; qid++) { @@ -5125,16 +5040,13 @@ iwn5000_post_alive(struct iwn_softc *sc) } iwn_nic_unlock(sc); - /* Configure WiMAX (IEEE 802.16e) coexistence. */ - memset(&wimax, 0, sizeof wimax); - DPRINTF(("Configuring WiMAX coexistence\n")); - error = iwn_cmd(sc, IWN5000_CMD_WIMAX_COEX, &wimax, sizeof wimax, 0); + /* Configure WiMAX coexistence for combo adapters. */ + error = iwn5000_send_wimax_coex(sc); if (error != 0) { aprint_error_dev(sc->sc_dev, "could not configure WiMAX coexistence\n"); return error; } - if (sc->hw_type != IWN_HW_REV_TYPE_5150) { struct iwn5000_phy_calib_crystal cmd; @@ -5154,7 +5066,7 @@ iwn5000_post_alive(struct iwn_softc *sc) return error; } } - if (sc->sc_flags & IWN_FLAG_FIRST_BOOT) { + if (!(sc->sc_flags & IWN_FLAG_CALIB_DONE)) { /* Query calibration from the initialization firmware. */ if ((error = iwn5000_query_calibration(sc)) != 0) { aprint_error_dev(sc->sc_dev, @@ -5162,12 +5074,9 @@ iwn5000_post_alive(struct iwn_softc *sc) return error; } /* - * We have the calibration results now so we can skip - * loading the initialization firmware next time. + * We have the calibration results now, reboot with the + * runtime firmware (call ourselves recursively!) */ - sc->sc_flags &= ~IWN_FLAG_FIRST_BOOT; - - /* Reboot (call ourselves recursively!) */ iwn_hw_stop(sc); error = iwn_hw_init(sc); } else { @@ -5210,7 +5119,8 @@ iwn4965_load_bootcode(struct iwn_softc * DELAY(10); } if (ntries == 1000) { - aprint_error_dev(sc->sc_dev, "could not load boot firmware\n"); + aprint_error_dev(sc->sc_dev, + "could not load boot firmware\n"); iwn_nic_unlock(sc); return ETIMEDOUT; } @@ -5251,7 +5161,8 @@ iwn4965_load_firmware(struct iwn_softc * /* Load firmware boot code. */ error = iwn4965_load_bootcode(sc, fw->boot.text, fw->boot.textsz); if (error != 0) { - aprint_error_dev(sc->sc_dev, "could not load boot firmware\n"); + aprint_error_dev(sc->sc_dev, + "could not load boot firmware\n"); return error; } /* Now press "execute". */ @@ -5260,7 +5171,7 @@ iwn4965_load_firmware(struct iwn_softc * /* Wait at most one second for first alive notification. */ if ((error = tsleep(sc, PCATCH, "iwninit", hz)) != 0) { aprint_error_dev(sc->sc_dev, - "timeout waiting for adapter to initialize %d\n", error); + "timeout waiting for adapter to initialize\n"); return error; } @@ -5305,21 +5216,21 @@ iwn5000_load_firmware_section(struct iwn if ((error = iwn_nic_lock(sc)) != 0) return error; - IWN_WRITE(sc, IWN_FH_TX_CONFIG(IWN_SRVC_CHNL), + IWN_WRITE(sc, IWN_FH_TX_CONFIG(IWN_SRVC_DMACHNL), 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), + IWN_WRITE(sc, IWN_FH_SRAM_ADDR(IWN_SRVC_DMACHNL), dst); + IWN_WRITE(sc, IWN_FH_TFBD_CTRL0(IWN_SRVC_DMACHNL), IWN_LOADDR(dma->paddr)); - IWN_WRITE(sc, IWN_FH_TFBD_CTRL1(IWN_SRVC_CHNL), + IWN_WRITE(sc, IWN_FH_TFBD_CTRL1(IWN_SRVC_DMACHNL), IWN_HIADDR(dma->paddr) << 28 | size); - IWN_WRITE(sc, IWN_FH_TXBUF_STATUS(IWN_SRVC_CHNL), + IWN_WRITE(sc, IWN_FH_TXBUF_STATUS(IWN_SRVC_DMACHNL), 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_WRITE(sc, IWN_FH_TX_CONFIG(IWN_SRVC_DMACHNL), IWN_FH_TX_CONFIG_DMA_ENA | IWN_FH_TX_CONFIG_CIRQ_HOST_ENDTFD); iwn_nic_unlock(sc); @@ -5335,23 +5246,21 @@ iwn5000_load_firmware(struct iwn_softc * int error; /* Load the initialization firmware on first boot only. */ - fw = (sc->sc_flags & IWN_FLAG_FIRST_BOOT) ? - &sc->fw.init : &sc->fw.main; + fw = (sc->sc_flags & IWN_FLAG_CALIB_DONE) ? + &sc->fw.main : &sc->fw.init; error = iwn5000_load_firmware_section(sc, IWN_FW_TEXT_BASE, fw->text, fw->textsz); if (error != 0) { aprint_error_dev(sc->sc_dev, - "could not load firmware %s section\n", - ".text"); + "could not load firmware %s section\n", ".text"); return error; } error = iwn5000_load_firmware_section(sc, IWN_FW_DATA_BASE, fw->data, fw->datasz); if (error != 0) { aprint_error_dev(sc->sc_dev, - "could not load firmware %s section\n", - ".data"); + "could not load firmware %s section\n", ".data"); return error; } @@ -5360,85 +5269,216 @@ iwn5000_load_firmware(struct iwn_softc * return 0; } +/* + * Extract text and data sections from a legacy firmware image. + */ +static int +iwn_read_firmware_leg(struct iwn_softc *sc, struct iwn_fw_info *fw) +{ + const uint32_t *ptr; + size_t hdrlen = 24; + uint32_t rev; + + ptr = (const uint32_t *)fw->data; + rev = le32toh(*ptr++); + + /* Check firmware API version. */ + if (IWN_FW_API(rev) <= 1) { + aprint_error_dev(sc->sc_dev, + "bad firmware, need API version >=2\n"); + return EINVAL; + } + if (IWN_FW_API(rev) >= 3) { + /* Skip build number (version 2 header). */ + hdrlen += 4; + ptr++; + } + if (fw->size < hdrlen) { + aprint_error_dev(sc->sc_dev, + "firmware too short: %zd bytes\n", fw->size); + return EINVAL; + } + fw->main.textsz = le32toh(*ptr++); + fw->main.datasz = le32toh(*ptr++); + fw->init.textsz = le32toh(*ptr++); + fw->init.datasz = le32toh(*ptr++); + fw->boot.textsz = le32toh(*ptr++); + + /* Check that all firmware sections fit. */ + if (fw->size < hdrlen + fw->main.textsz + fw->main.datasz + + fw->init.textsz + fw->init.datasz + fw->boot.textsz) { + aprint_error_dev(sc->sc_dev, + "firmware too short: %zd bytes\n", fw->size); + return EINVAL; + } + + /* Get pointers to firmware sections. */ + fw->main.text = (const uint8_t *)ptr; + fw->main.data = fw->main.text + fw->main.textsz; + fw->init.text = fw->main.data + fw->main.datasz; + fw->init.data = fw->init.text + fw->init.textsz; + fw->boot.text = fw->init.data + fw->init.datasz; + return 0; +} + +/* + * Extract text and data sections from a TLV firmware image. + */ +static int +iwn_read_firmware_tlv(struct iwn_softc *sc, struct iwn_fw_info *fw, + uint16_t alt) +{ + const struct iwn_fw_tlv_hdr *hdr; + const struct iwn_fw_tlv *tlv; + const uint8_t *ptr, *end; + uint64_t altmask; + uint32_t len; + + if (fw->size < sizeof (*hdr)) { + aprint_error_dev(sc->sc_dev, + "firmware too short: %zd bytes\n", fw->size); + return EINVAL; + } + hdr = (const struct iwn_fw_tlv_hdr *)fw->data; + if (hdr->signature != htole32(IWN_FW_SIGNATURE)) { + aprint_error_dev(sc->sc_dev, + "bad firmware signature 0x%08x\n", le32toh(hdr->signature)); + return EINVAL; + } + DPRINTF(("FW: \"%.64s\", build 0x%x\n", hdr->descr, + le32toh(hdr->build))); + + /* + * Select the closest supported alternative that is less than + * or equal to the specified one. + */ + altmask = le64toh(hdr->altmask); + while (alt > 0 && !(altmask & (1ULL << alt))) + alt--; /* Downgrade. */ + DPRINTF(("using alternative %d\n", alt)); + + ptr = (const uint8_t *)(hdr + 1); + end = (const uint8_t *)(fw->data + fw->size); + + /* Parse type-length-value fields. */ + while (ptr + sizeof (*tlv) <= end) { + tlv = (const struct iwn_fw_tlv *)ptr; + len = le32toh(tlv->len); + + ptr += sizeof (*tlv); + if (ptr + len > end) { + aprint_error_dev(sc->sc_dev, + "firmware too short: %zd bytes\n", fw->size); + return EINVAL; + } + /* Skip other alternatives. */ + if (tlv->alt != 0 && tlv->alt != htole16(alt)) + goto next; + + switch (le16toh(tlv->type)) { + case IWN_FW_TLV_MAIN_TEXT: + fw->main.text = ptr; + fw->main.textsz = len; + break; + case IWN_FW_TLV_MAIN_DATA: + fw->main.data = ptr; + fw->main.datasz = len; + break; + case IWN_FW_TLV_INIT_TEXT: + fw->init.text = ptr; + fw->init.textsz = len; + break; + case IWN_FW_TLV_INIT_DATA: + fw->init.data = ptr; + fw->init.datasz = len; + break; + case IWN_FW_TLV_BOOT_TEXT: + fw->boot.text = ptr; + fw->boot.textsz = len; + break; + default: + DPRINTF(("TLV type %d not handled\n", + le16toh(tlv->type))); + break; + } + next: /* TLV fields are 32-bit aligned. */ + ptr += (len + 3) & ~3; + } + return 0; +} + static int iwn_read_firmware(struct iwn_softc *sc) { const struct iwn_hal *hal = sc->sc_hal; struct iwn_fw_info *fw = &sc->fw; - struct iwn_firmware_hdr hdr; firmware_handle_t fwh; - size_t size; int error; - /* Read firmware image from filesystem. */ + /* Initialize for error returns */ + fw->data = NULL; + fw->size = 0; + + /* Open firmware image. */ if ((error = firmware_open("if_iwn", sc->fwname, &fwh)) != 0) { aprint_error_dev(sc->sc_dev, - "could not read firmware file %s\n", sc->fwname); + "could not get firmware handle %s\n", sc->fwname); return error; } - size = firmware_get_size(fwh); - if (size < sizeof (hdr)) { + fw->size = firmware_get_size(fwh); + if (fw->size < sizeof (uint32_t)) { aprint_error_dev(sc->sc_dev, - "truncated firmware header: %zu bytes\n", size); - error = EINVAL; - goto fail2; + "firmware too short: %zd bytes\n", fw->size); + firmware_close(fwh); + return EINVAL; } - /* Extract firmware header information. */ - if ((error = firmware_read(fwh, 0, &hdr, - sizeof (struct iwn_firmware_hdr))) != 0) { - aprint_error_dev(sc->sc_dev, "can't get firmware header\n"); - goto fail2; + + /* Read the firmware. */ + fw->data = firmware_malloc(fw->size); + if (fw->data == NULL) { + aprint_error_dev(sc->sc_dev, + "not enough memory to stock firmware %s\n", sc->fwname); + firmware_close(fwh); + return ENOMEM; + } + error = firmware_read(fwh, 0, fw->data, fw->size); + firmware_close(fwh); + if (error != 0) { + aprint_error_dev(sc->sc_dev, + "could not read firmware %s\n", sc->fwname); + goto out; + } + + /* Retrieve text and data sections. */ + if (*(const uint32_t *)fw->data != 0) /* Legacy image. */ + error = iwn_read_firmware_leg(sc, fw); + else + error = iwn_read_firmware_tlv(sc, fw, 1); + if (error != 0) { + aprint_error_dev(sc->sc_dev, + "could not read firmware sections\n"); + goto out; } - fw->main.textsz = le32toh(hdr.main_textsz); - fw->main.datasz = le32toh(hdr.main_datasz); - fw->init.textsz = le32toh(hdr.init_textsz); - fw->init.datasz = le32toh(hdr.init_datasz); - fw->boot.textsz = le32toh(hdr.boot_textsz); - fw->boot.datasz = 0; - /* Sanity-check firmware header. */ + /* Make sure text and data sections fit in hardware memory. */ 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) { - aprint_error_dev(sc->sc_dev, "invalid firmware header\n"); - error = EINVAL; - goto fail2; - } - - /* 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) { - aprint_error_dev(sc->sc_dev, - "firmware file too short: %zu bytes\n", size); - error = EINVAL; - goto fail2; - } - fw->data = firmware_malloc(size); - if (fw->data == NULL) { aprint_error_dev(sc->sc_dev, - "not enough memory to stock firmware\n"); - error = ENOMEM; - goto fail2; - } - if ((error = firmware_read(fwh, 0, fw->data, size)) != 0) { - aprint_error_dev(sc->sc_dev, "can't get firmware\n"); - goto fail3; + "firmware sections too large\n"); + goto out; } - /* Get pointers to firmware sections. */ - fw->main.text = fw->data + sizeof (struct iwn_firmware_hdr); - 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; - + /* We can proceed with loading the firmware. */ return 0; -fail3: firmware_free(fw->data, size); -fail2: firmware_close(fwh); - return error; +out: + firmware_free(fw->data, fw->size); + fw->data = NULL; + fw->size = 0; + return error ? error : EINVAL; } static int @@ -5450,10 +5490,10 @@ iwn_clock_wait(struct iwn_softc *sc) IWN_SETBITS(sc, IWN_GP_CNTRL, IWN_GP_CNTRL_INIT_DONE); /* Wait for clock stabilization. */ - for (ntries = 0; ntries < 25000; ntries++) { + for (ntries = 0; ntries < 2500; ntries++) { if (IWN_READ(sc, IWN_GP_CNTRL) & IWN_GP_CNTRL_MAC_CLOCK_READY) return 0; - DELAY(100); + DELAY(10); } aprint_error_dev(sc->sc_dev, "timeout waiting for clock stabilization\n"); @@ -5461,58 +5501,53 @@ iwn_clock_wait(struct iwn_softc *sc) } static int -iwn4965_apm_init(struct iwn_softc *sc) -{ - int error; - - /* Disable L0s. */ - IWN_SETBITS(sc, IWN_GIO_CHICKEN, IWN_GIO_CHICKEN_DIS_L0S_TIMER); - IWN_SETBITS(sc, IWN_GIO_CHICKEN, IWN_GIO_CHICKEN_L1A_NO_L0S_RX); - - 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. */ - iwn_prph_setbits(sc, IWN_APMG_PCI_STT, IWN_APMG_PCI_STT_L1A_DIS); - iwn_nic_unlock(sc); - - return 0; -} - -static int -iwn5000_apm_init(struct iwn_softc *sc) +iwn_apm_init(struct iwn_softc *sc) { + pcireg_t reg; int error; - /* Disable L0s. */ + /* Disable L0s exit timer (NMI bug workaround.) */ IWN_SETBITS(sc, IWN_GIO_CHICKEN, IWN_GIO_CHICKEN_DIS_L0S_TIMER); + /* Don't wait for ICH L0s (ICH bug workaround.) */ IWN_SETBITS(sc, IWN_GIO_CHICKEN, IWN_GIO_CHICKEN_L1A_NO_L0S_RX); - /* Set Flow Handler wait threshold to the maximum. */ + /* Set FH wait threshold to max (HW bug under stress workaround.) */ IWN_SETBITS(sc, IWN_DBG_HPET_MEM, 0xffff0000); - /* Enable HAP to move adapter from L1a to L0s. */ + /* Enable HAP INTA to move adapter from L1a to L0s. */ IWN_SETBITS(sc, IWN_HW_IF_CONFIG, IWN_HW_IF_CONFIG_HAP_WAKE_L1A); - if (sc->hw_type != IWN_HW_REV_TYPE_6000 && - sc->hw_type != IWN_HW_REV_TYPE_6050) + /* Retrieve PCIe Active State Power Management (ASPM). */ + reg = pci_conf_read(sc->sc_pct, sc->sc_pcitag, + sc->sc_cap_off + PCI_PCIE_LCSR); + /* Workaround for HW instability in PCIe L0->L0s->L1 transition. */ + 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 (sc->hw_type != IWN_HW_REV_TYPE_4965 && + sc->hw_type <= IWN_HW_REV_TYPE_1000) IWN_SETBITS(sc, IWN_ANA_PLL, IWN_ANA_PLL_INIT); + /* Wait for clock stabilization before accessing prph. */ if ((error = iwn_clock_wait(sc)) != 0) - return error; + 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); + if (sc->hw_type == IWN_HW_REV_TYPE_4965) { + /* Enable DMA and BSM (Bootstrap State Machine.) */ + iwn_prph_write(sc, IWN_APMG_CLK_EN, + IWN_APMG_CLK_CTRL_DMA_CLK_RQT | + IWN_APMG_CLK_CTRL_BSM_CLK_RQT); + } else { + /* Enable DMA. */ + iwn_prph_write(sc, IWN_APMG_CLK_EN, + IWN_APMG_CLK_CTRL_DMA_CLK_RQT); + } DELAY(20); - /* Disable L1. */ + /* Disable L1-Active. */ iwn_prph_setbits(sc, IWN_APMG_PCI_STT, IWN_APMG_PCI_STT_L1A_DIS); iwn_nic_unlock(sc); @@ -5524,13 +5559,15 @@ iwn_apm_stop_master(struct iwn_softc *sc { int ntries; + /* Stop busmaster DMA activity. */ IWN_SETBITS(sc, IWN_RESET, IWN_RESET_STOP_MASTER); for (ntries = 0; ntries < 100; ntries++) { if (IWN_READ(sc, IWN_RESET) & IWN_RESET_MASTER_DISABLED) return; DELAY(10); } - aprint_error_dev(sc->sc_dev, "timeout waiting for master\n"); + aprint_error_dev(sc->sc_dev, + "timeout waiting for master\n"); } static void @@ -5538,6 +5575,7 @@ iwn_apm_stop(struct iwn_softc *sc) { iwn_apm_stop_master(sc); + /* Reset the entire device. */ IWN_SETBITS(sc, IWN_RESET, IWN_RESET_SW); DELAY(10); /* Clear "initialization complete" bit. */ @@ -5547,16 +5585,6 @@ iwn_apm_stop(struct iwn_softc *sc) static int iwn4965_nic_config(struct iwn_softc *sc) { - pcireg_t 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 @@ -5576,16 +5604,8 @@ iwn4965_nic_config(struct iwn_softc *sc) static int iwn5000_nic_config(struct iwn_softc *sc) { + uint32_t tmp; int error; - pcireg_t 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) < 3) { IWN_SETBITS(sc, IWN_HW_IF_CONFIG, @@ -5599,21 +5619,82 @@ iwn5000_nic_config(struct iwn_softc *sc) if ((error = iwn_nic_lock(sc)) != 0) return error; iwn_prph_setbits(sc, IWN_APMG_PS, IWN_APMG_PS_EARLY_PWROFF_DIS); + + if (sc->hw_type == IWN_HW_REV_TYPE_1000) { + /* + * Select first Switching Voltage Regulator (1.32V) to + * solve a stability issue related to noisy DC2DC line + * in the silicon of 1000 Series. + */ + tmp = iwn_prph_read(sc, IWN_APMG_DIGITAL_SVR); + tmp &= ~IWN_APMG_DIGITAL_SVR_VOLTAGE_MASK; + tmp |= IWN_APMG_DIGITAL_SVR_VOLTAGE_1_32; + iwn_prph_write(sc, IWN_APMG_DIGITAL_SVR, tmp); + } iwn_nic_unlock(sc); + + if (sc->sc_flags & IWN_FLAG_INTERNAL_PA) { + /* Use internal power amplifier only. */ + IWN_WRITE(sc, IWN_GP_DRIVER, IWN_GP_DRIVER_RADIO_2X2_IPA); + } + if (sc->hw_type == IWN_HW_REV_TYPE_6050 && sc->calib_ver >= 6) { + /* Indicate that ROM calibration version is >=6. */ + IWN_SETBITS(sc, IWN_GP_DRIVER, IWN_GP_DRIVER_CALIB_VER6); + } return 0; } +/* + * Take NIC ownership over Intel Active Management Technology (AMT). + */ +static int +iwn_hw_prepare(struct iwn_softc *sc) +{ + int ntries; + + /* Check if hardware is ready. */ + IWN_SETBITS(sc, IWN_HW_IF_CONFIG, IWN_HW_IF_CONFIG_NIC_READY); + for (ntries = 0; ntries < 5; ntries++) { + if (IWN_READ(sc, IWN_HW_IF_CONFIG) & + IWN_HW_IF_CONFIG_NIC_READY) + return 0; + DELAY(10); + } + + /* Hardware not ready, force into ready state. */ + IWN_SETBITS(sc, IWN_HW_IF_CONFIG, IWN_HW_IF_CONFIG_PREPARE); + for (ntries = 0; ntries < 15000; ntries++) { + if (!(IWN_READ(sc, IWN_HW_IF_CONFIG) & + IWN_HW_IF_CONFIG_PREPARE_DONE)) + break; + DELAY(10); + } + if (ntries == 15000) + return ETIMEDOUT; + + /* Hardware should be ready now. */ + IWN_SETBITS(sc, IWN_HW_IF_CONFIG, IWN_HW_IF_CONFIG_NIC_READY); + for (ntries = 0; ntries < 5; ntries++) { + if (IWN_READ(sc, IWN_HW_IF_CONFIG) & + IWN_HW_IF_CONFIG_NIC_READY) + return 0; + DELAY(10); + } + return ETIMEDOUT; +} + static int iwn_hw_init(struct iwn_softc *sc) { const struct iwn_hal *hal = sc->sc_hal; - int error, qid; + int error, chnl, qid; /* Clear pending interrupts. */ IWN_WRITE(sc, IWN_INT, 0xffffffff); - if ((error = hal->apm_init(sc)) != 0) { - aprint_error_dev(sc->sc_dev, "could not power ON adapter\n"); + if ((error = iwn_apm_init(sc)) != 0) { + aprint_error_dev(sc->sc_dev, + "could not power ON adapter\n"); return error; } @@ -5638,7 +5719,7 @@ iwn_hw_init(struct iwn_softc *sc) 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_ENA | IWN_FH_RX_CONFIG_IGN_RXF_EMPTY | /* HW bug workaround */ IWN_FH_RX_CONFIG_IRQ_DST_HOST | IWN_FH_RX_CONFIG_SINGLE_FRAME | @@ -5663,12 +5744,15 @@ iwn_hw_init(struct iwn_softc *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_nic_unlock(sc); + + /* Enable DMA channels. */ + for (chnl = 0; chnl < hal->ndmachnls; chnl++) { + IWN_WRITE(sc, IWN_FH_TX_CONFIG(chnl), IWN_FH_TX_CONFIG_DMA_ENA | IWN_FH_TX_CONFIG_DMA_CREDIT_ENA); } - iwn_nic_unlock(sc); /* Clear "radio off" and "commands blocked" bits. */ IWN_WRITE(sc, IWN_UCODE_GP1_CLR, IWN_UCODE_GP1_RFKILL); @@ -5679,20 +5763,21 @@ iwn_hw_init(struct iwn_softc *sc) /* Enable interrupt coalescing. */ IWN_WRITE(sc, IWN_INT_COALESCING, 512 / 8); /* Enable interrupts. */ - IWN_WRITE(sc, IWN_MASK, IWN_INT_MASK); + IWN_WRITE(sc, IWN_INT_MASK, sc->int_mask); /* _Really_ make sure "radio off" bit is cleared! */ IWN_WRITE(sc, IWN_UCODE_GP1_CLR, IWN_UCODE_GP1_RFKILL); IWN_WRITE(sc, IWN_UCODE_GP1_CLR, IWN_UCODE_GP1_RFKILL); if ((error = hal->load_firmware(sc)) != 0) { - aprint_error_dev(sc->sc_dev, "could not load firmware\n"); + aprint_error_dev(sc->sc_dev, + "could not load firmware\n"); return error; } /* Wait at most one second for firmware alive notification. */ if ((error = tsleep(sc, PCATCH, "iwninit", hz)) != 0) { aprint_error_dev(sc->sc_dev, - "timeout waiting for adapter to initialize %d\n" ,error); + "timeout waiting for adapter to initialize\n"); return error; } /* Do post-firmware initialization. */ @@ -5703,14 +5788,16 @@ static void iwn_hw_stop(struct iwn_softc *sc) { const struct iwn_hal *hal = sc->sc_hal; - int qid; + int chnl, qid, ntries; + uint32_t tmp; IWN_WRITE(sc, IWN_RESET, IWN_RESET_NEVO); /* Disable interrupts. */ - IWN_WRITE(sc, IWN_MASK, 0); + IWN_WRITE(sc, IWN_INT_MASK, 0); IWN_WRITE(sc, IWN_INT, 0xffffffff); IWN_WRITE(sc, IWN_FH_INT, 0xffffffff); + sc->sc_flags &= ~IWN_FLAG_USE_ICT; /* Make sure we no longer hold the NIC lock. */ iwn_nic_unlock(sc); @@ -5718,15 +5805,31 @@ iwn_hw_stop(struct iwn_softc *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 all DMA channels. */ + if (iwn_nic_lock(sc) == 0) { + for (chnl = 0; chnl < hal->ndmachnls; chnl++) { + IWN_WRITE(sc, IWN_FH_TX_CONFIG(chnl), 0); + for (ntries = 0; ntries < 200; ntries++) { + tmp = IWN_READ(sc, IWN_FH_TX_STATUS); + if ((tmp & IWN_FH_TX_STATUS_IDLE(chnl)) == + IWN_FH_TX_STATUS_IDLE(chnl)) + break; + DELAY(10); + } + } + iwn_nic_unlock(sc); + } /* Stop RX ring. */ iwn_reset_rx_ring(sc, &sc->rxq); + /* Reset all TX rings. */ + for (qid = 0; qid < hal->ntxqs; qid++) + iwn_reset_tx_ring(sc, &sc->txq[qid]); + if (iwn_nic_lock(sc) == 0) { - iwn_prph_write(sc, IWN_APMG_CLK_DIS, IWN_APMG_CLK_DMA_RQT); + iwn_prph_write(sc, IWN_APMG_CLK_DIS, + IWN_APMG_CLK_CTRL_DMA_CLK_RQT); iwn_nic_unlock(sc); } DELAY(5); @@ -5741,48 +5844,68 @@ iwn_init(struct ifnet *ifp) struct ieee80211com *ic = &sc->sc_ic; int error; + mutex_enter(&sc->sc_mtx); + if (sc->sc_flags & IWN_FLAG_HW_INITED) + goto out; + if ((error = iwn_hw_prepare(sc)) != 0) { + aprint_error_dev(sc->sc_dev, + "hardware not ready\n"); + goto fail; + } + /* Check that the radio is not disabled by hardware switch. */ if (!(IWN_READ(sc, IWN_GP_CNTRL) & IWN_GP_CNTRL_RFKILL)) { aprint_error_dev(sc->sc_dev, "radio is disabled by hardware switch\n"); - sc->sc_radio = false; error = EPERM; /* :-) */ goto fail; } - sc->sc_radio = true; /* Read firmware images from the filesystem. */ if ((error = iwn_read_firmware(sc)) != 0) { - aprint_error_dev(sc->sc_dev, "could not read firmware\n"); + aprint_error_dev(sc->sc_dev, + "could not read firmware\n"); goto fail; } + /* Initialize interrupt mask to default value. */ + sc->int_mask = IWN_INT_MASK_DEF; + sc->sc_flags &= ~IWN_FLAG_USE_ICT; + /* Initialize hardware and upload firmware. */ + KASSERT(sc->fw.data != NULL && sc->fw.size > 0); error = iwn_hw_init(sc); - free(sc->fw.data, M_DEVBUF); + firmware_free(sc->fw.data, sc->fw.size); + sc->fw.data = NULL; + sc->fw.size = 0; if (error != 0) { - aprint_error_dev(sc->sc_dev, "could not initialize hardware\n"); + aprint_error_dev(sc->sc_dev, + "could not initialize hardware\n"); goto fail; } /* Configure adapter now that it is ready. */ if ((error = iwn_config(sc)) != 0) { - aprint_error_dev(sc->sc_dev, "could not configure device\n"); + aprint_error_dev(sc->sc_dev, + "could not configure device\n"); goto fail; } ifp->if_flags &= ~IFF_OACTIVE; ifp->if_flags |= IFF_RUNNING; - if (ic->ic_opmode != IEEE80211_M_MONITOR) { - if (ic->ic_opmode != IEEE80211_ROAMING_MANUAL) - ieee80211_new_state(ic, IEEE80211_S_SCAN, -1); - } else + if (ic->ic_opmode != IEEE80211_M_MONITOR) + ieee80211_begin_scan(ic, 0); + else ieee80211_new_state(ic, IEEE80211_S_RUN, -1); + sc->sc_flags |= IWN_FLAG_HW_INITED; +out: + mutex_exit(&sc->sc_mtx); return 0; -fail: iwn_stop(ifp, 1); +fail: mutex_exit(&sc->sc_mtx); + iwn_stop(ifp, 1); return error; } @@ -5792,6 +5915,9 @@ iwn_stop(struct ifnet *ifp, int disable) struct iwn_softc *sc = ifp->if_softc; struct ieee80211com *ic = &sc->sc_ic; + if (!disable) + mutex_enter(&sc->sc_mtx); + sc->sc_flags &= ~IWN_FLAG_HW_INITED; ifp->if_timer = sc->sc_tx_timer = 0; ifp->if_flags &= ~(IFF_RUNNING | IFF_OACTIVE); @@ -5800,21 +5926,232 @@ iwn_stop(struct ifnet *ifp, int disable) /* Power OFF hardware. */ iwn_hw_stop(sc); -#if 0 +#ifndef SMALL_KERNEL /* Temperature sensor is no longer valid. */ - sc->sensor.value = 0; - sc->sensor.flags |= SENSOR_FINVALID; + sc->sc_sensor.value_cur = 0; + sc->sc_sensor.state = ENVSYS_SINVALID; #endif + if (!disable) + mutex_exit(&sc->sc_mtx); } -static bool -iwn_resume(device_t dv, const pmf_qual_t *qual) +/* + * XXX MCLGETI alternative + * + * With IWN_USE_RBUF defined it uses the rbuf cache for receive buffers + * as long as there are available free buffers then it uses MEXTMALLOC., + * Without IWN_USE_RBUF defined it uses MEXTMALLOC exclusively. + * The MCLGET4K code is used for testing an alternative mbuf cache. + */ + +static struct mbuf * +MCLGETIalt(struct iwn_softc *sc, int how, + struct ifnet *ifp __unused, u_int size) { -#if 0 - struct iwn_softc *sc = device_private(dv); + struct mbuf *m; +#ifdef IWN_USE_RBUF + struct iwn_rbuf *rbuf; +#endif + + MGETHDR(m, how, MT_DATA); + if (m == NULL) + return NULL; + +#ifdef IWN_USE_RBUF + if (sc->rxq.nb_free_entries > 0 && + (rbuf = iwn_alloc_rbuf(sc)) != NULL) { + /* Attach buffer to mbuf header. */ + MEXTADD(m, rbuf->vaddr, size, 0, iwn_free_rbuf, rbuf); + m->m_flags |= M_EXT_RW; + } + else { + MEXTMALLOC(m, size, how); + if ((m->m_flags & M_EXT) == 0) { + m_freem(m); + return NULL; + } + } - (void)iwn_reset(sc); +#else +#ifdef MCLGET4K + if (size == 4096) + MCLGET4K(m, how); + else + panic("size must be 4k"); +#else + MEXTMALLOC(m, size, how); +#endif + if ((m->m_flags & M_EXT) == 0) { + m_freem(m); + return NULL; + } #endif - return true; + return m; } + +#ifdef IWN_USE_RBUF +static struct iwn_rbuf * +iwn_alloc_rbuf(struct iwn_softc *sc) +{ + struct iwn_rbuf *rbuf; + mutex_enter(&sc->rxq.freelist_mtx); + + rbuf = SLIST_FIRST(&sc->rxq.freelist); + if (rbuf != NULL) { + SLIST_REMOVE_HEAD(&sc->rxq.freelist, next); + sc->rxq.nb_free_entries --; + } + mutex_exit(&sc->rxq.freelist_mtx); + return rbuf; +} + +/* + * This is called automatically by the network stack when the mbuf to which + * our RX buffer is attached is freed. + */ +static void +iwn_free_rbuf(struct mbuf* m, void *buf, size_t size, void *arg) +{ + struct iwn_rbuf *rbuf = arg; + struct iwn_softc *sc = rbuf->sc; + + /* Put the RX buffer back in the free list. */ + mutex_enter(&sc->rxq.freelist_mtx); + SLIST_INSERT_HEAD(&sc->rxq.freelist, rbuf, next); + mutex_exit(&sc->rxq.freelist_mtx); + + sc->rxq.nb_free_entries ++; + if (__predict_true(m != NULL)) + pool_cache_put(mb_cache, m); +} + +static int +iwn_alloc_rpool(struct iwn_softc *sc) +{ + struct iwn_rx_ring *ring = &sc->rxq; + struct iwn_rbuf *rbuf; + int i, error; + + mutex_init(&ring->freelist_mtx, MUTEX_DEFAULT, IPL_NET); + + /* 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); + if (error != 0) { + aprint_error_dev(sc->sc_dev, + "could not allocate RX buffers DMA memory\n"); + return error; + } + /* ...and split it into chunks of IWN_RBUF_SIZE bytes. */ + SLIST_INIT(&ring->freelist); + for (i = 0; i < IWN_RBUF_COUNT; i++) { + rbuf = &ring->rbuf[i]; + + rbuf->sc = sc; /* Backpointer for callbacks. */ + rbuf->vaddr = (void *)((vaddr_t)ring->buf_dma.vaddr + i * IWN_RBUF_SIZE); + rbuf->paddr = ring->buf_dma.paddr + i * IWN_RBUF_SIZE; + + SLIST_INSERT_HEAD(&ring->freelist, rbuf, next); + } + ring->nb_free_entries = IWN_RBUF_COUNT; + return 0; +} + +static void +iwn_free_rpool(struct iwn_softc *sc) +{ + iwn_dma_contig_free(&sc->rxq.buf_dma); +} +#endif + +/* + * XXX code from OpenBSD src/sys/net80211/ieee80211_output.c + * Copyright (c) 2001 Atsushi Onoe + * Copyright (c) 2002, 2003 Sam Leffler, Errno Consulting + * Copyright (c) 2007-2009 Damien Bergamini + * All rights reserved. + */ + +/* + * Add an SSID element to a frame (see 7.3.2.1). + */ +static u_int8_t * +ieee80211_add_ssid(u_int8_t *frm, const u_int8_t *ssid, u_int len) +{ + *frm++ = IEEE80211_ELEMID_SSID; + *frm++ = len; + memcpy(frm, ssid, len); + return frm + len; +} + +/* + * Add a supported rates element to a frame (see 7.3.2.2). + */ +static u_int8_t * +ieee80211_add_rates(u_int8_t *frm, const struct ieee80211_rateset *rs) +{ + int nrates; + + *frm++ = IEEE80211_ELEMID_RATES; + nrates = min(rs->rs_nrates, IEEE80211_RATE_SIZE); + *frm++ = nrates; + memcpy(frm, rs->rs_rates, nrates); + return frm + nrates; +} + +/* + * Add an extended supported rates element to a frame (see 7.3.2.14). + */ +static u_int8_t * +ieee80211_add_xrates(u_int8_t *frm, const struct ieee80211_rateset *rs) +{ + int nrates; + + KASSERT(rs->rs_nrates > IEEE80211_RATE_SIZE); + + *frm++ = IEEE80211_ELEMID_XRATES; + nrates = rs->rs_nrates - IEEE80211_RATE_SIZE; + *frm++ = nrates; + memcpy(frm, rs->rs_rates + IEEE80211_RATE_SIZE, nrates); + return frm + nrates; +} + +/* + * 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 +iwn_fix_channel(struct ieee80211com *ic, struct mbuf *m) +{ + struct ieee80211_frame *wh; + uint8_t subtype; + uint8_t *frm, *efrm; + + wh = mtod(m, struct ieee80211_frame *); + + if ((wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) != IEEE80211_FC0_TYPE_MGT) + return; + + subtype = wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK; + + if (subtype != IEEE80211_FC0_SUBTYPE_BEACON && + subtype != IEEE80211_FC0_SUBTYPE_PROBE_RESP) + return; + + frm = (uint8_t *)(wh + 1); + efrm = mtod(m, uint8_t *) + m->m_len; + + frm += 12; /* skip tstamp, bintval and capinfo fields */ + while (frm < efrm) { + if (*frm == IEEE80211_ELEMID_DSPARMS) +#if IEEE80211_CHAN_MAX < 255 + if (frm[2] <= IEEE80211_CHAN_MAX) +#endif + ic->ic_curchan = &ic->ic_channels[frm[2]]; + + frm += frm[1] + 2; + } +} +