2008-11-10 21:05:12 +03:00
|
|
|
/* $NetBSD: if_iwi.c,v 1.77 2008/11/10 18:05:12 joerg Exp $ */
|
2005-01-11 21:24:24 +03:00
|
|
|
|
|
|
|
/*-
|
|
|
|
* Copyright (c) 2004, 2005
|
|
|
|
* Damien Bergamini <damien.bergamini@free.fr>. All rights reserved.
|
|
|
|
*
|
|
|
|
* Redistribution and use in source and binary forms, with or without
|
|
|
|
* modification, are permitted provided that the following conditions
|
|
|
|
* are met:
|
|
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
|
|
* notice unmodified, this list of conditions, and the following
|
|
|
|
* disclaimer.
|
|
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
|
|
* documentation and/or other materials provided with the distribution.
|
|
|
|
*
|
|
|
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
|
|
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
|
|
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
|
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
|
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
|
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
|
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
|
|
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
|
|
* SUCH DAMAGE.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <sys/cdefs.h>
|
2008-11-10 21:05:12 +03:00
|
|
|
__KERNEL_RCSID(0, "$NetBSD: if_iwi.c,v 1.77 2008/11/10 18:05:12 joerg Exp $");
|
2005-01-11 21:24:24 +03:00
|
|
|
|
|
|
|
/*-
|
2005-08-01 19:14:54 +04:00
|
|
|
* Intel(R) PRO/Wireless 2200BG/2225BG/2915ABG driver
|
2005-01-11 21:24:24 +03:00
|
|
|
* http://www.intel.com/network/connectivity/products/wireless/prowireless_mobile.htm
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "bpfilter.h"
|
|
|
|
|
|
|
|
#include <sys/param.h>
|
|
|
|
#include <sys/sockio.h>
|
|
|
|
#include <sys/sysctl.h>
|
|
|
|
#include <sys/mbuf.h>
|
|
|
|
#include <sys/kernel.h>
|
|
|
|
#include <sys/socket.h>
|
|
|
|
#include <sys/systm.h>
|
|
|
|
#include <sys/malloc.h>
|
|
|
|
#include <sys/conf.h>
|
2006-05-15 01:42:26 +04:00
|
|
|
#include <sys/kauth.h>
|
2005-01-11 21:24:24 +03:00
|
|
|
|
2007-10-19 15:59:34 +04:00
|
|
|
#include <sys/bus.h>
|
2005-01-11 21:24:24 +03:00
|
|
|
#include <machine/endian.h>
|
2007-10-19 15:59:34 +04:00
|
|
|
#include <sys/intr.h>
|
2005-01-11 21:24:24 +03:00
|
|
|
|
2006-08-09 15:35:59 +04:00
|
|
|
#include <dev/firmload.h>
|
|
|
|
|
2005-01-11 21:24:24 +03:00
|
|
|
#include <dev/pci/pcireg.h>
|
|
|
|
#include <dev/pci/pcivar.h>
|
|
|
|
#include <dev/pci/pcidevs.h>
|
|
|
|
|
|
|
|
#if NBPFILTER > 0
|
|
|
|
#include <net/bpf.h>
|
|
|
|
#endif
|
|
|
|
#include <net/if.h>
|
|
|
|
#include <net/if_arp.h>
|
|
|
|
#include <net/if_dl.h>
|
|
|
|
#include <net/if_ether.h>
|
|
|
|
#include <net/if_media.h>
|
|
|
|
#include <net/if_types.h>
|
|
|
|
|
|
|
|
#include <net80211/ieee80211_var.h>
|
|
|
|
#include <net80211/ieee80211_radiotap.h>
|
|
|
|
|
|
|
|
#include <netinet/in.h>
|
|
|
|
#include <netinet/in_systm.h>
|
|
|
|
#include <netinet/in_var.h>
|
|
|
|
#include <netinet/ip.h>
|
|
|
|
|
|
|
|
#include <crypto/arc4/arc4.h>
|
|
|
|
|
|
|
|
#include <dev/pci/if_iwireg.h>
|
|
|
|
#include <dev/pci/if_iwivar.h>
|
|
|
|
|
2005-09-04 10:58:20 +04:00
|
|
|
#ifdef IWI_DEBUG
|
|
|
|
#define DPRINTF(x) if (iwi_debug > 0) printf x
|
|
|
|
#define DPRINTFN(n, x) if (iwi_debug >= (n)) printf x
|
|
|
|
int iwi_debug = 4;
|
|
|
|
#else
|
|
|
|
#define DPRINTF(x)
|
|
|
|
#define DPRINTFN(n, x)
|
|
|
|
#endif
|
2005-01-11 21:24:24 +03:00
|
|
|
|
2008-10-30 03:27:31 +03:00
|
|
|
/* Permit loading the Intel firmware */
|
|
|
|
static int iwi_accept_eula;
|
|
|
|
|
2007-10-22 19:28:48 +04:00
|
|
|
static int iwi_match(device_t, struct cfdata *, void *);
|
|
|
|
static void iwi_attach(device_t, device_t, void *);
|
|
|
|
static int iwi_detach(device_t, int);
|
2005-08-19 12:50:06 +04:00
|
|
|
|
2005-09-25 10:49:54 +04:00
|
|
|
static int iwi_alloc_cmd_ring(struct iwi_softc *, struct iwi_cmd_ring *,
|
2005-08-19 12:50:06 +04:00
|
|
|
int);
|
2005-09-25 10:49:54 +04:00
|
|
|
static void iwi_reset_cmd_ring(struct iwi_softc *, struct iwi_cmd_ring *);
|
|
|
|
static void iwi_free_cmd_ring(struct iwi_softc *, struct iwi_cmd_ring *);
|
|
|
|
static int iwi_alloc_tx_ring(struct iwi_softc *, struct iwi_tx_ring *,
|
2006-12-20 19:30:20 +03:00
|
|
|
int, bus_addr_t, bus_size_t);
|
2005-09-25 10:49:54 +04:00
|
|
|
static void iwi_reset_tx_ring(struct iwi_softc *, struct iwi_tx_ring *);
|
|
|
|
static void iwi_free_tx_ring(struct iwi_softc *, struct iwi_tx_ring *);
|
2005-10-20 00:18:00 +04:00
|
|
|
static struct mbuf *
|
|
|
|
iwi_alloc_rx_buf(struct iwi_softc *sc);
|
2005-09-25 10:49:54 +04:00
|
|
|
static int iwi_alloc_rx_ring(struct iwi_softc *, struct iwi_rx_ring *,
|
2005-08-19 12:50:06 +04:00
|
|
|
int);
|
2005-09-25 10:49:54 +04:00
|
|
|
static void iwi_reset_rx_ring(struct iwi_softc *, struct iwi_rx_ring *);
|
|
|
|
static void iwi_free_rx_ring(struct iwi_softc *, struct iwi_rx_ring *);
|
|
|
|
|
2005-11-18 19:42:22 +03:00
|
|
|
static struct ieee80211_node *iwi_node_alloc(struct ieee80211_node_table *);
|
|
|
|
static void iwi_node_free(struct ieee80211_node *);
|
|
|
|
|
2006-12-20 19:30:20 +03:00
|
|
|
static int iwi_cvtrate(int);
|
2005-09-25 10:49:54 +04:00
|
|
|
static int iwi_media_change(struct ifnet *);
|
|
|
|
static void iwi_media_status(struct ifnet *, struct ifmediareq *);
|
2005-11-18 19:42:22 +03:00
|
|
|
static int iwi_wme_update(struct ieee80211com *);
|
2005-09-25 10:49:54 +04:00
|
|
|
static uint16_t iwi_read_prom_word(struct iwi_softc *, uint8_t);
|
|
|
|
static int iwi_newstate(struct ieee80211com *, enum ieee80211_state, int);
|
|
|
|
static void iwi_fix_channel(struct ieee80211com *, struct mbuf *);
|
|
|
|
static void iwi_frame_intr(struct iwi_softc *, struct iwi_rx_data *, int,
|
2005-01-11 21:24:24 +03:00
|
|
|
struct iwi_frame *);
|
2005-09-25 10:49:54 +04:00
|
|
|
static void iwi_notification_intr(struct iwi_softc *, struct iwi_notif *);
|
2005-11-26 10:42:10 +03:00
|
|
|
static void iwi_cmd_intr(struct iwi_softc *);
|
2005-09-25 10:49:54 +04:00
|
|
|
static void iwi_rx_intr(struct iwi_softc *);
|
2005-11-18 19:42:22 +03:00
|
|
|
static void iwi_tx_intr(struct iwi_softc *, struct iwi_tx_ring *);
|
2005-09-25 10:49:54 +04:00
|
|
|
static int iwi_intr(void *);
|
|
|
|
static int iwi_cmd(struct iwi_softc *, uint8_t, void *, uint8_t, int);
|
2005-11-18 19:42:22 +03:00
|
|
|
static void iwi_write_ibssnode(struct iwi_softc *, const struct iwi_node *);
|
|
|
|
static int iwi_tx_start(struct ifnet *, struct mbuf *, struct ieee80211_node *,
|
|
|
|
int);
|
2005-09-25 10:49:54 +04:00
|
|
|
static void iwi_start(struct ifnet *);
|
|
|
|
static void iwi_watchdog(struct ifnet *);
|
2005-11-18 19:42:22 +03:00
|
|
|
|
|
|
|
static int iwi_alloc_unr(struct iwi_softc *);
|
|
|
|
static void iwi_free_unr(struct iwi_softc *, int);
|
|
|
|
|
2005-09-25 10:49:54 +04:00
|
|
|
static int iwi_get_table0(struct iwi_softc *, uint32_t *);
|
2005-11-18 19:42:22 +03:00
|
|
|
|
2007-03-04 08:59:00 +03:00
|
|
|
static int iwi_ioctl(struct ifnet *, u_long, void *);
|
2005-09-25 10:49:54 +04:00
|
|
|
static void iwi_stop_master(struct iwi_softc *);
|
|
|
|
static int iwi_reset(struct iwi_softc *);
|
|
|
|
static int iwi_load_ucode(struct iwi_softc *, void *, int);
|
|
|
|
static int iwi_load_firmware(struct iwi_softc *, void *, int);
|
2006-08-09 15:35:59 +04:00
|
|
|
static int iwi_cache_firmware(struct iwi_softc *);
|
2005-09-25 10:49:54 +04:00
|
|
|
static void iwi_free_firmware(struct iwi_softc *);
|
|
|
|
static int iwi_config(struct iwi_softc *);
|
|
|
|
static int iwi_set_chan(struct iwi_softc *, struct ieee80211_channel *);
|
|
|
|
static int iwi_scan(struct iwi_softc *);
|
|
|
|
static int iwi_auth_and_assoc(struct iwi_softc *);
|
|
|
|
static int iwi_init(struct ifnet *);
|
|
|
|
static void iwi_stop(struct ifnet *, int);
|
2006-08-19 10:32:52 +04:00
|
|
|
static int iwi_getrfkill(struct iwi_softc *);
|
2005-11-29 16:57:00 +03:00
|
|
|
static void iwi_led_set(struct iwi_softc *, uint32_t, int);
|
2006-08-19 10:32:52 +04:00
|
|
|
static void iwi_sysctlattach(struct iwi_softc *);
|
2005-01-11 21:24:24 +03:00
|
|
|
|
2005-09-04 10:58:20 +04:00
|
|
|
/*
|
|
|
|
* Supported rates for 802.11a/b/g modes (in 500Kbps unit).
|
|
|
|
*/
|
|
|
|
static const struct ieee80211_rateset iwi_rateset_11a =
|
|
|
|
{ 8, { 12, 18, 24, 36, 48, 72, 96, 108 } };
|
|
|
|
|
|
|
|
static const struct ieee80211_rateset iwi_rateset_11b =
|
|
|
|
{ 4, { 2, 4, 11, 22 } };
|
|
|
|
|
|
|
|
static const struct ieee80211_rateset iwi_rateset_11g =
|
|
|
|
{ 12, { 2, 4, 11, 22, 12, 18, 24, 36, 48, 72, 96, 108 } };
|
|
|
|
|
2005-12-24 23:27:29 +03:00
|
|
|
static inline uint8_t
|
2005-09-17 16:40:27 +04:00
|
|
|
MEM_READ_1(struct iwi_softc *sc, uint32_t addr)
|
2005-01-11 21:24:24 +03:00
|
|
|
{
|
|
|
|
CSR_WRITE_4(sc, IWI_CSR_INDIRECT_ADDR, addr);
|
|
|
|
return CSR_READ_1(sc, IWI_CSR_INDIRECT_DATA);
|
|
|
|
}
|
|
|
|
|
2005-12-24 23:27:29 +03:00
|
|
|
static inline uint32_t
|
2005-09-17 16:40:27 +04:00
|
|
|
MEM_READ_4(struct iwi_softc *sc, uint32_t addr)
|
2005-01-11 21:24:24 +03:00
|
|
|
{
|
|
|
|
CSR_WRITE_4(sc, IWI_CSR_INDIRECT_ADDR, addr);
|
|
|
|
return CSR_READ_4(sc, IWI_CSR_INDIRECT_DATA);
|
|
|
|
}
|
|
|
|
|
2007-10-22 19:28:48 +04:00
|
|
|
CFATTACH_DECL_NEW(iwi, sizeof (struct iwi_softc), iwi_match, iwi_attach,
|
2005-01-11 21:24:24 +03:00
|
|
|
iwi_detach, NULL);
|
|
|
|
|
|
|
|
static int
|
2007-10-22 19:28:48 +04:00
|
|
|
iwi_match(device_t parent, struct cfdata *match, void *aux)
|
2005-01-11 21:24:24 +03:00
|
|
|
{
|
|
|
|
struct pci_attach_args *pa = aux;
|
2005-02-27 03:26:58 +03:00
|
|
|
|
2005-01-11 21:24:24 +03:00
|
|
|
if (PCI_VENDOR(pa->pa_id) != PCI_VENDOR_INTEL)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
if (PCI_PRODUCT(pa->pa_id) == PCI_PRODUCT_INTEL_PRO_WL_2200BG ||
|
2005-08-01 19:14:54 +04:00
|
|
|
PCI_PRODUCT(pa->pa_id) == PCI_PRODUCT_INTEL_PRO_WL_2225BG ||
|
|
|
|
PCI_PRODUCT(pa->pa_id) == PCI_PRODUCT_INTEL_PRO_WL_2915ABG_1 ||
|
|
|
|
PCI_PRODUCT(pa->pa_id) == PCI_PRODUCT_INTEL_PRO_WL_2915ABG_2)
|
2005-01-11 21:24:24 +03:00
|
|
|
return 1;
|
2005-02-27 03:26:58 +03:00
|
|
|
|
2005-01-11 21:24:24 +03:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Base Address Register */
|
|
|
|
#define IWI_PCI_BAR0 0x10
|
|
|
|
|
|
|
|
static void
|
2007-10-22 19:28:48 +04:00
|
|
|
iwi_attach(device_t parent, device_t self, void *aux)
|
2005-01-11 21:24:24 +03:00
|
|
|
{
|
2007-10-22 19:28:48 +04:00
|
|
|
struct iwi_softc *sc = device_private(self);
|
2005-01-11 21:24:24 +03:00
|
|
|
struct ieee80211com *ic = &sc->sc_ic;
|
2005-06-22 10:14:51 +04:00
|
|
|
struct ifnet *ifp = &sc->sc_if;
|
2005-01-11 21:24:24 +03:00
|
|
|
struct pci_attach_args *pa = aux;
|
|
|
|
const char *intrstr;
|
|
|
|
char devinfo[256];
|
|
|
|
bus_space_tag_t memt;
|
|
|
|
bus_space_handle_t memh;
|
|
|
|
pci_intr_handle_t ih;
|
|
|
|
pcireg_t data;
|
2005-09-17 16:40:27 +04:00
|
|
|
uint16_t val;
|
2005-01-11 21:24:24 +03:00
|
|
|
int error, revision, i;
|
|
|
|
|
2007-10-22 19:28:48 +04:00
|
|
|
sc->sc_dev = self;
|
2005-01-11 21:24:24 +03:00
|
|
|
sc->sc_pct = pa->pa_pc;
|
|
|
|
sc->sc_pcitag = pa->pa_tag;
|
|
|
|
|
|
|
|
pci_devinfo(pa->pa_id, pa->pa_class, 0, devinfo, sizeof devinfo);
|
|
|
|
revision = PCI_REVISION(pa->pa_class);
|
|
|
|
aprint_normal(": %s (rev. 0x%02x)\n", devinfo, revision);
|
|
|
|
|
2005-11-18 19:42:22 +03:00
|
|
|
/* clear unit numbers allocated to IBSS */
|
|
|
|
sc->sc_unr = 0;
|
|
|
|
|
2006-06-18 03:34:26 +04:00
|
|
|
/* power up chip */
|
2008-03-21 10:47:43 +03:00
|
|
|
if ((error = pci_activate(pa->pa_pc, pa->pa_tag, self,
|
2006-06-18 03:34:26 +04:00
|
|
|
NULL)) && error != EOPNOTSUPP) {
|
2007-10-22 19:28:48 +04:00
|
|
|
aprint_error_dev(self, "cannot activate %d\n", error);
|
2006-06-18 03:34:26 +04:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2005-01-11 21:24:24 +03:00
|
|
|
/* enable bus-mastering */
|
|
|
|
data = pci_conf_read(sc->sc_pct, sc->sc_pcitag, PCI_COMMAND_STATUS_REG);
|
|
|
|
data |= PCI_COMMAND_MASTER_ENABLE;
|
|
|
|
pci_conf_write(sc->sc_pct, sc->sc_pcitag, PCI_COMMAND_STATUS_REG, data);
|
|
|
|
|
|
|
|
/* map the register window */
|
|
|
|
error = pci_mapreg_map(pa, IWI_PCI_BAR0, PCI_MAPREG_TYPE_MEM |
|
2006-12-20 19:30:20 +03:00
|
|
|
PCI_MAPREG_MEM_TYPE_32BIT, 0, &memt, &memh, NULL, &sc->sc_sz);
|
2005-01-11 21:24:24 +03:00
|
|
|
if (error != 0) {
|
2007-10-22 19:28:48 +04:00
|
|
|
aprint_error_dev(self, "could not map memory space\n");
|
2005-01-11 21:24:24 +03:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
sc->sc_st = memt;
|
|
|
|
sc->sc_sh = memh;
|
|
|
|
sc->sc_dmat = pa->pa_dmat;
|
|
|
|
|
|
|
|
/* disable interrupts */
|
|
|
|
CSR_WRITE_4(sc, IWI_CSR_INTR_MASK, 0);
|
|
|
|
|
|
|
|
if (pci_intr_map(pa, &ih) != 0) {
|
2007-10-22 19:28:48 +04:00
|
|
|
aprint_error_dev(self, "could not map interrupt\n");
|
2005-01-11 21:24:24 +03:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
intrstr = pci_intr_string(sc->sc_pct, ih);
|
|
|
|
sc->sc_ih = pci_intr_establish(sc->sc_pct, ih, IPL_NET, iwi_intr, sc);
|
|
|
|
if (sc->sc_ih == NULL) {
|
2007-10-22 19:28:48 +04:00
|
|
|
aprint_error_dev(self, "could not establish interrupt");
|
2005-01-11 21:24:24 +03:00
|
|
|
if (intrstr != NULL)
|
|
|
|
aprint_error(" at %s", intrstr);
|
|
|
|
aprint_error("\n");
|
|
|
|
return;
|
|
|
|
}
|
2007-10-22 19:28:48 +04:00
|
|
|
aprint_normal_dev(self, "interrupting at %s\n", intrstr);
|
2005-01-11 21:24:24 +03:00
|
|
|
|
|
|
|
if (iwi_reset(sc) != 0) {
|
2007-10-22 19:28:48 +04:00
|
|
|
aprint_error_dev(self, "could not reset adapter\n");
|
2005-01-11 21:24:24 +03:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2005-08-19 12:50:06 +04:00
|
|
|
/*
|
|
|
|
* Allocate rings.
|
|
|
|
*/
|
|
|
|
if (iwi_alloc_cmd_ring(sc, &sc->cmdq, IWI_CMD_RING_COUNT) != 0) {
|
2007-10-22 19:28:48 +04:00
|
|
|
aprint_error_dev(self, "could not allocate command ring\n");
|
2005-08-19 12:50:06 +04:00
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
2005-11-18 19:42:22 +03:00
|
|
|
error = iwi_alloc_tx_ring(sc, &sc->txq[0], IWI_TX_RING_COUNT,
|
|
|
|
IWI_CSR_TX1_RIDX, IWI_CSR_TX1_WIDX);
|
|
|
|
if (error != 0) {
|
2007-10-22 19:28:48 +04:00
|
|
|
aprint_error_dev(self, "could not allocate Tx ring 1\n");
|
2005-11-18 19:42:22 +03:00
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
|
|
|
error = iwi_alloc_tx_ring(sc, &sc->txq[1], IWI_TX_RING_COUNT,
|
|
|
|
IWI_CSR_TX2_RIDX, IWI_CSR_TX2_WIDX);
|
|
|
|
if (error != 0) {
|
2007-10-22 19:28:48 +04:00
|
|
|
aprint_error_dev(self, "could not allocate Tx ring 2\n");
|
2005-11-18 19:42:22 +03:00
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
|
|
|
error = iwi_alloc_tx_ring(sc, &sc->txq[2], IWI_TX_RING_COUNT,
|
|
|
|
IWI_CSR_TX3_RIDX, IWI_CSR_TX3_WIDX);
|
|
|
|
if (error != 0) {
|
2007-10-22 19:28:48 +04:00
|
|
|
aprint_error_dev(self, "could not allocate Tx ring 3\n");
|
2005-11-18 19:42:22 +03:00
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
|
|
|
error = iwi_alloc_tx_ring(sc, &sc->txq[3], IWI_TX_RING_COUNT,
|
|
|
|
IWI_CSR_TX4_RIDX, IWI_CSR_TX4_WIDX);
|
|
|
|
if (error != 0) {
|
2007-10-22 19:28:48 +04:00
|
|
|
aprint_error_dev(self, "could not allocate Tx ring 4\n");
|
2005-08-19 12:50:06 +04:00
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (iwi_alloc_rx_ring(sc, &sc->rxq, IWI_RX_RING_COUNT) != 0) {
|
2007-10-22 19:28:48 +04:00
|
|
|
aprint_error_dev(self, "could not allocate Rx ring\n");
|
2005-08-19 12:50:06 +04:00
|
|
|
goto fail;
|
2005-01-11 21:24:24 +03:00
|
|
|
}
|
|
|
|
|
2005-06-22 10:14:51 +04:00
|
|
|
ic->ic_ifp = ifp;
|
2005-11-18 19:42:22 +03:00
|
|
|
ic->ic_wme.wme_update = iwi_wme_update;
|
2005-07-31 01:15:51 +04:00
|
|
|
ic->ic_phytype = IEEE80211_T_OFDM; /* not only, but not used */
|
|
|
|
ic->ic_opmode = IEEE80211_M_STA; /* default to BSS mode */
|
2005-08-01 19:14:54 +04:00
|
|
|
ic->ic_state = IEEE80211_S_INIT;
|
2005-01-11 21:24:24 +03:00
|
|
|
|
2008-11-10 21:05:12 +03:00
|
|
|
sc->sc_fwname = "ipw2200-bss.fw";
|
2006-08-09 15:35:59 +04:00
|
|
|
|
2005-01-11 21:24:24 +03:00
|
|
|
/* set device capabilities */
|
2005-11-18 19:42:22 +03:00
|
|
|
ic->ic_caps =
|
|
|
|
IEEE80211_C_IBSS | /* IBSS mode supported */
|
|
|
|
IEEE80211_C_MONITOR | /* monitor mode supported */
|
|
|
|
IEEE80211_C_TXPMGT | /* tx power management */
|
|
|
|
IEEE80211_C_SHPREAMBLE | /* short preamble supported */
|
2006-12-20 19:30:20 +03:00
|
|
|
IEEE80211_C_SHSLOT | /* short slot time supported */
|
2005-11-18 19:42:22 +03:00
|
|
|
IEEE80211_C_WPA | /* 802.11i */
|
|
|
|
IEEE80211_C_WME; /* 802.11e */
|
2005-01-11 21:24:24 +03:00
|
|
|
|
|
|
|
/* read MAC address from EEPROM */
|
|
|
|
val = iwi_read_prom_word(sc, IWI_EEPROM_MAC + 0);
|
2005-10-29 12:44:28 +04:00
|
|
|
ic->ic_myaddr[0] = val & 0xff;
|
|
|
|
ic->ic_myaddr[1] = val >> 8;
|
2005-01-11 21:24:24 +03:00
|
|
|
val = iwi_read_prom_word(sc, IWI_EEPROM_MAC + 1);
|
2005-10-29 12:44:28 +04:00
|
|
|
ic->ic_myaddr[2] = val & 0xff;
|
|
|
|
ic->ic_myaddr[3] = val >> 8;
|
2005-01-11 21:24:24 +03:00
|
|
|
val = iwi_read_prom_word(sc, IWI_EEPROM_MAC + 2);
|
2005-10-29 12:44:28 +04:00
|
|
|
ic->ic_myaddr[4] = val & 0xff;
|
|
|
|
ic->ic_myaddr[5] = val >> 8;
|
2005-01-11 21:24:24 +03:00
|
|
|
|
2007-10-22 19:28:48 +04:00
|
|
|
aprint_verbose_dev(self, "802.11 address %s\n",
|
2005-01-11 21:24:24 +03:00
|
|
|
ether_sprintf(ic->ic_myaddr));
|
|
|
|
|
2005-11-29 16:57:00 +03:00
|
|
|
/* read the NIC type from EEPROM */
|
2006-05-28 17:12:42 +04:00
|
|
|
val = iwi_read_prom_word(sc, IWI_EEPROM_NIC_TYPE);
|
2005-11-29 16:57:00 +03:00
|
|
|
sc->nictype = val & 0xff;
|
|
|
|
|
2007-10-22 19:28:48 +04:00
|
|
|
DPRINTF(("%s: NIC type %d\n", device_xname(self), sc->nictype));
|
2005-08-01 19:14:54 +04:00
|
|
|
|
2005-08-27 11:26:47 +04:00
|
|
|
if (PCI_PRODUCT(pa->pa_id) == PCI_PRODUCT_INTEL_PRO_WL_2915ABG_1 ||
|
|
|
|
PCI_PRODUCT(pa->pa_id) == PCI_PRODUCT_INTEL_PRO_WL_2915ABG_2) {
|
2005-08-01 19:14:54 +04:00
|
|
|
/* set supported .11a rates (2915ABG only) */
|
2005-01-11 21:24:24 +03:00
|
|
|
ic->ic_sup_rates[IEEE80211_MODE_11A] = iwi_rateset_11a;
|
|
|
|
|
|
|
|
/* set supported .11a channels */
|
|
|
|
for (i = 36; i <= 64; i += 4) {
|
|
|
|
ic->ic_channels[i].ic_freq =
|
|
|
|
ieee80211_ieee2mhz(i, IEEE80211_CHAN_5GHZ);
|
|
|
|
ic->ic_channels[i].ic_flags = IEEE80211_CHAN_A;
|
|
|
|
}
|
2005-07-31 01:15:51 +04:00
|
|
|
for (i = 149; i <= 165; i += 4) {
|
2005-01-11 21:24:24 +03:00
|
|
|
ic->ic_channels[i].ic_freq =
|
|
|
|
ieee80211_ieee2mhz(i, IEEE80211_CHAN_5GHZ);
|
|
|
|
ic->ic_channels[i].ic_flags = IEEE80211_CHAN_A;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* set supported .11b and .11g rates */
|
|
|
|
ic->ic_sup_rates[IEEE80211_MODE_11B] = iwi_rateset_11b;
|
|
|
|
ic->ic_sup_rates[IEEE80211_MODE_11G] = iwi_rateset_11g;
|
|
|
|
|
|
|
|
/* set supported .11b and .11g channels (1 through 14) */
|
|
|
|
for (i = 1; i <= 14; i++) {
|
|
|
|
ic->ic_channels[i].ic_freq =
|
|
|
|
ieee80211_ieee2mhz(i, IEEE80211_CHAN_2GHZ);
|
|
|
|
ic->ic_channels[i].ic_flags =
|
|
|
|
IEEE80211_CHAN_CCK | IEEE80211_CHAN_OFDM |
|
|
|
|
IEEE80211_CHAN_DYN | IEEE80211_CHAN_2GHZ;
|
|
|
|
}
|
|
|
|
|
|
|
|
ifp->if_softc = sc;
|
|
|
|
ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
|
|
|
|
ifp->if_init = iwi_init;
|
|
|
|
ifp->if_stop = iwi_stop;
|
|
|
|
ifp->if_ioctl = iwi_ioctl;
|
|
|
|
ifp->if_start = iwi_start;
|
|
|
|
ifp->if_watchdog = iwi_watchdog;
|
|
|
|
IFQ_SET_READY(&ifp->if_snd);
|
2007-10-22 19:28:48 +04:00
|
|
|
memcpy(ifp->if_xname, device_xname(self), IFNAMSIZ);
|
2005-01-11 21:24:24 +03:00
|
|
|
|
|
|
|
if_attach(ifp);
|
2005-06-22 10:14:51 +04:00
|
|
|
ieee80211_ifattach(ic);
|
2005-11-18 19:42:22 +03:00
|
|
|
/* override default methods */
|
|
|
|
ic->ic_node_alloc = iwi_node_alloc;
|
|
|
|
sc->sc_node_free = ic->ic_node_free;
|
|
|
|
ic->ic_node_free = iwi_node_free;
|
2005-01-11 21:24:24 +03:00
|
|
|
/* override state transition machine */
|
|
|
|
sc->sc_newstate = ic->ic_newstate;
|
|
|
|
ic->ic_newstate = iwi_newstate;
|
2005-06-22 10:14:51 +04:00
|
|
|
ieee80211_media_init(ic, iwi_media_change, iwi_media_status);
|
2005-01-11 21:24:24 +03:00
|
|
|
|
|
|
|
#if NBPFILTER > 0
|
|
|
|
bpfattach2(ifp, DLT_IEEE802_11_RADIO,
|
|
|
|
sizeof (struct ieee80211_frame) + 64, &sc->sc_drvbpf);
|
|
|
|
|
|
|
|
sc->sc_rxtap_len = sizeof sc->sc_rxtapu;
|
|
|
|
sc->sc_rxtap.wr_ihdr.it_len = htole16(sc->sc_rxtap_len);
|
|
|
|
sc->sc_rxtap.wr_ihdr.it_present = htole32(IWI_RX_RADIOTAP_PRESENT);
|
|
|
|
|
|
|
|
sc->sc_txtap_len = sizeof sc->sc_txtapu;
|
|
|
|
sc->sc_txtap.wt_ihdr.it_len = htole16(sc->sc_txtap_len);
|
|
|
|
sc->sc_txtap.wt_ihdr.it_present = htole32(IWI_TX_RADIOTAP_PRESENT);
|
|
|
|
#endif
|
2005-08-19 18:26:38 +04:00
|
|
|
|
2006-08-19 10:32:52 +04:00
|
|
|
iwi_sysctlattach(sc);
|
|
|
|
|
2008-03-11 23:40:51 +03:00
|
|
|
if (!pmf_device_register(self, NULL, NULL))
|
2007-12-09 23:27:42 +03:00
|
|
|
aprint_error_dev(self, "couldn't establish power handler\n");
|
|
|
|
else
|
|
|
|
pmf_class_network_register(self, ifp);
|
2005-08-19 18:26:38 +04:00
|
|
|
|
2005-08-01 19:14:54 +04:00
|
|
|
ieee80211_announce(ic);
|
2005-08-19 12:50:06 +04:00
|
|
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
fail: iwi_detach(self, 0);
|
2005-01-11 21:24:24 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2007-10-22 19:31:46 +04:00
|
|
|
iwi_detach(device_t self, int flags)
|
2005-01-11 21:24:24 +03:00
|
|
|
{
|
2007-10-22 19:31:46 +04:00
|
|
|
struct iwi_softc *sc = device_private(self);
|
2005-06-22 10:14:51 +04:00
|
|
|
struct ifnet *ifp = &sc->sc_if;
|
2005-01-11 21:24:24 +03:00
|
|
|
|
2007-12-09 23:27:42 +03:00
|
|
|
pmf_device_deregister(self);
|
|
|
|
|
2006-04-11 19:42:56 +04:00
|
|
|
if (ifp != NULL)
|
|
|
|
iwi_stop(ifp, 1);
|
|
|
|
|
2005-01-11 21:24:24 +03:00
|
|
|
iwi_free_firmware(sc);
|
|
|
|
|
2005-06-22 10:14:51 +04:00
|
|
|
ieee80211_ifdetach(&sc->sc_ic);
|
2005-09-25 10:49:54 +04:00
|
|
|
if (ifp != NULL)
|
|
|
|
if_detach(ifp);
|
2005-01-11 21:24:24 +03:00
|
|
|
|
2005-08-19 12:50:06 +04:00
|
|
|
iwi_free_cmd_ring(sc, &sc->cmdq);
|
2005-11-18 19:42:22 +03:00
|
|
|
iwi_free_tx_ring(sc, &sc->txq[0]);
|
|
|
|
iwi_free_tx_ring(sc, &sc->txq[1]);
|
|
|
|
iwi_free_tx_ring(sc, &sc->txq[2]);
|
|
|
|
iwi_free_tx_ring(sc, &sc->txq[3]);
|
2005-08-19 12:50:06 +04:00
|
|
|
iwi_free_rx_ring(sc, &sc->rxq);
|
2005-01-11 21:24:24 +03:00
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2005-08-19 12:50:06 +04:00
|
|
|
iwi_alloc_cmd_ring(struct iwi_softc *sc, struct iwi_cmd_ring *ring,
|
|
|
|
int count)
|
2005-01-11 21:24:24 +03:00
|
|
|
{
|
2005-08-19 12:50:06 +04:00
|
|
|
int error, nsegs;
|
|
|
|
|
|
|
|
ring->count = count;
|
|
|
|
ring->queued = 0;
|
|
|
|
ring->cur = ring->next = 0;
|
2005-01-11 21:24:24 +03:00
|
|
|
|
|
|
|
/*
|
2005-08-19 12:50:06 +04:00
|
|
|
* Allocate and map command ring
|
2005-01-11 21:24:24 +03:00
|
|
|
*/
|
|
|
|
error = bus_dmamap_create(sc->sc_dmat,
|
2005-09-25 10:49:54 +04:00
|
|
|
IWI_CMD_DESC_SIZE * count, 1,
|
|
|
|
IWI_CMD_DESC_SIZE * count, 0,
|
2005-08-19 12:50:06 +04:00
|
|
|
BUS_DMA_NOWAIT, &ring->desc_map);
|
2005-01-11 21:24:24 +03:00
|
|
|
if (error != 0) {
|
2007-10-22 19:28:48 +04:00
|
|
|
aprint_error_dev(sc->sc_dev,
|
|
|
|
"could not create command ring DMA map\n");
|
2005-01-11 21:24:24 +03:00
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
|
|
|
error = bus_dmamem_alloc(sc->sc_dmat,
|
2005-09-25 10:49:54 +04:00
|
|
|
IWI_CMD_DESC_SIZE * count, PAGE_SIZE, 0,
|
2005-08-19 12:50:06 +04:00
|
|
|
&sc->cmdq.desc_seg, 1, &nsegs, BUS_DMA_NOWAIT);
|
2005-01-11 21:24:24 +03:00
|
|
|
if (error != 0) {
|
2007-10-22 19:28:48 +04:00
|
|
|
aprint_error_dev(sc->sc_dev,
|
|
|
|
"could not allocate command ring DMA memory\n");
|
2005-01-11 21:24:24 +03:00
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
2005-08-19 12:50:06 +04:00
|
|
|
error = bus_dmamem_map(sc->sc_dmat, &sc->cmdq.desc_seg, nsegs,
|
2005-09-25 10:49:54 +04:00
|
|
|
IWI_CMD_DESC_SIZE * count,
|
2007-03-04 08:59:00 +03:00
|
|
|
(void **)&sc->cmdq.desc, BUS_DMA_NOWAIT);
|
2005-01-11 21:24:24 +03:00
|
|
|
if (error != 0) {
|
2007-10-22 19:28:48 +04:00
|
|
|
aprint_error_dev(sc->sc_dev,
|
|
|
|
"could not map command ring DMA memory\n");
|
2005-01-11 21:24:24 +03:00
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
2005-08-19 12:50:06 +04:00
|
|
|
error = bus_dmamap_load(sc->sc_dmat, sc->cmdq.desc_map, sc->cmdq.desc,
|
2005-09-25 10:49:54 +04:00
|
|
|
IWI_CMD_DESC_SIZE * count, NULL,
|
2005-01-11 21:24:24 +03:00
|
|
|
BUS_DMA_NOWAIT);
|
|
|
|
if (error != 0) {
|
2007-10-22 19:28:48 +04:00
|
|
|
aprint_error_dev(sc->sc_dev,
|
|
|
|
"could not load command ring DMA map\n");
|
2005-01-11 21:24:24 +03:00
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
2005-08-19 12:50:06 +04:00
|
|
|
memset(sc->cmdq.desc, 0,
|
2005-09-25 10:49:54 +04:00
|
|
|
IWI_CMD_DESC_SIZE * count);
|
2005-08-19 12:50:06 +04:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
fail: iwi_free_cmd_ring(sc, ring);
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
iwi_reset_cmd_ring(struct iwi_softc *sc, struct iwi_cmd_ring *ring)
|
|
|
|
{
|
2005-11-26 10:42:10 +03:00
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = ring->next; i != ring->cur;) {
|
|
|
|
bus_dmamap_sync(sc->sc_dmat, sc->cmdq.desc_map,
|
|
|
|
i * IWI_CMD_DESC_SIZE, IWI_CMD_DESC_SIZE,
|
|
|
|
BUS_DMASYNC_POSTWRITE);
|
|
|
|
|
|
|
|
wakeup(&ring->desc[i]);
|
|
|
|
i = (i + 1) % ring->count;
|
|
|
|
}
|
|
|
|
|
2005-08-19 12:50:06 +04:00
|
|
|
ring->queued = 0;
|
|
|
|
ring->cur = ring->next = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
iwi_free_cmd_ring(struct iwi_softc *sc, struct iwi_cmd_ring *ring)
|
|
|
|
{
|
|
|
|
if (ring->desc_map != NULL) {
|
|
|
|
if (ring->desc != NULL) {
|
|
|
|
bus_dmamap_unload(sc->sc_dmat, ring->desc_map);
|
2007-03-04 08:59:00 +03:00
|
|
|
bus_dmamem_unmap(sc->sc_dmat, (void *)ring->desc,
|
2005-09-25 10:49:54 +04:00
|
|
|
IWI_CMD_DESC_SIZE * ring->count);
|
2005-08-19 12:50:06 +04:00
|
|
|
bus_dmamem_free(sc->sc_dmat, &ring->desc_seg, 1);
|
|
|
|
}
|
|
|
|
bus_dmamap_destroy(sc->sc_dmat, ring->desc_map);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
iwi_alloc_tx_ring(struct iwi_softc *sc, struct iwi_tx_ring *ring,
|
2006-12-20 19:30:20 +03:00
|
|
|
int count, bus_size_t csr_ridx, bus_size_t csr_widx)
|
2005-08-19 12:50:06 +04:00
|
|
|
{
|
|
|
|
int i, error, nsegs;
|
|
|
|
|
|
|
|
ring->count = count;
|
|
|
|
ring->queued = 0;
|
|
|
|
ring->cur = ring->next = 0;
|
2005-11-18 19:42:22 +03:00
|
|
|
ring->csr_ridx = csr_ridx;
|
|
|
|
ring->csr_widx = csr_widx;
|
2005-01-11 21:24:24 +03:00
|
|
|
|
|
|
|
/*
|
2005-08-19 12:50:06 +04:00
|
|
|
* Allocate and map Tx ring
|
2005-01-11 21:24:24 +03:00
|
|
|
*/
|
|
|
|
error = bus_dmamap_create(sc->sc_dmat,
|
2005-09-25 10:49:54 +04:00
|
|
|
IWI_TX_DESC_SIZE * count, 1,
|
|
|
|
IWI_TX_DESC_SIZE * count, 0, BUS_DMA_NOWAIT,
|
2005-08-19 12:50:06 +04:00
|
|
|
&ring->desc_map);
|
2005-01-11 21:24:24 +03:00
|
|
|
if (error != 0) {
|
2007-10-22 19:28:48 +04:00
|
|
|
aprint_error_dev(sc->sc_dev,
|
|
|
|
"could not create tx ring DMA map\n");
|
2005-01-11 21:24:24 +03:00
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
|
|
|
error = bus_dmamem_alloc(sc->sc_dmat,
|
2005-09-25 10:49:54 +04:00
|
|
|
IWI_TX_DESC_SIZE * count, PAGE_SIZE, 0,
|
2005-08-19 12:50:06 +04:00
|
|
|
&ring->desc_seg, 1, &nsegs, BUS_DMA_NOWAIT);
|
2005-01-11 21:24:24 +03:00
|
|
|
if (error != 0) {
|
2007-10-22 19:28:48 +04:00
|
|
|
aprint_error_dev(sc->sc_dev,
|
|
|
|
"could not allocate tx ring DMA memory\n");
|
2005-01-11 21:24:24 +03:00
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
2005-08-19 12:50:06 +04:00
|
|
|
error = bus_dmamem_map(sc->sc_dmat, &ring->desc_seg, nsegs,
|
2005-09-25 10:49:54 +04:00
|
|
|
IWI_TX_DESC_SIZE * count,
|
2007-03-04 08:59:00 +03:00
|
|
|
(void **)&ring->desc, BUS_DMA_NOWAIT);
|
2005-01-11 21:24:24 +03:00
|
|
|
if (error != 0) {
|
2007-10-22 19:28:48 +04:00
|
|
|
aprint_error_dev(sc->sc_dev,
|
|
|
|
"could not map tx ring DMA memory\n");
|
2005-01-11 21:24:24 +03:00
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
2005-08-19 12:50:06 +04:00
|
|
|
error = bus_dmamap_load(sc->sc_dmat, ring->desc_map, ring->desc,
|
2005-09-25 10:49:54 +04:00
|
|
|
IWI_TX_DESC_SIZE * count, NULL,
|
2005-01-11 21:24:24 +03:00
|
|
|
BUS_DMA_NOWAIT);
|
|
|
|
if (error != 0) {
|
2007-10-22 19:28:48 +04:00
|
|
|
aprint_error_dev(sc->sc_dev,
|
|
|
|
"could not load tx ring DMA map\n");
|
2005-01-11 21:24:24 +03:00
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
2005-09-25 10:49:54 +04:00
|
|
|
memset(ring->desc, 0, IWI_TX_DESC_SIZE * count);
|
2005-08-19 12:50:06 +04:00
|
|
|
|
|
|
|
ring->data = malloc(count * sizeof (struct iwi_tx_data), M_DEVBUF,
|
|
|
|
M_NOWAIT | M_ZERO);
|
|
|
|
if (ring->data == NULL) {
|
2007-10-22 19:28:48 +04:00
|
|
|
aprint_error_dev(sc->sc_dev, "could not allocate soft data\n");
|
2005-08-19 12:50:06 +04:00
|
|
|
error = ENOMEM;
|
|
|
|
goto fail;
|
|
|
|
}
|
2005-01-11 21:24:24 +03:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Allocate Tx buffers DMA maps
|
|
|
|
*/
|
2005-08-19 12:50:06 +04:00
|
|
|
for (i = 0; i < count; i++) {
|
2005-01-11 21:24:24 +03:00
|
|
|
error = bus_dmamap_create(sc->sc_dmat, MCLBYTES, IWI_MAX_NSEG,
|
2005-08-19 12:50:06 +04:00
|
|
|
MCLBYTES, 0, BUS_DMA_NOWAIT, &ring->data[i].map);
|
2005-01-11 21:24:24 +03:00
|
|
|
if (error != 0) {
|
2007-10-22 19:28:48 +04:00
|
|
|
aprint_error_dev(sc->sc_dev,
|
|
|
|
"could not create tx buf DMA map");
|
2005-01-11 21:24:24 +03:00
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
}
|
2005-08-19 12:50:06 +04:00
|
|
|
return 0;
|
|
|
|
|
|
|
|
fail: iwi_free_tx_ring(sc, ring);
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
iwi_reset_tx_ring(struct iwi_softc *sc, struct iwi_tx_ring *ring)
|
|
|
|
{
|
|
|
|
struct iwi_tx_data *data;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0; i < ring->count; i++) {
|
|
|
|
data = &ring->data[i];
|
|
|
|
|
|
|
|
if (data->m != NULL) {
|
|
|
|
bus_dmamap_sync(sc->sc_dmat, data->map, 0,
|
2005-10-29 14:48:02 +04:00
|
|
|
data->map->dm_mapsize, BUS_DMASYNC_POSTWRITE);
|
2005-08-19 12:50:06 +04:00
|
|
|
bus_dmamap_unload(sc->sc_dmat, data->map);
|
|
|
|
m_freem(data->m);
|
|
|
|
data->m = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (data->ni != NULL) {
|
|
|
|
ieee80211_free_node(data->ni);
|
|
|
|
data->ni = NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
ring->queued = 0;
|
|
|
|
ring->cur = ring->next = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
iwi_free_tx_ring(struct iwi_softc *sc, struct iwi_tx_ring *ring)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
if (ring->desc_map != NULL) {
|
|
|
|
if (ring->desc != NULL) {
|
|
|
|
bus_dmamap_unload(sc->sc_dmat, ring->desc_map);
|
2007-03-04 08:59:00 +03:00
|
|
|
bus_dmamem_unmap(sc->sc_dmat, (void *)ring->desc,
|
2005-09-25 10:49:54 +04:00
|
|
|
IWI_TX_DESC_SIZE * ring->count);
|
2005-08-19 12:50:06 +04:00
|
|
|
bus_dmamem_free(sc->sc_dmat, &ring->desc_seg, 1);
|
|
|
|
}
|
|
|
|
bus_dmamap_destroy(sc->sc_dmat, ring->desc_map);
|
|
|
|
}
|
|
|
|
|
|
|
|
for (i = 0; i < ring->count; i++) {
|
|
|
|
if (ring->data[i].m != NULL) {
|
|
|
|
bus_dmamap_unload(sc->sc_dmat, ring->data[i].map);
|
|
|
|
m_freem(ring->data[i].m);
|
|
|
|
}
|
|
|
|
bus_dmamap_destroy(sc->sc_dmat, ring->data[i].map);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2007-10-22 19:28:48 +04:00
|
|
|
iwi_alloc_rx_ring(struct iwi_softc *sc, struct iwi_rx_ring *ring, int count)
|
2005-08-19 12:50:06 +04:00
|
|
|
{
|
|
|
|
int i, error;
|
|
|
|
|
|
|
|
ring->count = count;
|
|
|
|
ring->cur = 0;
|
|
|
|
|
|
|
|
ring->data = malloc(count * sizeof (struct iwi_rx_data), M_DEVBUF,
|
|
|
|
M_NOWAIT | M_ZERO);
|
|
|
|
if (ring->data == NULL) {
|
2007-10-22 19:28:48 +04:00
|
|
|
aprint_error_dev(sc->sc_dev, "could not allocate soft data\n");
|
2005-08-19 12:50:06 +04:00
|
|
|
error = ENOMEM;
|
|
|
|
goto fail;
|
|
|
|
}
|
2005-01-11 21:24:24 +03:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Allocate and map Rx buffers
|
|
|
|
*/
|
2005-08-19 12:50:06 +04:00
|
|
|
for (i = 0; i < count; i++) {
|
2005-01-11 21:24:24 +03:00
|
|
|
|
|
|
|
error = bus_dmamap_create(sc->sc_dmat, MCLBYTES, 1, MCLBYTES,
|
2005-10-20 00:18:00 +04:00
|
|
|
0, BUS_DMA_WAITOK | BUS_DMA_ALLOCNOW, &ring->data[i].map);
|
2005-01-11 21:24:24 +03:00
|
|
|
if (error != 0) {
|
2007-10-22 19:28:48 +04:00
|
|
|
aprint_error_dev(sc->sc_dev,
|
|
|
|
"could not create rx buf DMA map");
|
2005-01-11 21:24:24 +03:00
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
2005-10-20 00:18:00 +04:00
|
|
|
if ((ring->data[i].m = iwi_alloc_rx_buf(sc)) == NULL) {
|
2005-01-11 21:24:24 +03:00
|
|
|
error = ENOMEM;
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
2005-10-29 14:48:02 +04:00
|
|
|
error = bus_dmamap_load_mbuf(sc->sc_dmat, ring->data[i].map,
|
|
|
|
ring->data[i].m, BUS_DMA_READ | BUS_DMA_NOWAIT);
|
2005-01-11 21:24:24 +03:00
|
|
|
if (error != 0) {
|
2007-10-22 19:28:48 +04:00
|
|
|
aprint_error_dev(sc->sc_dev,
|
|
|
|
"could not load rx buffer DMA map\n");
|
2005-01-11 21:24:24 +03:00
|
|
|
goto fail;
|
|
|
|
}
|
2005-11-26 10:42:10 +03:00
|
|
|
|
|
|
|
bus_dmamap_sync(sc->sc_dmat, ring->data[i].map, 0,
|
|
|
|
ring->data[i].map->dm_mapsize, BUS_DMASYNC_PREREAD);
|
2005-01-11 21:24:24 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
2005-08-19 12:50:06 +04:00
|
|
|
fail: iwi_free_rx_ring(sc, ring);
|
2005-01-11 21:24:24 +03:00
|
|
|
return error;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2006-11-16 04:32:37 +03:00
|
|
|
iwi_reset_rx_ring(struct iwi_softc *sc, struct iwi_rx_ring *ring)
|
2005-01-11 21:24:24 +03:00
|
|
|
{
|
2005-08-19 12:50:06 +04:00
|
|
|
ring->cur = 0;
|
|
|
|
}
|
2005-01-11 21:24:24 +03:00
|
|
|
|
2005-08-19 12:50:06 +04:00
|
|
|
static void
|
|
|
|
iwi_free_rx_ring(struct iwi_softc *sc, struct iwi_rx_ring *ring)
|
|
|
|
{
|
|
|
|
int i;
|
2005-01-11 21:24:24 +03:00
|
|
|
|
2005-08-19 12:50:06 +04:00
|
|
|
for (i = 0; i < ring->count; i++) {
|
|
|
|
if (ring->data[i].m != NULL) {
|
|
|
|
bus_dmamap_unload(sc->sc_dmat, ring->data[i].map);
|
|
|
|
m_freem(ring->data[i].m);
|
2005-01-11 21:24:24 +03:00
|
|
|
}
|
2005-08-19 12:50:06 +04:00
|
|
|
bus_dmamap_destroy(sc->sc_dmat, ring->data[i].map);
|
2005-01-11 21:24:24 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2005-11-18 19:42:22 +03:00
|
|
|
static struct ieee80211_node *
|
2006-11-16 04:32:37 +03:00
|
|
|
iwi_node_alloc(struct ieee80211_node_table *nt)
|
2005-11-18 19:42:22 +03:00
|
|
|
{
|
|
|
|
struct iwi_node *in;
|
|
|
|
|
|
|
|
in = malloc(sizeof (struct iwi_node), M_80211_NODE, M_NOWAIT | M_ZERO);
|
|
|
|
if (in == NULL)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
in->in_station = -1;
|
|
|
|
|
|
|
|
return &in->in_node;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
iwi_alloc_unr(struct iwi_softc *sc)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0; i < IWI_MAX_IBSSNODE - 1; i++)
|
|
|
|
if ((sc->sc_unr & (1 << i)) == 0) {
|
|
|
|
sc->sc_unr |= 1 << i;
|
|
|
|
return i;
|
|
|
|
}
|
|
|
|
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
iwi_free_unr(struct iwi_softc *sc, int r)
|
|
|
|
{
|
|
|
|
|
|
|
|
sc->sc_unr &= 1 << r;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
iwi_node_free(struct ieee80211_node *ni)
|
|
|
|
{
|
|
|
|
struct ieee80211com *ic = ni->ni_ic;
|
|
|
|
struct iwi_softc *sc = ic->ic_ifp->if_softc;
|
|
|
|
struct iwi_node *in = (struct iwi_node *)ni;
|
|
|
|
|
|
|
|
if (in->in_station != -1)
|
|
|
|
iwi_free_unr(sc, in->in_station);
|
|
|
|
|
|
|
|
sc->sc_node_free(ni);
|
|
|
|
}
|
|
|
|
|
2005-01-11 21:24:24 +03:00
|
|
|
static int
|
|
|
|
iwi_media_change(struct ifnet *ifp)
|
|
|
|
{
|
|
|
|
int error;
|
|
|
|
|
|
|
|
error = ieee80211_media_change(ifp);
|
|
|
|
if (error != ENETRESET)
|
|
|
|
return error;
|
|
|
|
|
|
|
|
if ((ifp->if_flags & (IFF_UP | IFF_RUNNING)) == (IFF_UP | IFF_RUNNING))
|
|
|
|
iwi_init(ifp);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2006-12-20 19:30:20 +03:00
|
|
|
/*
|
|
|
|
* Convert h/w rate code to IEEE rate code.
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
iwi_cvtrate(int iwirate)
|
|
|
|
{
|
|
|
|
switch (iwirate) {
|
|
|
|
case IWI_RATE_DS1: return 2;
|
|
|
|
case IWI_RATE_DS2: return 4;
|
|
|
|
case IWI_RATE_DS5: return 11;
|
|
|
|
case IWI_RATE_DS11: return 22;
|
|
|
|
case IWI_RATE_OFDM6: return 12;
|
|
|
|
case IWI_RATE_OFDM9: return 18;
|
|
|
|
case IWI_RATE_OFDM12: return 24;
|
|
|
|
case IWI_RATE_OFDM18: return 36;
|
|
|
|
case IWI_RATE_OFDM24: return 48;
|
|
|
|
case IWI_RATE_OFDM36: return 72;
|
|
|
|
case IWI_RATE_OFDM48: return 96;
|
|
|
|
case IWI_RATE_OFDM54: return 108;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2005-09-04 10:58:20 +04:00
|
|
|
/*
|
2005-11-18 19:42:22 +03:00
|
|
|
* The firmware automatically adapts the transmit speed. We report its current
|
|
|
|
* value here.
|
2005-09-04 10:58:20 +04:00
|
|
|
*/
|
2005-01-11 21:24:24 +03:00
|
|
|
static void
|
|
|
|
iwi_media_status(struct ifnet *ifp, struct ifmediareq *imr)
|
|
|
|
{
|
|
|
|
struct iwi_softc *sc = ifp->if_softc;
|
|
|
|
struct ieee80211com *ic = &sc->sc_ic;
|
2006-12-20 19:30:20 +03:00
|
|
|
int rate;
|
2005-01-11 21:24:24 +03:00
|
|
|
|
|
|
|
imr->ifm_status = IFM_AVALID;
|
|
|
|
imr->ifm_active = IFM_IEEE80211;
|
|
|
|
if (ic->ic_state == IEEE80211_S_RUN)
|
|
|
|
imr->ifm_status |= IFM_ACTIVE;
|
|
|
|
|
|
|
|
/* read current transmission rate from adapter */
|
2006-12-20 19:30:20 +03:00
|
|
|
rate = iwi_cvtrate(CSR_READ_4(sc, IWI_CSR_CURRENT_TX_RATE));
|
2005-01-11 21:24:24 +03:00
|
|
|
imr->ifm_active |= ieee80211_rate2media(ic, rate, ic->ic_curmode);
|
2006-12-20 19:30:20 +03:00
|
|
|
|
2005-01-11 21:24:24 +03:00
|
|
|
switch (ic->ic_opmode) {
|
|
|
|
case IEEE80211_M_STA:
|
|
|
|
break;
|
|
|
|
|
|
|
|
case IEEE80211_M_IBSS:
|
|
|
|
imr->ifm_active |= IFM_IEEE80211_ADHOC;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case IEEE80211_M_MONITOR:
|
|
|
|
imr->ifm_active |= IFM_IEEE80211_MONITOR;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case IEEE80211_M_AHDEMO:
|
|
|
|
case IEEE80211_M_HOSTAP:
|
|
|
|
/* should not get there */
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
iwi_newstate(struct ieee80211com *ic, enum ieee80211_state nstate, int arg)
|
|
|
|
{
|
2005-06-22 10:14:51 +04:00
|
|
|
struct iwi_softc *sc = ic->ic_ifp->if_softc;
|
2005-01-11 21:24:24 +03:00
|
|
|
|
2006-12-20 19:30:20 +03:00
|
|
|
DPRINTF(("%s: %s -> %s flags 0x%x\n", __func__,
|
|
|
|
ieee80211_state_name[ic->ic_state],
|
|
|
|
ieee80211_state_name[nstate], sc->flags));
|
|
|
|
|
2005-01-11 21:24:24 +03:00
|
|
|
switch (nstate) {
|
|
|
|
case IEEE80211_S_SCAN:
|
2005-07-31 01:15:51 +04:00
|
|
|
if (sc->flags & IWI_FLAG_SCANNING)
|
|
|
|
break;
|
|
|
|
|
|
|
|
ieee80211_node_table_reset(&ic->ic_scan);
|
|
|
|
ic->ic_flags |= IEEE80211_F_SCAN | IEEE80211_F_ASCAN;
|
|
|
|
sc->flags |= IWI_FLAG_SCANNING;
|
2005-11-29 16:57:00 +03:00
|
|
|
/* blink the led while scanning */
|
|
|
|
iwi_led_set(sc, IWI_LED_ASSOCIATED, 1);
|
2005-01-11 21:24:24 +03:00
|
|
|
iwi_scan(sc);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case IEEE80211_S_AUTH:
|
|
|
|
iwi_auth_and_assoc(sc);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case IEEE80211_S_RUN:
|
|
|
|
if (ic->ic_opmode == IEEE80211_M_IBSS)
|
|
|
|
ieee80211_new_state(ic, IEEE80211_S_AUTH, -1);
|
2005-06-20 13:03:44 +04:00
|
|
|
else if (ic->ic_opmode == IEEE80211_M_MONITOR)
|
|
|
|
iwi_set_chan(sc, ic->ic_ibss_chan);
|
2005-07-31 01:15:51 +04:00
|
|
|
|
|
|
|
return (*sc->sc_newstate)(ic, nstate,
|
|
|
|
IEEE80211_FC0_SUBTYPE_ASSOC_RESP);
|
2005-01-11 21:24:24 +03:00
|
|
|
|
|
|
|
case IEEE80211_S_ASSOC:
|
2005-11-29 16:57:00 +03:00
|
|
|
iwi_led_set(sc, IWI_LED_ASSOCIATED, 0);
|
2005-07-31 01:15:51 +04:00
|
|
|
break;
|
|
|
|
|
2005-01-11 21:24:24 +03:00
|
|
|
case IEEE80211_S_INIT:
|
2005-07-31 01:15:51 +04:00
|
|
|
sc->flags &= ~IWI_FLAG_SCANNING;
|
2005-08-31 01:18:47 +04:00
|
|
|
return (*sc->sc_newstate)(ic, nstate, arg);
|
2005-01-11 21:24:24 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
ic->ic_state = nstate;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2005-11-18 19:42:22 +03:00
|
|
|
/*
|
|
|
|
* WME parameters coming from IEEE 802.11e specification. These values are
|
|
|
|
* already declared in ieee80211_proto.c, but they are static so they can't
|
|
|
|
* be reused here.
|
|
|
|
*/
|
|
|
|
static const struct wmeParams iwi_wme_cck_params[WME_NUM_AC] = {
|
2006-08-30 20:43:56 +04:00
|
|
|
{ 0, 3, 5, 7, 0, 0, }, /* WME_AC_BE */
|
|
|
|
{ 0, 3, 5, 10, 0, 0, }, /* WME_AC_BK */
|
|
|
|
{ 0, 2, 4, 5, 188, 0, }, /* WME_AC_VI */
|
|
|
|
{ 0, 2, 3, 4, 102, 0, }, /* WME_AC_VO */
|
2005-11-18 19:42:22 +03:00
|
|
|
};
|
|
|
|
|
|
|
|
static const struct wmeParams iwi_wme_ofdm_params[WME_NUM_AC] = {
|
2006-08-30 20:43:56 +04:00
|
|
|
{ 0, 3, 4, 6, 0, 0, }, /* WME_AC_BE */
|
|
|
|
{ 0, 3, 4, 10, 0, 0, }, /* WME_AC_BK */
|
|
|
|
{ 0, 2, 3, 4, 94, 0, }, /* WME_AC_VI */
|
|
|
|
{ 0, 2, 2, 3, 47, 0, }, /* WME_AC_VO */
|
2005-11-18 19:42:22 +03:00
|
|
|
};
|
|
|
|
|
|
|
|
static int
|
|
|
|
iwi_wme_update(struct ieee80211com *ic)
|
|
|
|
{
|
|
|
|
#define IWI_EXP2(v) htole16((1 << (v)) - 1)
|
|
|
|
#define IWI_USEC(v) htole16(IEEE80211_TXOP_TO_US(v))
|
|
|
|
struct iwi_softc *sc = ic->ic_ifp->if_softc;
|
|
|
|
struct iwi_wme_params wme[3];
|
|
|
|
const struct wmeParams *wmep;
|
|
|
|
int ac;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* We shall not override firmware default WME values if WME is not
|
|
|
|
* actually enabled.
|
|
|
|
*/
|
|
|
|
if (!(ic->ic_flags & IEEE80211_F_WME))
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
for (ac = 0; ac < WME_NUM_AC; ac++) {
|
|
|
|
/* set WME values for current operating mode */
|
|
|
|
wmep = &ic->ic_wme.wme_chanParams.cap_wmeParams[ac];
|
|
|
|
wme[0].aifsn[ac] = wmep->wmep_aifsn;
|
|
|
|
wme[0].cwmin[ac] = IWI_EXP2(wmep->wmep_logcwmin);
|
|
|
|
wme[0].cwmax[ac] = IWI_EXP2(wmep->wmep_logcwmax);
|
|
|
|
wme[0].burst[ac] = IWI_USEC(wmep->wmep_txopLimit);
|
|
|
|
wme[0].acm[ac] = wmep->wmep_acm;
|
|
|
|
|
|
|
|
/* set WME values for CCK modulation */
|
|
|
|
wmep = &iwi_wme_cck_params[ac];
|
|
|
|
wme[1].aifsn[ac] = wmep->wmep_aifsn;
|
|
|
|
wme[1].cwmin[ac] = IWI_EXP2(wmep->wmep_logcwmin);
|
|
|
|
wme[1].cwmax[ac] = IWI_EXP2(wmep->wmep_logcwmax);
|
|
|
|
wme[1].burst[ac] = IWI_USEC(wmep->wmep_txopLimit);
|
|
|
|
wme[1].acm[ac] = wmep->wmep_acm;
|
|
|
|
|
|
|
|
/* set WME values for OFDM modulation */
|
|
|
|
wmep = &iwi_wme_ofdm_params[ac];
|
|
|
|
wme[2].aifsn[ac] = wmep->wmep_aifsn;
|
|
|
|
wme[2].cwmin[ac] = IWI_EXP2(wmep->wmep_logcwmin);
|
|
|
|
wme[2].cwmax[ac] = IWI_EXP2(wmep->wmep_logcwmax);
|
|
|
|
wme[2].burst[ac] = IWI_USEC(wmep->wmep_txopLimit);
|
|
|
|
wme[2].acm[ac] = wmep->wmep_acm;
|
|
|
|
}
|
|
|
|
|
|
|
|
DPRINTF(("Setting WME parameters\n"));
|
|
|
|
return iwi_cmd(sc, IWI_CMD_SET_WME_PARAMS, wme, sizeof wme, 1);
|
|
|
|
#undef IWI_USEC
|
|
|
|
#undef IWI_EXP2
|
|
|
|
}
|
|
|
|
|
2005-01-11 21:24:24 +03:00
|
|
|
/*
|
|
|
|
* Read 16 bits at address 'addr' from the serial EEPROM.
|
|
|
|
*/
|
2005-09-17 16:40:27 +04:00
|
|
|
static uint16_t
|
|
|
|
iwi_read_prom_word(struct iwi_softc *sc, uint8_t addr)
|
2005-01-11 21:24:24 +03:00
|
|
|
{
|
2005-09-17 16:40:27 +04:00
|
|
|
uint32_t tmp;
|
|
|
|
uint16_t val;
|
2005-01-11 21:24:24 +03:00
|
|
|
int n;
|
|
|
|
|
|
|
|
/* Clock C once before the first command */
|
|
|
|
IWI_EEPROM_CTL(sc, 0);
|
|
|
|
IWI_EEPROM_CTL(sc, IWI_EEPROM_S);
|
|
|
|
IWI_EEPROM_CTL(sc, IWI_EEPROM_S | IWI_EEPROM_C);
|
|
|
|
IWI_EEPROM_CTL(sc, IWI_EEPROM_S);
|
|
|
|
|
|
|
|
/* Write start bit (1) */
|
|
|
|
IWI_EEPROM_CTL(sc, IWI_EEPROM_S | IWI_EEPROM_D);
|
|
|
|
IWI_EEPROM_CTL(sc, IWI_EEPROM_S | IWI_EEPROM_D | IWI_EEPROM_C);
|
|
|
|
|
|
|
|
/* Write READ opcode (10) */
|
|
|
|
IWI_EEPROM_CTL(sc, IWI_EEPROM_S | IWI_EEPROM_D);
|
|
|
|
IWI_EEPROM_CTL(sc, IWI_EEPROM_S | IWI_EEPROM_D | IWI_EEPROM_C);
|
|
|
|
IWI_EEPROM_CTL(sc, IWI_EEPROM_S);
|
|
|
|
IWI_EEPROM_CTL(sc, IWI_EEPROM_S | IWI_EEPROM_C);
|
|
|
|
|
|
|
|
/* Write address A7-A0 */
|
|
|
|
for (n = 7; n >= 0; n--) {
|
|
|
|
IWI_EEPROM_CTL(sc, IWI_EEPROM_S |
|
|
|
|
(((addr >> n) & 1) << IWI_EEPROM_SHIFT_D));
|
|
|
|
IWI_EEPROM_CTL(sc, IWI_EEPROM_S |
|
|
|
|
(((addr >> n) & 1) << IWI_EEPROM_SHIFT_D) | IWI_EEPROM_C);
|
|
|
|
}
|
|
|
|
|
|
|
|
IWI_EEPROM_CTL(sc, IWI_EEPROM_S);
|
|
|
|
|
|
|
|
/* Read data Q15-Q0 */
|
|
|
|
val = 0;
|
|
|
|
for (n = 15; n >= 0; n--) {
|
|
|
|
IWI_EEPROM_CTL(sc, IWI_EEPROM_S | IWI_EEPROM_C);
|
|
|
|
IWI_EEPROM_CTL(sc, IWI_EEPROM_S);
|
|
|
|
tmp = MEM_READ_4(sc, IWI_MEM_EEPROM_CTL);
|
|
|
|
val |= ((tmp & IWI_EEPROM_Q) >> IWI_EEPROM_SHIFT_Q) << n;
|
|
|
|
}
|
|
|
|
|
|
|
|
IWI_EEPROM_CTL(sc, 0);
|
|
|
|
|
|
|
|
/* Clear Chip Select and clock C */
|
|
|
|
IWI_EEPROM_CTL(sc, IWI_EEPROM_S);
|
|
|
|
IWI_EEPROM_CTL(sc, 0);
|
|
|
|
IWI_EEPROM_CTL(sc, IWI_EEPROM_C);
|
|
|
|
|
2005-10-29 12:44:28 +04:00
|
|
|
return val;
|
2005-01-11 21:24:24 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* XXX: Hack to set the current channel to the value advertised in beacons or
|
|
|
|
* probe responses. Only used during AP detection.
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
iwi_fix_channel(struct ieee80211com *ic, struct mbuf *m)
|
|
|
|
{
|
|
|
|
struct ieee80211_frame *wh;
|
2005-09-17 16:40:27 +04:00
|
|
|
uint8_t subtype;
|
|
|
|
uint8_t *frm, *efrm;
|
2005-01-11 21:24:24 +03:00
|
|
|
|
|
|
|
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;
|
|
|
|
|
2005-09-17 16:40:27 +04:00
|
|
|
frm = (uint8_t *)(wh + 1);
|
|
|
|
efrm = mtod(m, uint8_t *) + m->m_len;
|
2005-01-11 21:24:24 +03:00
|
|
|
|
|
|
|
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
|
2005-11-18 19:42:22 +03:00
|
|
|
ic->ic_curchan = &ic->ic_channels[frm[2]];
|
2005-01-11 21:24:24 +03:00
|
|
|
|
|
|
|
frm += frm[1] + 2;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2005-10-20 00:18:00 +04:00
|
|
|
static struct mbuf *
|
|
|
|
iwi_alloc_rx_buf(struct iwi_softc *sc)
|
|
|
|
{
|
|
|
|
struct mbuf *m;
|
|
|
|
|
|
|
|
MGETHDR(m, M_DONTWAIT, MT_DATA);
|
|
|
|
if (m == NULL) {
|
2007-10-22 19:28:48 +04:00
|
|
|
aprint_error_dev(sc->sc_dev, "could not allocate rx mbuf\n");
|
2005-10-20 00:18:00 +04:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
MCLGET(m, M_DONTWAIT);
|
|
|
|
if (!(m->m_flags & M_EXT)) {
|
2007-10-22 19:28:48 +04:00
|
|
|
aprint_error_dev(sc->sc_dev,
|
|
|
|
"could not allocate rx mbuf cluster\n");
|
2005-10-20 00:18:00 +04:00
|
|
|
m_freem(m);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2005-10-29 14:48:02 +04:00
|
|
|
m->m_pkthdr.len = m->m_len = m->m_ext.ext_size;
|
2005-10-20 00:18:00 +04:00
|
|
|
return m;
|
|
|
|
}
|
|
|
|
|
2005-01-11 21:24:24 +03:00
|
|
|
static void
|
2005-08-19 12:50:06 +04:00
|
|
|
iwi_frame_intr(struct iwi_softc *sc, struct iwi_rx_data *data, int i,
|
2005-01-11 21:24:24 +03:00
|
|
|
struct iwi_frame *frame)
|
|
|
|
{
|
|
|
|
struct ieee80211com *ic = &sc->sc_ic;
|
2005-07-31 01:15:51 +04:00
|
|
|
struct ifnet *ifp = ic->ic_ifp;
|
2005-10-20 00:18:00 +04:00
|
|
|
struct mbuf *m, *m_new;
|
2005-07-31 01:15:51 +04:00
|
|
|
struct ieee80211_frame *wh;
|
2005-01-11 21:24:24 +03:00
|
|
|
struct ieee80211_node *ni;
|
|
|
|
int error;
|
|
|
|
|
2005-08-19 12:50:06 +04:00
|
|
|
DPRINTFN(5, ("received frame len=%u chan=%u rssi=%u\n",
|
|
|
|
le16toh(frame->len), frame->chan, frame->rssi_dbm));
|
2005-01-11 21:24:24 +03:00
|
|
|
|
2005-07-31 01:15:51 +04:00
|
|
|
if (le16toh(frame->len) < sizeof (struct ieee80211_frame) ||
|
2005-01-11 21:24:24 +03:00
|
|
|
le16toh(frame->len) > MCLBYTES) {
|
2007-10-22 19:28:48 +04:00
|
|
|
DPRINTF(("%s: bad frame length\n", device_xname(sc->sc_dev)));
|
2005-08-19 12:50:06 +04:00
|
|
|
ifp->if_ierrors++;
|
|
|
|
return;
|
2005-01-11 21:24:24 +03:00
|
|
|
}
|
|
|
|
|
2005-10-20 00:18:00 +04:00
|
|
|
/*
|
|
|
|
* Try to allocate a new mbuf for this ring element and
|
|
|
|
* load it before processing the current mbuf. If the ring
|
|
|
|
* element cannot be reloaded, drop the received packet
|
|
|
|
* and reuse the old mbuf. In the unlikely case that
|
|
|
|
* the old mbuf can't be reloaded either, explicitly panic.
|
|
|
|
*
|
|
|
|
* XXX Reorganize buffer by moving elements from the logical
|
|
|
|
* end of the ring to the front instead of dropping.
|
|
|
|
*/
|
|
|
|
if ((m_new = iwi_alloc_rx_buf(sc)) == NULL) {
|
|
|
|
ifp->if_ierrors++;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2005-08-19 12:50:06 +04:00
|
|
|
bus_dmamap_unload(sc->sc_dmat, data->map);
|
2005-01-11 21:24:24 +03:00
|
|
|
|
2005-10-29 14:48:02 +04:00
|
|
|
error = bus_dmamap_load_mbuf(sc->sc_dmat, data->map, m_new,
|
|
|
|
BUS_DMA_READ | BUS_DMA_NOWAIT);
|
2005-10-20 00:18:00 +04:00
|
|
|
if (error != 0) {
|
2007-10-22 19:28:48 +04:00
|
|
|
aprint_error_dev(sc->sc_dev,
|
|
|
|
"could not load rx buf DMA map\n");
|
2005-10-20 00:18:00 +04:00
|
|
|
m_freem(m_new);
|
|
|
|
ifp->if_ierrors++;
|
2005-10-29 14:48:02 +04:00
|
|
|
error = bus_dmamap_load_mbuf(sc->sc_dmat, data->map,
|
|
|
|
data->m, BUS_DMA_READ | BUS_DMA_NOWAIT);
|
2005-10-20 00:18:00 +04:00
|
|
|
if (error)
|
|
|
|
panic("%s: unable to remap rx buf",
|
2007-10-22 19:28:48 +04:00
|
|
|
device_xname(sc->sc_dev));
|
2005-10-20 00:18:00 +04:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* New mbuf successfully loaded, update RX ring and continue
|
|
|
|
* processing.
|
|
|
|
*/
|
2005-08-19 12:50:06 +04:00
|
|
|
m = data->m;
|
2005-10-20 00:18:00 +04:00
|
|
|
data->m = m_new;
|
|
|
|
CSR_WRITE_4(sc, IWI_CSR_RX_BASE + i * 4, data->map->dm_segs[0].ds_addr);
|
|
|
|
|
|
|
|
/* Finalize mbuf */
|
2005-01-11 21:24:24 +03:00
|
|
|
m->m_pkthdr.rcvif = ifp;
|
|
|
|
m->m_pkthdr.len = m->m_len = sizeof (struct iwi_hdr) +
|
|
|
|
sizeof (struct iwi_frame) + le16toh(frame->len);
|
|
|
|
|
|
|
|
m_adj(m, sizeof (struct iwi_hdr) + sizeof (struct iwi_frame));
|
|
|
|
|
2005-07-31 01:15:51 +04:00
|
|
|
if (ic->ic_state == IEEE80211_S_SCAN)
|
|
|
|
iwi_fix_channel(ic, m);
|
2005-01-11 21:24:24 +03:00
|
|
|
|
|
|
|
#if NBPFILTER > 0
|
|
|
|
if (sc->sc_drvbpf != NULL) {
|
|
|
|
struct iwi_rx_radiotap_header *tap = &sc->sc_rxtap;
|
2005-08-19 12:50:06 +04:00
|
|
|
|
2005-07-31 01:15:51 +04:00
|
|
|
tap->wr_flags = 0;
|
2006-12-20 19:30:20 +03:00
|
|
|
tap->wr_rate = iwi_cvtrate(frame->rate);
|
2005-07-31 01:15:51 +04:00
|
|
|
tap->wr_chan_freq =
|
|
|
|
htole16(ic->ic_channels[frame->chan].ic_freq);
|
|
|
|
tap->wr_chan_flags =
|
|
|
|
htole16(ic->ic_channels[frame->chan].ic_flags);
|
|
|
|
tap->wr_antsignal = frame->signal;
|
|
|
|
tap->wr_antenna = frame->antenna;
|
2005-01-11 21:24:24 +03:00
|
|
|
|
2005-09-13 01:11:11 +04:00
|
|
|
bpf_mtap2(sc->sc_drvbpf, tap, sc->sc_rxtap_len, m);
|
2005-01-11 21:24:24 +03:00
|
|
|
}
|
|
|
|
#endif
|
2005-07-31 01:15:51 +04:00
|
|
|
wh = mtod(m, struct ieee80211_frame *);
|
|
|
|
ni = ieee80211_find_rxnode(ic, (struct ieee80211_frame_min *)wh);
|
2005-02-27 03:26:58 +03:00
|
|
|
|
2005-01-11 21:24:24 +03:00
|
|
|
/* Send the frame to the upper layer */
|
2005-07-31 01:15:51 +04:00
|
|
|
ieee80211_input(ic, m, ni, frame->rssi_dbm, 0);
|
2005-01-11 21:24:24 +03:00
|
|
|
|
2005-07-31 01:15:51 +04:00
|
|
|
/* node is no longer needed */
|
2005-06-22 10:14:51 +04:00
|
|
|
ieee80211_free_node(ni);
|
2005-01-11 21:24:24 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2005-09-25 10:49:54 +04:00
|
|
|
iwi_notification_intr(struct iwi_softc *sc, struct iwi_notif *notif)
|
2005-01-11 21:24:24 +03:00
|
|
|
{
|
|
|
|
struct ieee80211com *ic = &sc->sc_ic;
|
|
|
|
struct iwi_notif_scan_channel *chan;
|
|
|
|
struct iwi_notif_scan_complete *scan;
|
|
|
|
struct iwi_notif_authentication *auth;
|
|
|
|
struct iwi_notif_association *assoc;
|
2006-12-20 19:30:20 +03:00
|
|
|
struct iwi_notif_beacon_state *beacon;
|
2005-01-11 21:24:24 +03:00
|
|
|
|
|
|
|
switch (notif->type) {
|
|
|
|
case IWI_NOTIF_TYPE_SCAN_CHANNEL:
|
|
|
|
chan = (struct iwi_notif_scan_channel *)(notif + 1);
|
|
|
|
|
2006-12-20 19:30:20 +03:00
|
|
|
DPRINTFN(2, ("Scan of channel %u complete (%u)\n",
|
|
|
|
ic->ic_channels[chan->nchan].ic_freq, chan->nchan));
|
2005-01-11 21:24:24 +03:00
|
|
|
break;
|
|
|
|
|
|
|
|
case IWI_NOTIF_TYPE_SCAN_COMPLETE:
|
|
|
|
scan = (struct iwi_notif_scan_complete *)(notif + 1);
|
|
|
|
|
|
|
|
DPRINTFN(2, ("Scan completed (%u, %u)\n", scan->nchan,
|
|
|
|
scan->status));
|
|
|
|
|
2005-06-20 13:03:44 +04:00
|
|
|
/* monitor mode uses scan to set the channel ... */
|
2005-07-31 01:15:51 +04:00
|
|
|
if (ic->ic_opmode != IEEE80211_M_MONITOR) {
|
|
|
|
sc->flags &= ~IWI_FLAG_SCANNING;
|
2005-06-20 13:03:44 +04:00
|
|
|
ieee80211_end_scan(ic);
|
2005-07-31 01:15:51 +04:00
|
|
|
} else
|
2005-06-20 13:03:44 +04:00
|
|
|
iwi_set_chan(sc, ic->ic_ibss_chan);
|
2005-01-11 21:24:24 +03:00
|
|
|
break;
|
|
|
|
|
|
|
|
case IWI_NOTIF_TYPE_AUTHENTICATION:
|
|
|
|
auth = (struct iwi_notif_authentication *)(notif + 1);
|
|
|
|
|
|
|
|
DPRINTFN(2, ("Authentication (%u)\n", auth->state));
|
|
|
|
|
|
|
|
switch (auth->state) {
|
2006-12-20 19:30:20 +03:00
|
|
|
case IWI_AUTH_SUCCESS:
|
2005-11-18 19:42:22 +03:00
|
|
|
ieee80211_node_authorize(ic->ic_bss);
|
2005-01-11 21:24:24 +03:00
|
|
|
ieee80211_new_state(ic, IEEE80211_S_ASSOC, -1);
|
|
|
|
break;
|
|
|
|
|
2006-12-20 19:30:20 +03:00
|
|
|
case IWI_AUTH_FAIL:
|
2005-01-11 21:24:24 +03:00
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
2007-10-22 19:28:48 +04:00
|
|
|
aprint_error_dev(sc->sc_dev,
|
|
|
|
"unknown authentication state %u\n", auth->state);
|
2005-01-11 21:24:24 +03:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case IWI_NOTIF_TYPE_ASSOCIATION:
|
|
|
|
assoc = (struct iwi_notif_association *)(notif + 1);
|
|
|
|
|
|
|
|
DPRINTFN(2, ("Association (%u, %u)\n", assoc->state,
|
|
|
|
assoc->status));
|
|
|
|
|
|
|
|
switch (assoc->state) {
|
2006-12-20 19:30:20 +03:00
|
|
|
case IWI_AUTH_SUCCESS:
|
2005-07-31 01:15:51 +04:00
|
|
|
/* re-association, do nothing */
|
|
|
|
break;
|
|
|
|
|
2006-12-20 19:30:20 +03:00
|
|
|
case IWI_ASSOC_SUCCESS:
|
2005-01-11 21:24:24 +03:00
|
|
|
ieee80211_new_state(ic, IEEE80211_S_RUN, -1);
|
|
|
|
break;
|
|
|
|
|
2006-12-20 19:30:20 +03:00
|
|
|
case IWI_ASSOC_FAIL:
|
2005-07-31 01:15:51 +04:00
|
|
|
ieee80211_begin_scan(ic, 1);
|
2005-01-11 21:24:24 +03:00
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
2007-10-22 19:28:48 +04:00
|
|
|
aprint_error_dev(sc->sc_dev,
|
|
|
|
"unknown association state %u\n", assoc->state);
|
2005-01-11 21:24:24 +03:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case IWI_NOTIF_TYPE_BEACON:
|
2006-12-20 19:30:20 +03:00
|
|
|
beacon = (struct iwi_notif_beacon_state *)(notif + 1);
|
|
|
|
|
|
|
|
if (beacon->state == IWI_BEACON_MISS) {
|
2007-10-22 19:28:48 +04:00
|
|
|
DPRINTFN(5, ("%s: %u beacon(s) missed\n",
|
|
|
|
device_xname(sc->sc_dev), le32toh(beacon->number)));
|
2006-12-20 19:30:20 +03:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case IWI_NOTIF_TYPE_FRAG_LENGTH:
|
|
|
|
case IWI_NOTIF_TYPE_LINK_QUALITY:
|
|
|
|
case IWI_NOTIF_TYPE_TGI_TX_KEY:
|
|
|
|
case IWI_NOTIF_TYPE_CALIBRATION:
|
2005-01-11 21:24:24 +03:00
|
|
|
case IWI_NOTIF_TYPE_NOISE:
|
|
|
|
DPRINTFN(5, ("Notification (%u)\n", notif->type));
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
2006-12-20 19:30:20 +03:00
|
|
|
DPRINTF(("%s: unknown notification type %u flags 0x%x len %d\n",
|
2007-10-22 19:28:48 +04:00
|
|
|
device_xname(sc->sc_dev), notif->type, notif->flags,
|
|
|
|
le16toh(notif->len)));
|
2005-01-11 21:24:24 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2005-11-26 10:42:10 +03:00
|
|
|
static void
|
|
|
|
iwi_cmd_intr(struct iwi_softc *sc)
|
|
|
|
{
|
|
|
|
uint32_t hw;
|
|
|
|
|
|
|
|
hw = CSR_READ_4(sc, IWI_CSR_CMD_RIDX);
|
|
|
|
|
2006-12-20 19:30:20 +03:00
|
|
|
bus_dmamap_sync(sc->sc_dmat, sc->cmdq.desc_map,
|
|
|
|
sc->cmdq.next * IWI_CMD_DESC_SIZE, IWI_CMD_DESC_SIZE,
|
|
|
|
BUS_DMASYNC_POSTWRITE);
|
|
|
|
|
|
|
|
wakeup(&sc->cmdq.desc[sc->cmdq.next]);
|
|
|
|
|
|
|
|
sc->cmdq.next = (sc->cmdq.next + 1) % sc->cmdq.count;
|
2005-11-26 10:42:10 +03:00
|
|
|
|
2006-12-20 19:30:20 +03:00
|
|
|
if (--sc->cmdq.queued > 0) {
|
|
|
|
CSR_WRITE_4(sc, IWI_CSR_CMD_WIDX, (sc->cmdq.next + 1) % sc->cmdq.count);
|
2005-11-26 10:42:10 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2005-01-11 21:24:24 +03:00
|
|
|
static void
|
|
|
|
iwi_rx_intr(struct iwi_softc *sc)
|
|
|
|
{
|
2005-08-19 12:50:06 +04:00
|
|
|
struct iwi_rx_data *data;
|
2005-01-11 21:24:24 +03:00
|
|
|
struct iwi_hdr *hdr;
|
2005-08-19 12:50:06 +04:00
|
|
|
uint32_t hw;
|
2005-01-11 21:24:24 +03:00
|
|
|
|
2005-08-19 12:50:06 +04:00
|
|
|
hw = CSR_READ_4(sc, IWI_CSR_RX_RIDX);
|
2005-01-11 21:24:24 +03:00
|
|
|
|
2005-08-19 12:50:06 +04:00
|
|
|
for (; sc->rxq.cur != hw;) {
|
|
|
|
data = &sc->rxq.data[sc->rxq.cur];
|
2005-01-11 21:24:24 +03:00
|
|
|
|
2005-08-19 12:50:06 +04:00
|
|
|
bus_dmamap_sync(sc->sc_dmat, data->map, 0,
|
2005-10-29 14:48:02 +04:00
|
|
|
data->map->dm_mapsize, BUS_DMASYNC_POSTREAD);
|
2005-01-11 21:24:24 +03:00
|
|
|
|
2005-08-19 12:50:06 +04:00
|
|
|
hdr = mtod(data->m, struct iwi_hdr *);
|
2005-01-11 21:24:24 +03:00
|
|
|
|
|
|
|
switch (hdr->type) {
|
|
|
|
case IWI_HDR_TYPE_FRAME:
|
2005-08-19 12:50:06 +04:00
|
|
|
iwi_frame_intr(sc, data, sc->rxq.cur,
|
2005-01-11 21:24:24 +03:00
|
|
|
(struct iwi_frame *)(hdr + 1));
|
|
|
|
break;
|
|
|
|
|
|
|
|
case IWI_HDR_TYPE_NOTIF:
|
2005-09-25 10:49:54 +04:00
|
|
|
iwi_notification_intr(sc,
|
2005-01-11 21:24:24 +03:00
|
|
|
(struct iwi_notif *)(hdr + 1));
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
2007-10-22 19:28:48 +04:00
|
|
|
aprint_error_dev(sc->sc_dev, "unknown hdr type %u\n",
|
|
|
|
hdr->type);
|
2005-01-11 21:24:24 +03:00
|
|
|
}
|
2005-08-19 12:50:06 +04:00
|
|
|
|
2005-11-26 10:42:10 +03:00
|
|
|
bus_dmamap_sync(sc->sc_dmat, data->map, 0,
|
|
|
|
data->map->dm_mapsize, BUS_DMASYNC_PREREAD);
|
|
|
|
|
2005-08-19 12:50:06 +04:00
|
|
|
DPRINTFN(15, ("rx done idx=%u\n", sc->rxq.cur));
|
|
|
|
|
|
|
|
sc->rxq.cur = (sc->rxq.cur + 1) % sc->rxq.count;
|
2005-01-11 21:24:24 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Tell the firmware what we have processed */
|
2005-08-19 12:50:06 +04:00
|
|
|
hw = (hw == 0) ? sc->rxq.count - 1 : hw - 1;
|
|
|
|
CSR_WRITE_4(sc, IWI_CSR_RX_WIDX, hw);
|
2005-01-11 21:24:24 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2005-11-18 19:42:22 +03:00
|
|
|
iwi_tx_intr(struct iwi_softc *sc, struct iwi_tx_ring *txq)
|
2005-01-11 21:24:24 +03:00
|
|
|
{
|
2005-06-22 10:14:51 +04:00
|
|
|
struct ifnet *ifp = &sc->sc_if;
|
2005-08-19 12:50:06 +04:00
|
|
|
struct iwi_tx_data *data;
|
2005-09-17 16:40:27 +04:00
|
|
|
uint32_t hw;
|
2005-01-11 21:24:24 +03:00
|
|
|
|
2005-11-18 19:42:22 +03:00
|
|
|
hw = CSR_READ_4(sc, txq->csr_ridx);
|
2005-01-11 21:24:24 +03:00
|
|
|
|
2005-11-18 19:42:22 +03:00
|
|
|
for (; txq->next != hw;) {
|
|
|
|
data = &txq->data[txq->next];
|
2005-01-11 21:24:24 +03:00
|
|
|
|
2005-08-19 12:50:06 +04:00
|
|
|
bus_dmamap_sync(sc->sc_dmat, data->map, 0,
|
2005-10-29 14:48:02 +04:00
|
|
|
data->map->dm_mapsize, BUS_DMASYNC_POSTWRITE);
|
2005-08-19 12:50:06 +04:00
|
|
|
bus_dmamap_unload(sc->sc_dmat, data->map);
|
|
|
|
m_freem(data->m);
|
|
|
|
data->m = NULL;
|
|
|
|
ieee80211_free_node(data->ni);
|
|
|
|
data->ni = NULL;
|
2005-01-11 21:24:24 +03:00
|
|
|
|
2005-11-18 19:42:22 +03:00
|
|
|
DPRINTFN(15, ("tx done idx=%u\n", txq->next));
|
2005-01-11 21:24:24 +03:00
|
|
|
|
2005-07-31 01:15:51 +04:00
|
|
|
ifp->if_opackets++;
|
2005-01-11 21:24:24 +03:00
|
|
|
|
2005-11-18 19:42:22 +03:00
|
|
|
txq->queued--;
|
|
|
|
txq->next = (txq->next + 1) % txq->count;
|
2005-01-11 21:24:24 +03:00
|
|
|
}
|
|
|
|
|
2005-08-19 12:50:06 +04:00
|
|
|
sc->sc_tx_timer = 0;
|
|
|
|
ifp->if_flags &= ~IFF_OACTIVE;
|
2005-01-11 21:24:24 +03:00
|
|
|
|
|
|
|
/* Call start() since some buffer descriptors have been released */
|
|
|
|
(*ifp->if_start)(ifp);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
iwi_intr(void *arg)
|
|
|
|
{
|
|
|
|
struct iwi_softc *sc = arg;
|
2005-09-17 16:40:27 +04:00
|
|
|
uint32_t r;
|
2005-01-11 21:24:24 +03:00
|
|
|
|
|
|
|
if ((r = CSR_READ_4(sc, IWI_CSR_INTR)) == 0 || r == 0xffffffff)
|
|
|
|
return 0;
|
|
|
|
|
2005-09-29 23:57:36 +04:00
|
|
|
/* Acknowledge interrupts */
|
|
|
|
CSR_WRITE_4(sc, IWI_CSR_INTR, r);
|
2005-01-11 21:24:24 +03:00
|
|
|
|
2006-12-20 19:30:20 +03:00
|
|
|
if (r & IWI_INTR_FATAL_ERROR) {
|
2007-10-22 19:28:48 +04:00
|
|
|
aprint_error_dev(sc->sc_dev, "fatal error\n");
|
2005-08-01 19:14:54 +04:00
|
|
|
sc->sc_ic.ic_ifp->if_flags &= ~IFF_UP;
|
2005-06-22 10:14:51 +04:00
|
|
|
iwi_stop(&sc->sc_if, 1);
|
2005-11-26 10:42:10 +03:00
|
|
|
return (1);
|
2005-01-11 21:24:24 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
if (r & IWI_INTR_FW_INITED) {
|
|
|
|
if (!(r & (IWI_INTR_FATAL_ERROR | IWI_INTR_PARITY_ERROR)))
|
|
|
|
wakeup(sc);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (r & IWI_INTR_RADIO_OFF) {
|
|
|
|
DPRINTF(("radio transmitter off\n"));
|
2005-08-01 19:14:54 +04:00
|
|
|
sc->sc_ic.ic_ifp->if_flags &= ~IFF_UP;
|
2005-06-22 10:14:51 +04:00
|
|
|
iwi_stop(&sc->sc_if, 1);
|
2005-11-26 10:42:10 +03:00
|
|
|
return (1);
|
2005-01-11 21:24:24 +03:00
|
|
|
}
|
|
|
|
|
2005-08-19 12:50:06 +04:00
|
|
|
if (r & IWI_INTR_CMD_DONE)
|
2005-11-26 10:42:10 +03:00
|
|
|
iwi_cmd_intr(sc);
|
2005-01-11 21:24:24 +03:00
|
|
|
|
2005-08-19 12:50:06 +04:00
|
|
|
if (r & IWI_INTR_TX1_DONE)
|
2005-11-18 19:42:22 +03:00
|
|
|
iwi_tx_intr(sc, &sc->txq[0]);
|
|
|
|
|
|
|
|
if (r & IWI_INTR_TX2_DONE)
|
|
|
|
iwi_tx_intr(sc, &sc->txq[1]);
|
|
|
|
|
|
|
|
if (r & IWI_INTR_TX3_DONE)
|
|
|
|
iwi_tx_intr(sc, &sc->txq[2]);
|
|
|
|
|
|
|
|
if (r & IWI_INTR_TX4_DONE)
|
|
|
|
iwi_tx_intr(sc, &sc->txq[3]);
|
|
|
|
|
|
|
|
if (r & IWI_INTR_RX_DONE)
|
|
|
|
iwi_rx_intr(sc);
|
2005-01-11 21:24:24 +03:00
|
|
|
|
2007-10-22 19:28:48 +04:00
|
|
|
if (r & IWI_INTR_PARITY_ERROR)
|
|
|
|
aprint_error_dev(sc->sc_dev, "parity error\n");
|
2006-12-20 19:30:20 +03:00
|
|
|
|
2005-01-11 21:24:24 +03:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2005-09-17 16:40:27 +04:00
|
|
|
iwi_cmd(struct iwi_softc *sc, uint8_t type, void *data, uint8_t len,
|
2005-01-11 21:24:24 +03:00
|
|
|
int async)
|
|
|
|
{
|
|
|
|
struct iwi_cmd_desc *desc;
|
|
|
|
|
2005-08-19 12:50:06 +04:00
|
|
|
desc = &sc->cmdq.desc[sc->cmdq.cur];
|
2005-01-11 21:24:24 +03:00
|
|
|
|
|
|
|
desc->hdr.type = IWI_HDR_TYPE_COMMAND;
|
|
|
|
desc->hdr.flags = IWI_HDR_FLAG_IRQ;
|
|
|
|
desc->type = type;
|
|
|
|
desc->len = len;
|
|
|
|
memcpy(desc->data, data, len);
|
|
|
|
|
2005-08-19 12:50:06 +04:00
|
|
|
bus_dmamap_sync(sc->sc_dmat, sc->cmdq.desc_map,
|
2005-09-25 10:49:54 +04:00
|
|
|
sc->cmdq.cur * IWI_CMD_DESC_SIZE,
|
|
|
|
IWI_CMD_DESC_SIZE, BUS_DMASYNC_PREWRITE);
|
2005-01-11 21:24:24 +03:00
|
|
|
|
2006-12-20 19:30:20 +03:00
|
|
|
DPRINTFN(2, ("sending command idx=%u type=%u len=%u async=%d\n",
|
|
|
|
sc->cmdq.cur, type, len, async));
|
2005-08-19 12:50:06 +04:00
|
|
|
|
|
|
|
sc->cmdq.cur = (sc->cmdq.cur + 1) % sc->cmdq.count;
|
2006-12-20 19:30:20 +03:00
|
|
|
|
|
|
|
if (++sc->cmdq.queued == 1)
|
|
|
|
CSR_WRITE_4(sc, IWI_CSR_CMD_WIDX, sc->cmdq.cur);
|
2005-01-11 21:24:24 +03:00
|
|
|
|
2005-11-26 10:42:10 +03:00
|
|
|
return async ? 0 : tsleep(desc, 0, "iwicmd", hz);
|
2005-01-11 21:24:24 +03:00
|
|
|
}
|
|
|
|
|
2005-11-18 19:42:22 +03:00
|
|
|
static void
|
|
|
|
iwi_write_ibssnode(struct iwi_softc *sc, const struct iwi_node *in)
|
|
|
|
{
|
|
|
|
struct iwi_ibssnode node;
|
|
|
|
|
|
|
|
/* write node information into NIC memory */
|
|
|
|
memset(&node, 0, sizeof node);
|
|
|
|
IEEE80211_ADDR_COPY(node.bssid, in->in_node.ni_macaddr);
|
|
|
|
|
|
|
|
CSR_WRITE_REGION_1(sc,
|
|
|
|
IWI_CSR_NODE_BASE + in->in_station * sizeof node,
|
|
|
|
(uint8_t *)&node, sizeof node);
|
|
|
|
}
|
|
|
|
|
2005-01-11 21:24:24 +03:00
|
|
|
static int
|
2005-11-18 19:42:22 +03:00
|
|
|
iwi_tx_start(struct ifnet *ifp, struct mbuf *m0, struct ieee80211_node *ni,
|
|
|
|
int ac)
|
2005-01-11 21:24:24 +03:00
|
|
|
{
|
|
|
|
struct iwi_softc *sc = ifp->if_softc;
|
|
|
|
struct ieee80211com *ic = &sc->sc_ic;
|
2005-11-18 19:42:22 +03:00
|
|
|
struct iwi_node *in = (struct iwi_node *)ni;
|
|
|
|
struct ieee80211_frame *wh;
|
2005-07-31 01:15:51 +04:00
|
|
|
struct ieee80211_key *k;
|
2005-11-18 19:42:22 +03:00
|
|
|
const struct chanAccParams *cap;
|
|
|
|
struct iwi_tx_ring *txq = &sc->txq[ac];
|
2005-08-19 12:50:06 +04:00
|
|
|
struct iwi_tx_data *data;
|
2005-01-11 21:24:24 +03:00
|
|
|
struct iwi_tx_desc *desc;
|
|
|
|
struct mbuf *mnew;
|
2005-11-18 19:42:22 +03:00
|
|
|
int error, hdrlen, i, noack = 0;
|
2005-01-11 21:24:24 +03:00
|
|
|
|
2005-11-18 19:42:22 +03:00
|
|
|
wh = mtod(m0, struct ieee80211_frame *);
|
|
|
|
|
|
|
|
if (wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_QOS) {
|
|
|
|
hdrlen = sizeof (struct ieee80211_qosframe);
|
|
|
|
cap = &ic->ic_wme.wme_chanParams;
|
|
|
|
noack = cap->cap_wmeParams[ac].wmep_noackPolicy;
|
|
|
|
} else
|
|
|
|
hdrlen = sizeof (struct ieee80211_frame);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* This is only used in IBSS mode where the firmware expect an index
|
|
|
|
* in a h/w table instead of a destination address.
|
|
|
|
*/
|
|
|
|
if (ic->ic_opmode == IEEE80211_M_IBSS && in->in_station == -1) {
|
|
|
|
in->in_station = iwi_alloc_unr(sc);
|
|
|
|
|
|
|
|
if (in->in_station == -1) { /* h/w table is full */
|
|
|
|
m_freem(m0);
|
|
|
|
ieee80211_free_node(ni);
|
|
|
|
ifp->if_oerrors++;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
iwi_write_ibssnode(sc, in);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (wh->i_fc[1] & IEEE80211_FC1_WEP) {
|
2005-07-31 01:15:51 +04:00
|
|
|
k = ieee80211_crypto_encap(ic, ni, m0);
|
|
|
|
if (k == NULL) {
|
|
|
|
m_freem(m0);
|
|
|
|
return ENOBUFS;
|
|
|
|
}
|
2005-11-18 19:42:22 +03:00
|
|
|
|
|
|
|
/* packet header may have moved, reset our local pointer */
|
|
|
|
wh = mtod(m0, struct ieee80211_frame *);
|
2005-07-31 01:15:51 +04:00
|
|
|
}
|
|
|
|
|
2005-01-11 21:24:24 +03:00
|
|
|
#if NBPFILTER > 0
|
|
|
|
if (sc->sc_drvbpf != NULL) {
|
|
|
|
struct iwi_tx_radiotap_header *tap = &sc->sc_txtap;
|
|
|
|
|
|
|
|
tap->wt_flags = 0;
|
2005-07-31 01:15:51 +04:00
|
|
|
tap->wt_chan_freq = htole16(ic->ic_ibss_chan->ic_freq);
|
|
|
|
tap->wt_chan_flags = htole16(ic->ic_ibss_chan->ic_flags);
|
2005-01-11 21:24:24 +03:00
|
|
|
|
|
|
|
bpf_mtap2(sc->sc_drvbpf, tap, sc->sc_txtap_len, m0);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2005-11-18 19:42:22 +03:00
|
|
|
data = &txq->data[txq->cur];
|
|
|
|
desc = &txq->desc[txq->cur];
|
2005-01-11 21:24:24 +03:00
|
|
|
|
2005-11-18 19:42:22 +03:00
|
|
|
/* save and trim IEEE802.11 header */
|
2007-03-04 08:59:00 +03:00
|
|
|
m_copydata(m0, 0, hdrlen, (void *)&desc->wh);
|
2005-11-18 19:42:22 +03:00
|
|
|
m_adj(m0, hdrlen);
|
2005-01-11 21:24:24 +03:00
|
|
|
|
2005-10-29 14:48:02 +04:00
|
|
|
error = bus_dmamap_load_mbuf(sc->sc_dmat, data->map, m0,
|
|
|
|
BUS_DMA_WRITE | BUS_DMA_NOWAIT);
|
2005-01-11 21:24:24 +03:00
|
|
|
if (error != 0 && error != EFBIG) {
|
2007-10-22 19:28:48 +04:00
|
|
|
aprint_error_dev(sc->sc_dev, "could not map mbuf (error %d)\n",
|
|
|
|
error);
|
2005-01-11 21:24:24 +03:00
|
|
|
m_freem(m0);
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
if (error != 0) {
|
|
|
|
/* too many fragments, linearize */
|
|
|
|
|
|
|
|
MGETHDR(mnew, M_DONTWAIT, MT_DATA);
|
|
|
|
if (mnew == NULL) {
|
|
|
|
m_freem(m0);
|
|
|
|
return ENOMEM;
|
|
|
|
}
|
|
|
|
|
|
|
|
M_COPY_PKTHDR(mnew, m0);
|
|
|
|
|
2005-10-29 14:48:02 +04:00
|
|
|
/* If the data won't fit in the header, get a cluster */
|
|
|
|
if (m0->m_pkthdr.len > MHLEN) {
|
|
|
|
MCLGET(mnew, M_DONTWAIT);
|
|
|
|
if (!(mnew->m_flags & M_EXT)) {
|
|
|
|
m_freem(m0);
|
|
|
|
m_freem(mnew);
|
|
|
|
return ENOMEM;
|
|
|
|
}
|
|
|
|
}
|
2007-03-04 08:59:00 +03:00
|
|
|
m_copydata(m0, 0, m0->m_pkthdr.len, mtod(mnew, void *));
|
2005-01-11 21:24:24 +03:00
|
|
|
m_freem(m0);
|
|
|
|
mnew->m_len = mnew->m_pkthdr.len;
|
|
|
|
m0 = mnew;
|
|
|
|
|
2005-08-19 12:50:06 +04:00
|
|
|
error = bus_dmamap_load_mbuf(sc->sc_dmat, data->map, m0,
|
2005-10-29 14:48:02 +04:00
|
|
|
BUS_DMA_WRITE | BUS_DMA_NOWAIT);
|
2005-01-11 21:24:24 +03:00
|
|
|
if (error != 0) {
|
2007-10-22 19:28:48 +04:00
|
|
|
aprint_error_dev(sc->sc_dev,
|
|
|
|
"could not map mbuf (error %d)\n", error);
|
2005-01-11 21:24:24 +03:00
|
|
|
m_freem(m0);
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2005-08-19 12:50:06 +04:00
|
|
|
data->m = m0;
|
|
|
|
data->ni = ni;
|
2005-01-11 21:24:24 +03:00
|
|
|
|
|
|
|
desc->hdr.type = IWI_HDR_TYPE_DATA;
|
|
|
|
desc->hdr.flags = IWI_HDR_FLAG_IRQ;
|
2005-11-18 19:42:22 +03:00
|
|
|
desc->station =
|
|
|
|
(ic->ic_opmode == IEEE80211_M_IBSS) ? in->in_station : 0;
|
2005-01-11 21:24:24 +03:00
|
|
|
desc->cmd = IWI_DATA_CMD_TX;
|
|
|
|
desc->len = htole16(m0->m_pkthdr.len);
|
|
|
|
desc->flags = 0;
|
2005-11-18 19:42:22 +03:00
|
|
|
desc->xflags = 0;
|
|
|
|
|
|
|
|
if (!noack && !IEEE80211_IS_MULTICAST(desc->wh.i_addr1))
|
2005-01-11 21:24:24 +03:00
|
|
|
desc->flags |= IWI_DATA_FLAG_NEED_ACK;
|
|
|
|
|
2005-07-31 01:15:51 +04:00
|
|
|
#if 0
|
2005-01-11 21:24:24 +03:00
|
|
|
if (ic->ic_flags & IEEE80211_F_PRIVACY) {
|
2005-11-18 19:42:22 +03:00
|
|
|
desc->wh.i_fc[1] |= IEEE80211_FC1_WEP;
|
2005-07-31 01:15:51 +04:00
|
|
|
desc->wep_txkey = ic->ic_crypto.cs_def_txkey;
|
2005-01-11 21:24:24 +03:00
|
|
|
} else
|
2005-07-31 01:15:51 +04:00
|
|
|
#endif
|
2005-01-11 21:24:24 +03:00
|
|
|
desc->flags |= IWI_DATA_FLAG_NO_WEP;
|
|
|
|
|
|
|
|
if (ic->ic_flags & IEEE80211_F_SHPREAMBLE)
|
|
|
|
desc->flags |= IWI_DATA_FLAG_SHPREAMBLE;
|
|
|
|
|
2005-11-18 19:42:22 +03:00
|
|
|
if (desc->wh.i_fc[0] & IEEE80211_FC0_SUBTYPE_QOS)
|
|
|
|
desc->xflags |= IWI_DATA_XFLAG_QOS;
|
|
|
|
|
2006-12-20 19:30:20 +03:00
|
|
|
if (ic->ic_curmode == IEEE80211_MODE_11B)
|
|
|
|
desc->xflags |= IWI_DATA_XFLAG_CCK;
|
|
|
|
|
2005-08-19 12:50:06 +04:00
|
|
|
desc->nseg = htole32(data->map->dm_nsegs);
|
|
|
|
for (i = 0; i < data->map->dm_nsegs; i++) {
|
|
|
|
desc->seg_addr[i] = htole32(data->map->dm_segs[i].ds_addr);
|
2005-10-29 12:10:38 +04:00
|
|
|
desc->seg_len[i] = htole16(data->map->dm_segs[i].ds_len);
|
2005-01-11 21:24:24 +03:00
|
|
|
}
|
|
|
|
|
2005-11-18 19:42:22 +03:00
|
|
|
bus_dmamap_sync(sc->sc_dmat, txq->desc_map,
|
|
|
|
txq->cur * IWI_TX_DESC_SIZE,
|
2005-09-25 10:49:54 +04:00
|
|
|
IWI_TX_DESC_SIZE, BUS_DMASYNC_PREWRITE);
|
2005-01-11 21:24:24 +03:00
|
|
|
|
2005-10-29 12:10:38 +04:00
|
|
|
bus_dmamap_sync(sc->sc_dmat, data->map, 0, data->map->dm_mapsize,
|
2005-01-11 21:24:24 +03:00
|
|
|
BUS_DMASYNC_PREWRITE);
|
|
|
|
|
2005-11-18 19:42:22 +03:00
|
|
|
DPRINTFN(5, ("sending data frame txq=%u idx=%u len=%u nseg=%u\n",
|
|
|
|
ac, txq->cur, le16toh(desc->len), le32toh(desc->nseg)));
|
2005-01-11 21:24:24 +03:00
|
|
|
|
|
|
|
/* Inform firmware about this new packet */
|
2005-11-18 19:42:22 +03:00
|
|
|
txq->queued++;
|
|
|
|
txq->cur = (txq->cur + 1) % txq->count;
|
|
|
|
CSR_WRITE_4(sc, txq->csr_widx, txq->cur);
|
2005-01-11 21:24:24 +03:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
iwi_start(struct ifnet *ifp)
|
|
|
|
{
|
|
|
|
struct iwi_softc *sc = ifp->if_softc;
|
|
|
|
struct ieee80211com *ic = &sc->sc_ic;
|
|
|
|
struct mbuf *m0;
|
2005-07-31 01:15:51 +04:00
|
|
|
struct ether_header *eh;
|
2005-01-11 21:24:24 +03:00
|
|
|
struct ieee80211_node *ni;
|
2005-11-18 19:42:22 +03:00
|
|
|
int ac;
|
2005-01-11 21:24:24 +03:00
|
|
|
|
|
|
|
if (ic->ic_state != IEEE80211_S_RUN)
|
|
|
|
return;
|
|
|
|
|
|
|
|
for (;;) {
|
|
|
|
IF_DEQUEUE(&ifp->if_snd, m0);
|
|
|
|
if (m0 == NULL)
|
|
|
|
break;
|
|
|
|
|
2005-08-19 12:50:06 +04:00
|
|
|
if (m0->m_len < sizeof (struct ether_header) &&
|
2005-11-18 19:42:22 +03:00
|
|
|
(m0 = m_pullup(m0, sizeof (struct ether_header))) == NULL) {
|
|
|
|
ifp->if_oerrors++;
|
2005-09-25 10:49:54 +04:00
|
|
|
continue;
|
2005-11-18 19:42:22 +03:00
|
|
|
}
|
2005-08-19 12:50:06 +04:00
|
|
|
|
2005-07-31 01:15:51 +04:00
|
|
|
eh = mtod(m0, struct ether_header *);
|
|
|
|
ni = ieee80211_find_txnode(ic, eh->ether_dhost);
|
|
|
|
if (ni == NULL) {
|
2005-06-22 10:14:51 +04:00
|
|
|
m_freem(m0);
|
2005-11-18 19:42:22 +03:00
|
|
|
ifp->if_oerrors++;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* classify mbuf so we can find which tx ring to use */
|
|
|
|
if (ieee80211_classify(ic, m0, ni) != 0) {
|
|
|
|
m_freem(m0);
|
|
|
|
ieee80211_free_node(ni);
|
|
|
|
ifp->if_oerrors++;
|
2005-06-22 10:14:51 +04:00
|
|
|
continue;
|
|
|
|
}
|
2005-01-11 21:24:24 +03:00
|
|
|
|
2005-11-18 19:42:22 +03:00
|
|
|
/* no QoS encapsulation for EAPOL frames */
|
|
|
|
ac = (eh->ether_type != htons(ETHERTYPE_PAE)) ?
|
|
|
|
M_WME_GETAC(m0) : WME_AC_BE;
|
|
|
|
|
|
|
|
if (sc->txq[ac].queued > sc->txq[ac].count - 8) {
|
|
|
|
/* there is no place left in this ring */
|
|
|
|
IF_PREPEND(&ifp->if_snd, m0);
|
|
|
|
ifp->if_flags |= IFF_OACTIVE;
|
|
|
|
break;
|
|
|
|
}
|
2005-11-23 23:08:29 +03:00
|
|
|
|
|
|
|
#if NBPFILTER > 0
|
|
|
|
if (ifp->if_bpf != NULL)
|
|
|
|
bpf_mtap(ifp->if_bpf, m0);
|
2005-11-18 19:42:22 +03:00
|
|
|
#endif
|
2005-11-23 23:08:29 +03:00
|
|
|
|
2005-07-31 01:15:51 +04:00
|
|
|
m0 = ieee80211_encap(ic, m0, ni);
|
|
|
|
if (m0 == NULL) {
|
|
|
|
ieee80211_free_node(ni);
|
2005-11-18 19:42:22 +03:00
|
|
|
ifp->if_oerrors++;
|
2005-07-31 01:15:51 +04:00
|
|
|
continue;
|
|
|
|
}
|
2005-01-11 21:24:24 +03:00
|
|
|
|
2005-09-25 10:49:54 +04:00
|
|
|
#if NBPFILTER > 0
|
|
|
|
if (ic->ic_rawbpf != NULL)
|
|
|
|
bpf_mtap(ic->ic_rawbpf, m0);
|
|
|
|
#endif
|
|
|
|
|
2005-11-18 19:42:22 +03:00
|
|
|
if (iwi_tx_start(ifp, m0, ni, ac) != 0) {
|
2005-07-31 01:15:51 +04:00
|
|
|
ieee80211_free_node(ni);
|
|
|
|
ifp->if_oerrors++;
|
2005-01-11 21:24:24 +03:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* start watchdog timer */
|
|
|
|
sc->sc_tx_timer = 5;
|
|
|
|
ifp->if_timer = 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
iwi_watchdog(struct ifnet *ifp)
|
|
|
|
{
|
|
|
|
struct iwi_softc *sc = ifp->if_softc;
|
|
|
|
|
|
|
|
ifp->if_timer = 0;
|
|
|
|
|
|
|
|
if (sc->sc_tx_timer > 0) {
|
|
|
|
if (--sc->sc_tx_timer == 0) {
|
2007-10-22 19:28:48 +04:00
|
|
|
aprint_error_dev(sc->sc_dev, "device timeout\n");
|
2005-07-31 01:15:51 +04:00
|
|
|
ifp->if_oerrors++;
|
|
|
|
ifp->if_flags &= ~IFF_UP;
|
2005-01-11 21:24:24 +03:00
|
|
|
iwi_stop(ifp, 1);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
ifp->if_timer = 1;
|
|
|
|
}
|
|
|
|
|
2005-06-22 10:14:51 +04:00
|
|
|
ieee80211_watchdog(&sc->sc_ic);
|
2005-01-11 21:24:24 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2005-09-17 16:40:27 +04:00
|
|
|
iwi_get_table0(struct iwi_softc *sc, uint32_t *tbl)
|
2005-01-11 21:24:24 +03:00
|
|
|
{
|
2005-09-17 16:40:27 +04:00
|
|
|
uint32_t size, buf[128];
|
2005-01-11 21:24:24 +03:00
|
|
|
|
|
|
|
if (!(sc->flags & IWI_FLAG_FW_INITED)) {
|
|
|
|
memset(buf, 0, sizeof buf);
|
|
|
|
return copyout(buf, tbl, sizeof buf);
|
|
|
|
}
|
|
|
|
|
|
|
|
size = min(CSR_READ_4(sc, IWI_CSR_TABLE0_SIZE), 128 - 1);
|
|
|
|
CSR_READ_REGION_4(sc, IWI_CSR_TABLE0_BASE, &buf[1], size);
|
|
|
|
|
|
|
|
return copyout(buf, tbl, sizeof buf);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2007-03-04 08:59:00 +03:00
|
|
|
iwi_ioctl(struct ifnet *ifp, u_long cmd, void *data)
|
2005-01-11 21:24:24 +03:00
|
|
|
{
|
2005-12-05 12:24:54 +03:00
|
|
|
#define IS_RUNNING(ifp) \
|
|
|
|
((ifp->if_flags & IFF_UP) && (ifp->if_flags & IFF_RUNNING))
|
|
|
|
|
2005-01-11 21:24:24 +03:00
|
|
|
struct iwi_softc *sc = ifp->if_softc;
|
2005-11-18 19:42:22 +03:00
|
|
|
struct ieee80211com *ic = &sc->sc_ic;
|
2005-12-05 12:24:54 +03:00
|
|
|
struct ifreq *ifr = (struct ifreq *)data;
|
2005-01-11 21:24:24 +03:00
|
|
|
int s, error = 0;
|
2006-08-19 10:32:52 +04:00
|
|
|
int val;
|
2005-01-11 21:24:24 +03:00
|
|
|
|
|
|
|
s = splnet();
|
|
|
|
|
|
|
|
switch (cmd) {
|
|
|
|
case SIOCSIFFLAGS:
|
*** Summary ***
When a link-layer address changes (e.g., ifconfig ex0 link
02:de:ad:be:ef:02 active), send a gratuitous ARP and/or a Neighbor
Advertisement to update the network-/link-layer address bindings
on our LAN peers.
Refuse a change of ethernet address to the address 00:00:00:00:00:00
or to any multicast/broadcast address. (Thanks matt@.)
Reorder ifnet ioctl operations so that driver ioctls may inherit
the functions of their "class"---ether_ioctl(), fddi_ioctl(), et
cetera---and the class ioctls may inherit from the generic ioctl,
ifioctl_common(), but both driver- and class-ioctls may override
the generic behavior. Make network drivers share more code.
Distinguish a "factory" link-layer address from others for the
purposes of both protecting that address from deletion and computing
EUI64.
Return consistent, appropriate error codes from network drivers.
Improve readability. KNF.
*** Details ***
In if_attach(), always initialize the interface ioctl routine,
ifnet->if_ioctl, if the driver has not already initialized it.
Delete if_ioctl == NULL tests everywhere else, because it cannot
happen.
In the ioctl routines of network interfaces, inherit common ioctl
behaviors by calling either ifioctl_common() or whichever ioctl
routine is appropriate for the class of interface---e.g., ether_ioctl()
for ethernets.
Stop (ab)using SIOCSIFADDR and start to use SIOCINITIFADDR. In
the user->kernel interface, SIOCSIFADDR's argument was an ifreq,
but on the protocol->ifnet interface, SIOCSIFADDR's argument was
an ifaddr. That was confusing, and it would work against me as I
make it possible for a network interface to overload most ioctls.
On the protocol->ifnet interface, replace SIOCSIFADDR with
SIOCINITIFADDR. In ifioctl(), return EPERM if userland tries to
invoke SIOCINITIFADDR.
In ifioctl(), give the interface the first shot at handling most
interface ioctls, and give the protocol the second shot, instead
of the other way around. Finally, let compatibility code (COMPAT_OSOCK)
take a shot.
Pull device initialization out of switch statements under
SIOCINITIFADDR. For example, pull ..._init() out of any switch
statement that looks like this:
switch (...->sa_family) {
case ...:
..._init();
...
break;
...
default:
..._init();
...
break;
}
Rewrite many if-else clauses that handle all permutations of IFF_UP
and IFF_RUNNING to use a switch statement,
switch (x & (IFF_UP|IFF_RUNNING)) {
case 0:
...
break;
case IFF_RUNNING:
...
break;
case IFF_UP:
...
break;
case IFF_UP|IFF_RUNNING:
...
break;
}
unifdef lots of code containing #ifdef FreeBSD, #ifdef NetBSD, and
#ifdef SIOCSIFMTU, especially in fwip(4) and in ndis(4).
In ipw(4), remove an if_set_sadl() call that is out of place.
In nfe(4), reuse the jumbo MTU logic in ether_ioctl().
Let ethernets register a callback for setting h/w state such as
promiscuous mode and the multicast filter in accord with a change
in the if_flags: ether_set_ifflags_cb() registers a callback that
returns ENETRESET if the caller should reset the ethernet by calling
if_init(), 0 on success, != 0 on failure. Pull common code from
ex(4), gem(4), nfe(4), sip(4), tlp(4), vge(4) into ether_ioctl(),
and register if_flags callbacks for those drivers.
Return ENOTTY instead of EINVAL for inappropriate ioctls. In
zyd(4), use ENXIO instead of ENOTTY to indicate that the device is
not any longer attached.
Add to if_set_sadl() a boolean 'factory' argument that indicates
whether a link-layer address was assigned by the factory or some
other source. In a comment, recommend using the factory address
for generating an EUI64, and update in6_get_hw_ifid() to prefer a
factory address to any other link-layer address.
Add a routing message, RTM_LLINFO_UPD, that tells protocols to
update the binding of network-layer addresses to link-layer addresses.
Implement this message in IPv4 and IPv6 by sending a gratuitous
ARP or a neighbor advertisement, respectively. Generate RTM_LLINFO_UPD
messages on a change of an interface's link-layer address.
In ether_ioctl(), do not let SIOCALIFADDR set a link-layer address
that is broadcast/multicast or equal to 00:00:00:00:00:00.
Make ether_ioctl() call ifioctl_common() to handle ioctls that it
does not understand.
In gif(4), initialize if_softc and use it, instead of assuming that
the gif_softc and ifp overlap.
Let ifioctl_common() handle SIOCGIFADDR.
Sprinkle rtcache_invariants(), which checks on DIAGNOSTIC kernels
that certain invariants on a struct route are satisfied.
In agr(4), rewrite agr_ioctl_filter() to be a bit more explicit
about the ioctls that we do not allow on an agr(4) member interface.
bzero -> memset. Delete unnecessary casts to void *. Use
sockaddr_in_init() and sockaddr_in6_init(). Compare pointers with
NULL instead of "testing truth". Replace some instances of (type
*)0 with NULL. Change some K&R prototypes to ANSI C, and join
lines.
2008-11-07 03:20:01 +03:00
|
|
|
if ((error = ifioctl_common(ifp, cmd, data)) != 0)
|
|
|
|
break;
|
2005-01-11 21:24:24 +03:00
|
|
|
if (ifp->if_flags & IFF_UP) {
|
|
|
|
if (!(ifp->if_flags & IFF_RUNNING))
|
|
|
|
iwi_init(ifp);
|
|
|
|
} else {
|
|
|
|
if (ifp->if_flags & IFF_RUNNING)
|
|
|
|
iwi_stop(ifp, 1);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
2005-12-05 12:24:54 +03:00
|
|
|
case SIOCADDMULTI:
|
|
|
|
case SIOCDELMULTI:
|
2007-09-01 11:32:22 +04:00
|
|
|
/* XXX no h/w multicast filter? --dyoung */
|
|
|
|
if ((error = ether_ioctl(ifp, cmd, data)) == ENETRESET) {
|
2005-12-05 12:24:54 +03:00
|
|
|
/* setup multicast filter, etc */
|
|
|
|
error = 0;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
2005-01-11 21:24:24 +03:00
|
|
|
case SIOCGTABLE0:
|
2005-09-17 16:40:27 +04:00
|
|
|
error = iwi_get_table0(sc, (uint32_t *)ifr->ifr_data);
|
2005-01-11 21:24:24 +03:00
|
|
|
break;
|
|
|
|
|
|
|
|
case SIOCGRADIO:
|
2006-08-19 10:32:52 +04:00
|
|
|
val = !iwi_getrfkill(sc);
|
|
|
|
error = copyout(&val, (int *)ifr->ifr_data, sizeof val);
|
2005-01-11 21:24:24 +03:00
|
|
|
break;
|
|
|
|
|
2006-08-09 15:35:59 +04:00
|
|
|
case SIOCSIFMEDIA:
|
|
|
|
if (ifr->ifr_media & IFM_IEEE80211_ADHOC) {
|
2008-11-07 17:58:27 +03:00
|
|
|
sc->sc_fwname = "ipw2200-ibss.fw";
|
2006-08-09 15:35:59 +04:00
|
|
|
} else if (ifr->ifr_media & IFM_IEEE80211_MONITOR) {
|
2008-11-07 17:58:27 +03:00
|
|
|
sc->sc_fwname = "ipw2200-sniffer.fw";
|
2006-08-09 15:35:59 +04:00
|
|
|
} else {
|
2008-11-07 17:58:27 +03:00
|
|
|
sc->sc_fwname = "ipw2200-bss.fw";
|
2006-08-09 15:35:59 +04:00
|
|
|
}
|
|
|
|
error = iwi_cache_firmware(sc);
|
|
|
|
if (error)
|
|
|
|
break;
|
|
|
|
/* FALLTRHOUGH */
|
2005-01-11 21:24:24 +03:00
|
|
|
|
|
|
|
default:
|
2005-06-22 10:14:51 +04:00
|
|
|
error = ieee80211_ioctl(&sc->sc_ic, cmd, data);
|
2005-01-11 21:24:24 +03:00
|
|
|
|
2005-12-05 12:24:54 +03:00
|
|
|
if (error == ENETRESET) {
|
|
|
|
if (IS_RUNNING(ifp) &&
|
|
|
|
(ic->ic_roaming != IEEE80211_ROAMING_MANUAL))
|
|
|
|
iwi_init(ifp);
|
|
|
|
error = 0;
|
|
|
|
}
|
2005-01-11 21:24:24 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
splx(s);
|
|
|
|
return error;
|
2005-12-05 12:24:54 +03:00
|
|
|
#undef IS_RUNNING
|
2005-01-11 21:24:24 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
iwi_stop_master(struct iwi_softc *sc)
|
|
|
|
{
|
|
|
|
int ntries;
|
|
|
|
|
|
|
|
/* Disable interrupts */
|
|
|
|
CSR_WRITE_4(sc, IWI_CSR_INTR_MASK, 0);
|
|
|
|
|
|
|
|
CSR_WRITE_4(sc, IWI_CSR_RST, IWI_RST_STOP_MASTER);
|
|
|
|
for (ntries = 0; ntries < 5; ntries++) {
|
|
|
|
if (CSR_READ_4(sc, IWI_CSR_RST) & IWI_RST_MASTER_DISABLED)
|
|
|
|
break;
|
|
|
|
DELAY(10);
|
|
|
|
}
|
|
|
|
if (ntries == 5)
|
2007-10-22 19:28:48 +04:00
|
|
|
aprint_error_dev(sc->sc_dev, "timeout waiting for master\n");
|
2005-01-11 21:24:24 +03:00
|
|
|
|
|
|
|
CSR_WRITE_4(sc, IWI_CSR_RST, CSR_READ_4(sc, IWI_CSR_RST) |
|
|
|
|
IWI_RST_PRINCETON_RESET);
|
|
|
|
|
|
|
|
sc->flags &= ~IWI_FLAG_FW_INITED;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
iwi_reset(struct iwi_softc *sc)
|
|
|
|
{
|
|
|
|
int i, ntries;
|
|
|
|
|
|
|
|
iwi_stop_master(sc);
|
|
|
|
|
|
|
|
/* Move adapter to D0 state */
|
|
|
|
CSR_WRITE_4(sc, IWI_CSR_CTL, CSR_READ_4(sc, IWI_CSR_CTL) |
|
|
|
|
IWI_CTL_INIT);
|
|
|
|
|
|
|
|
/* Initialize Phase-Locked Level (PLL) */
|
|
|
|
CSR_WRITE_4(sc, IWI_CSR_READ_INT, IWI_READ_INT_INIT_HOST);
|
|
|
|
|
|
|
|
/* Wait for clock stabilization */
|
|
|
|
for (ntries = 0; ntries < 1000; ntries++) {
|
|
|
|
if (CSR_READ_4(sc, IWI_CSR_CTL) & IWI_CTL_CLOCK_READY)
|
|
|
|
break;
|
|
|
|
DELAY(200);
|
|
|
|
}
|
2005-09-25 10:49:54 +04:00
|
|
|
if (ntries == 1000) {
|
2007-10-22 19:28:48 +04:00
|
|
|
aprint_error_dev(sc->sc_dev,
|
|
|
|
"timeout waiting for clock stabilization\n");
|
2006-12-20 19:30:20 +03:00
|
|
|
return ETIMEDOUT;
|
2005-09-25 10:49:54 +04:00
|
|
|
}
|
2005-01-11 21:24:24 +03:00
|
|
|
|
|
|
|
CSR_WRITE_4(sc, IWI_CSR_RST, CSR_READ_4(sc, IWI_CSR_RST) |
|
|
|
|
IWI_RST_SW_RESET);
|
|
|
|
|
|
|
|
DELAY(10);
|
|
|
|
|
|
|
|
CSR_WRITE_4(sc, IWI_CSR_CTL, CSR_READ_4(sc, IWI_CSR_CTL) |
|
|
|
|
IWI_CTL_INIT);
|
|
|
|
|
|
|
|
/* Clear NIC memory */
|
|
|
|
CSR_WRITE_4(sc, IWI_CSR_AUTOINC_ADDR, 0);
|
|
|
|
for (i = 0; i < 0xc000; i++)
|
|
|
|
CSR_WRITE_4(sc, IWI_CSR_AUTOINC_DATA, 0);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
iwi_load_ucode(struct iwi_softc *sc, void *uc, int size)
|
|
|
|
{
|
2005-09-17 16:40:27 +04:00
|
|
|
uint16_t *w;
|
2005-01-11 21:24:24 +03:00
|
|
|
int ntries, i;
|
|
|
|
|
|
|
|
CSR_WRITE_4(sc, IWI_CSR_RST, CSR_READ_4(sc, IWI_CSR_RST) |
|
|
|
|
IWI_RST_STOP_MASTER);
|
|
|
|
for (ntries = 0; ntries < 5; ntries++) {
|
|
|
|
if (CSR_READ_4(sc, IWI_CSR_RST) & IWI_RST_MASTER_DISABLED)
|
|
|
|
break;
|
|
|
|
DELAY(10);
|
|
|
|
}
|
|
|
|
if (ntries == 5) {
|
2007-10-22 19:28:48 +04:00
|
|
|
aprint_error_dev(sc->sc_dev, "timeout waiting for master\n");
|
2006-12-20 19:30:20 +03:00
|
|
|
return ETIMEDOUT;
|
2005-01-11 21:24:24 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
MEM_WRITE_4(sc, 0x3000e0, 0x80000000);
|
|
|
|
DELAY(5000);
|
|
|
|
CSR_WRITE_4(sc, IWI_CSR_RST, CSR_READ_4(sc, IWI_CSR_RST) &
|
|
|
|
~IWI_RST_PRINCETON_RESET);
|
|
|
|
DELAY(5000);
|
|
|
|
MEM_WRITE_4(sc, 0x3000e0, 0);
|
|
|
|
DELAY(1000);
|
|
|
|
MEM_WRITE_4(sc, 0x300004, 1);
|
|
|
|
DELAY(1000);
|
|
|
|
MEM_WRITE_4(sc, 0x300004, 0);
|
|
|
|
DELAY(1000);
|
|
|
|
MEM_WRITE_1(sc, 0x200000, 0x00);
|
|
|
|
MEM_WRITE_1(sc, 0x200000, 0x40);
|
2005-08-19 12:50:06 +04:00
|
|
|
DELAY(1000);
|
2005-01-11 21:24:24 +03:00
|
|
|
|
|
|
|
/* Adapter is buggy, we must set the address for each word */
|
|
|
|
for (w = uc; size > 0; w++, size -= 2)
|
2005-10-29 12:10:38 +04:00
|
|
|
MEM_WRITE_2(sc, 0x200010, htole16(*w));
|
2005-01-11 21:24:24 +03:00
|
|
|
|
|
|
|
MEM_WRITE_1(sc, 0x200000, 0x00);
|
|
|
|
MEM_WRITE_1(sc, 0x200000, 0x80);
|
|
|
|
|
|
|
|
/* Wait until we get a response in the uc queue */
|
|
|
|
for (ntries = 0; ntries < 100; ntries++) {
|
|
|
|
if (MEM_READ_1(sc, 0x200000) & 1)
|
|
|
|
break;
|
|
|
|
DELAY(100);
|
|
|
|
}
|
|
|
|
if (ntries == 100) {
|
2007-10-22 19:28:48 +04:00
|
|
|
aprint_error_dev(sc->sc_dev,
|
|
|
|
"timeout waiting for ucode to initialize\n");
|
2006-12-20 19:30:20 +03:00
|
|
|
return ETIMEDOUT;
|
2005-01-11 21:24:24 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Empty the uc queue or the firmware will not initialize properly */
|
|
|
|
for (i = 0; i < 7; i++)
|
|
|
|
MEM_READ_4(sc, 0x200004);
|
|
|
|
|
|
|
|
MEM_WRITE_1(sc, 0x200000, 0x00);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* macro to handle unaligned little endian data in firmware image */
|
|
|
|
#define GETLE32(p) ((p)[0] | (p)[1] << 8 | (p)[2] << 16 | (p)[3] << 24)
|
|
|
|
static int
|
|
|
|
iwi_load_firmware(struct iwi_softc *sc, void *fw, int size)
|
|
|
|
{
|
|
|
|
bus_dmamap_t map;
|
|
|
|
u_char *p, *end;
|
2005-09-25 19:18:21 +04:00
|
|
|
uint32_t sentinel, ctl, sum;
|
|
|
|
uint32_t cs, sl, cd, cl;
|
2005-01-11 21:24:24 +03:00
|
|
|
int ntries, nsegs, error;
|
2005-09-25 19:18:21 +04:00
|
|
|
int sn;
|
2005-01-11 21:24:24 +03:00
|
|
|
|
2008-06-16 10:19:24 +04:00
|
|
|
nsegs = atop((char*)fw+size-1) - atop((char *)fw) + 1;
|
2005-09-25 19:18:21 +04:00
|
|
|
|
2005-09-25 19:29:37 +04:00
|
|
|
/* Create a DMA map for the firmware image */
|
2005-09-25 19:18:21 +04:00
|
|
|
error = bus_dmamap_create(sc->sc_dmat, size, nsegs, size, 0,
|
2005-01-11 21:24:24 +03:00
|
|
|
BUS_DMA_NOWAIT, &map);
|
|
|
|
if (error != 0) {
|
2007-10-22 19:28:48 +04:00
|
|
|
aprint_error_dev(sc->sc_dev,
|
|
|
|
"could not create firmware DMA map\n");
|
2005-01-11 21:24:24 +03:00
|
|
|
goto fail1;
|
|
|
|
}
|
|
|
|
|
2005-09-25 19:18:21 +04:00
|
|
|
error = bus_dmamap_load(sc->sc_dmat, map, fw, size, NULL,
|
|
|
|
BUS_DMA_NOWAIT | BUS_DMA_WRITE);
|
2005-01-11 21:24:24 +03:00
|
|
|
if (error != 0) {
|
2007-10-22 19:28:48 +04:00
|
|
|
aprint_error_dev(sc->sc_dev, "could not load fw dma map(%d)\n",
|
|
|
|
error);
|
2005-01-11 21:24:24 +03:00
|
|
|
goto fail2;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Make sure the adapter will get up-to-date values */
|
|
|
|
bus_dmamap_sync(sc->sc_dmat, map, 0, size, BUS_DMASYNC_PREWRITE);
|
|
|
|
|
|
|
|
/* Tell the adapter where the command blocks are stored */
|
|
|
|
MEM_WRITE_4(sc, 0x3000a0, 0x27000);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Store command blocks into adapter's internal memory using register
|
|
|
|
* indirections. The adapter will read the firmware image through DMA
|
|
|
|
* using information stored in command blocks.
|
|
|
|
*/
|
2005-09-25 19:18:21 +04:00
|
|
|
p = fw;
|
2005-01-11 21:24:24 +03:00
|
|
|
end = p + size;
|
|
|
|
CSR_WRITE_4(sc, IWI_CSR_AUTOINC_ADDR, 0x27000);
|
|
|
|
|
2005-09-25 19:18:21 +04:00
|
|
|
sn = 0;
|
|
|
|
sl = cl = 0;
|
|
|
|
cs = cd = 0;
|
2005-01-11 21:24:24 +03:00
|
|
|
while (p < end) {
|
2005-09-25 19:18:21 +04:00
|
|
|
if (sl == 0) {
|
|
|
|
cs = map->dm_segs[sn].ds_addr;
|
|
|
|
sl = map->dm_segs[sn].ds_len;
|
|
|
|
sn++;
|
|
|
|
}
|
|
|
|
if (cl == 0) {
|
|
|
|
cd = GETLE32(p); p += 4; cs += 4; sl -= 4;
|
|
|
|
cl = GETLE32(p); p += 4; cs += 4; sl -= 4;
|
|
|
|
}
|
|
|
|
while (sl > 0 && cl > 0) {
|
|
|
|
int len = min(cl, sl);
|
|
|
|
|
|
|
|
sl -= len;
|
|
|
|
cl -= len;
|
|
|
|
p += len;
|
2005-01-11 21:24:24 +03:00
|
|
|
|
2005-09-25 19:18:21 +04:00
|
|
|
while (len > 0) {
|
|
|
|
int mlen = min(len, IWI_CB_MAXDATALEN);
|
2005-01-11 21:24:24 +03:00
|
|
|
|
2005-09-25 19:18:21 +04:00
|
|
|
ctl = IWI_CB_DEFAULT_CTL | mlen;
|
|
|
|
sum = ctl ^ cs ^ cd;
|
2005-01-11 21:24:24 +03:00
|
|
|
|
2005-09-25 19:18:21 +04:00
|
|
|
/* Write a command block */
|
|
|
|
CSR_WRITE_4(sc, IWI_CSR_AUTOINC_DATA, ctl);
|
|
|
|
CSR_WRITE_4(sc, IWI_CSR_AUTOINC_DATA, cs);
|
|
|
|
CSR_WRITE_4(sc, IWI_CSR_AUTOINC_DATA, cd);
|
|
|
|
CSR_WRITE_4(sc, IWI_CSR_AUTOINC_DATA, sum);
|
2005-01-11 21:24:24 +03:00
|
|
|
|
2005-09-25 19:18:21 +04:00
|
|
|
cs += mlen;
|
|
|
|
cd += mlen;
|
|
|
|
len -= mlen;
|
|
|
|
}
|
2005-01-11 21:24:24 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Write a fictive final command block (sentinel) */
|
|
|
|
sentinel = CSR_READ_4(sc, IWI_CSR_AUTOINC_ADDR);
|
|
|
|
CSR_WRITE_4(sc, IWI_CSR_AUTOINC_DATA, 0);
|
|
|
|
|
|
|
|
CSR_WRITE_4(sc, IWI_CSR_RST, CSR_READ_4(sc, IWI_CSR_RST) &
|
|
|
|
~(IWI_RST_MASTER_DISABLED | IWI_RST_STOP_MASTER));
|
|
|
|
|
|
|
|
/* Tell the adapter to start processing command blocks */
|
|
|
|
MEM_WRITE_4(sc, 0x3000a4, 0x540100);
|
|
|
|
|
|
|
|
/* Wait until the adapter has processed all command blocks */
|
|
|
|
for (ntries = 0; ntries < 400; ntries++) {
|
|
|
|
if (MEM_READ_4(sc, 0x3000d0) >= sentinel)
|
|
|
|
break;
|
|
|
|
DELAY(100);
|
|
|
|
}
|
|
|
|
if (ntries == 400) {
|
2007-10-22 19:28:48 +04:00
|
|
|
aprint_error_dev(sc->sc_dev, "timeout processing cb\n");
|
2006-12-20 19:30:20 +03:00
|
|
|
error = ETIMEDOUT;
|
2005-11-26 10:42:10 +03:00
|
|
|
goto fail3;
|
2005-01-11 21:24:24 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
/* We're done with command blocks processing */
|
|
|
|
MEM_WRITE_4(sc, 0x3000a4, 0x540c00);
|
|
|
|
|
|
|
|
/* Allow interrupts so we know when the firmware is inited */
|
|
|
|
CSR_WRITE_4(sc, IWI_CSR_INTR_MASK, IWI_INTR_MASK);
|
|
|
|
|
|
|
|
/* Tell the adapter to initialize the firmware */
|
|
|
|
CSR_WRITE_4(sc, IWI_CSR_RST, 0);
|
|
|
|
CSR_WRITE_4(sc, IWI_CSR_CTL, CSR_READ_4(sc, IWI_CSR_CTL) |
|
|
|
|
IWI_CTL_ALLOW_STANDBY);
|
|
|
|
|
|
|
|
/* Wait at most one second for firmware initialization to complete */
|
|
|
|
if ((error = tsleep(sc, 0, "iwiinit", hz)) != 0) {
|
2007-10-22 19:28:48 +04:00
|
|
|
aprint_error_dev(sc->sc_dev,
|
|
|
|
"timeout waiting for firmware initialization to complete\n");
|
2005-09-25 19:18:21 +04:00
|
|
|
goto fail3;
|
2005-01-11 21:24:24 +03:00
|
|
|
}
|
|
|
|
|
2005-09-25 19:18:21 +04:00
|
|
|
fail3:
|
|
|
|
bus_dmamap_sync(sc->sc_dmat, map, 0, size, BUS_DMASYNC_POSTWRITE);
|
2005-01-11 21:24:24 +03:00
|
|
|
bus_dmamap_unload(sc->sc_dmat, map);
|
2005-09-25 19:18:21 +04:00
|
|
|
fail2:
|
|
|
|
bus_dmamap_destroy(sc->sc_dmat, map);
|
2005-02-27 03:26:58 +03:00
|
|
|
|
2005-09-25 19:18:21 +04:00
|
|
|
fail1:
|
|
|
|
return error;
|
2005-01-11 21:24:24 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Store firmware into kernel memory so we can download it when we need to,
|
|
|
|
* e.g when the adapter wakes up from suspend mode.
|
|
|
|
*/
|
|
|
|
static int
|
2006-08-09 15:35:59 +04:00
|
|
|
iwi_cache_firmware(struct iwi_softc *sc)
|
2005-01-11 21:24:24 +03:00
|
|
|
{
|
|
|
|
struct iwi_firmware *kfw = &sc->fw;
|
2006-08-09 15:35:59 +04:00
|
|
|
firmware_handle_t fwh;
|
2006-12-20 19:30:20 +03:00
|
|
|
const struct iwi_firmware_hdr *hdr;
|
|
|
|
off_t size;
|
|
|
|
char *fw;
|
2005-01-11 21:24:24 +03:00
|
|
|
int error;
|
|
|
|
|
2008-10-30 03:27:31 +03:00
|
|
|
if (iwi_accept_eula == 0) {
|
|
|
|
aprint_error_dev(sc->sc_dev,
|
|
|
|
"EULA not accepted, can't load firmware\n");
|
|
|
|
return EPERM;
|
|
|
|
}
|
|
|
|
|
2005-01-11 21:24:24 +03:00
|
|
|
iwi_free_firmware(sc);
|
2006-12-20 19:30:20 +03:00
|
|
|
error = firmware_open("if_iwi", sc->sc_fwname, &fwh);
|
|
|
|
if (error != 0) {
|
2007-10-22 19:28:48 +04:00
|
|
|
aprint_error_dev(sc->sc_dev, "firmware_open failed\n");
|
2006-12-20 19:30:20 +03:00
|
|
|
goto fail1;
|
|
|
|
}
|
|
|
|
|
|
|
|
size = firmware_get_size(fwh);
|
|
|
|
if (size < sizeof(struct iwi_firmware_hdr)) {
|
2007-10-22 19:28:48 +04:00
|
|
|
aprint_error_dev(sc->sc_dev, "image '%s' has no header\n",
|
|
|
|
sc->sc_fwname);
|
2006-12-20 19:30:20 +03:00
|
|
|
error = EIO;
|
2005-01-11 21:24:24 +03:00
|
|
|
goto fail1;
|
2006-12-20 19:30:20 +03:00
|
|
|
}
|
2005-01-11 21:24:24 +03:00
|
|
|
|
2006-12-20 19:30:20 +03:00
|
|
|
sc->sc_blob = firmware_malloc(size);
|
|
|
|
if (sc->sc_blob == NULL) {
|
2005-01-11 21:24:24 +03:00
|
|
|
error = ENOMEM;
|
2006-08-09 15:35:59 +04:00
|
|
|
firmware_close(fwh);
|
2005-01-11 21:24:24 +03:00
|
|
|
goto fail1;
|
|
|
|
}
|
|
|
|
|
2006-12-20 19:30:20 +03:00
|
|
|
error = firmware_read(fwh, 0, sc->sc_blob, size);
|
2006-08-09 15:35:59 +04:00
|
|
|
firmware_close(fwh);
|
|
|
|
if (error != 0)
|
|
|
|
goto fail2;
|
|
|
|
|
|
|
|
|
2006-12-20 19:30:20 +03:00
|
|
|
hdr = (const struct iwi_firmware_hdr *)sc->sc_blob;
|
|
|
|
if (size < sizeof(struct iwi_firmware_hdr) + hdr->bsize + hdr->usize + hdr->fsize) {
|
2007-10-22 19:28:48 +04:00
|
|
|
aprint_error_dev(sc->sc_dev, "image '%s' too small\n",
|
|
|
|
sc->sc_fwname);
|
2006-12-20 19:30:20 +03:00
|
|
|
error = EIO;
|
2005-01-11 21:24:24 +03:00
|
|
|
goto fail2;
|
|
|
|
}
|
|
|
|
|
2006-12-20 19:30:20 +03:00
|
|
|
hdr = (const struct iwi_firmware_hdr *)sc->sc_blob;
|
2007-03-04 22:14:25 +03:00
|
|
|
DPRINTF(("firmware version = %d\n", le32toh(hdr->version)));
|
2006-12-20 19:30:20 +03:00
|
|
|
if ((IWI_FW_GET_MAJOR(le32toh(hdr->version)) != IWI_FW_REQ_MAJOR) ||
|
|
|
|
(IWI_FW_GET_MINOR(le32toh(hdr->version)) != IWI_FW_REQ_MINOR)) {
|
2007-10-22 19:28:48 +04:00
|
|
|
aprint_error_dev(sc->sc_dev,
|
|
|
|
"version for '%s' %d.%d != %d.%d\n", sc->sc_fwname,
|
2006-12-20 19:30:20 +03:00
|
|
|
IWI_FW_GET_MAJOR(le32toh(hdr->version)),
|
|
|
|
IWI_FW_GET_MINOR(le32toh(hdr->version)),
|
|
|
|
IWI_FW_REQ_MAJOR, IWI_FW_REQ_MINOR);
|
|
|
|
error = EIO;
|
|
|
|
goto fail2;
|
2005-01-11 21:24:24 +03:00
|
|
|
}
|
|
|
|
|
2006-12-20 19:30:20 +03:00
|
|
|
kfw->boot_size = hdr->bsize;
|
|
|
|
kfw->ucode_size = hdr->usize;
|
|
|
|
kfw->main_size = hdr->fsize;
|
|
|
|
|
|
|
|
fw = sc->sc_blob + sizeof(struct iwi_firmware_hdr);
|
|
|
|
kfw->boot = fw;
|
|
|
|
fw += kfw->boot_size;
|
|
|
|
kfw->ucode = fw;
|
|
|
|
fw += kfw->ucode_size;
|
|
|
|
kfw->main = fw;
|
2005-01-11 21:24:24 +03:00
|
|
|
|
2006-12-20 19:30:20 +03:00
|
|
|
DPRINTF(("Firmware cached: boot %p, ucode %p, main %p\n",
|
|
|
|
kfw->boot, kfw->ucode, kfw->main));
|
2005-01-11 21:24:24 +03:00
|
|
|
DPRINTF(("Firmware cached: boot %u, ucode %u, main %u\n",
|
|
|
|
kfw->boot_size, kfw->ucode_size, kfw->main_size));
|
|
|
|
|
|
|
|
sc->flags |= IWI_FLAG_FW_CACHED;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
2006-12-20 19:30:20 +03:00
|
|
|
|
|
|
|
fail2: firmware_free(sc->sc_blob, 0);
|
2005-01-11 21:24:24 +03:00
|
|
|
fail1:
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
iwi_free_firmware(struct iwi_softc *sc)
|
|
|
|
{
|
2006-08-09 15:35:59 +04:00
|
|
|
|
2005-01-11 21:24:24 +03:00
|
|
|
if (!(sc->flags & IWI_FLAG_FW_CACHED))
|
|
|
|
return;
|
2005-02-27 03:26:58 +03:00
|
|
|
|
2007-01-23 18:02:50 +03:00
|
|
|
firmware_free(sc->sc_blob, 0);
|
2005-01-11 21:24:24 +03:00
|
|
|
|
|
|
|
sc->flags &= ~IWI_FLAG_FW_CACHED;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
iwi_config(struct iwi_softc *sc)
|
|
|
|
{
|
|
|
|
struct ieee80211com *ic = &sc->sc_ic;
|
2005-06-22 10:14:51 +04:00
|
|
|
struct ifnet *ifp = &sc->sc_if;
|
2005-01-11 21:24:24 +03:00
|
|
|
struct iwi_configuration config;
|
|
|
|
struct iwi_rateset rs;
|
|
|
|
struct iwi_txpower power;
|
2005-07-31 01:15:51 +04:00
|
|
|
struct ieee80211_key *wk;
|
2005-01-11 21:24:24 +03:00
|
|
|
struct iwi_wep_key wepkey;
|
2005-09-17 16:40:27 +04:00
|
|
|
uint32_t data;
|
2006-12-20 19:30:20 +03:00
|
|
|
int error, nchan, i;
|
2005-01-11 21:24:24 +03:00
|
|
|
|
2007-08-27 02:45:55 +04:00
|
|
|
IEEE80211_ADDR_COPY(ic->ic_myaddr, CLLADDR(ifp->if_sadl));
|
2005-01-11 21:24:24 +03:00
|
|
|
DPRINTF(("Setting MAC address to %s\n", ether_sprintf(ic->ic_myaddr)));
|
|
|
|
error = iwi_cmd(sc, IWI_CMD_SET_MAC_ADDRESS, ic->ic_myaddr,
|
|
|
|
IEEE80211_ADDR_LEN, 0);
|
|
|
|
if (error != 0)
|
|
|
|
return error;
|
|
|
|
|
|
|
|
memset(&config, 0, sizeof config);
|
2005-07-31 01:15:51 +04:00
|
|
|
config.bluetooth_coexistence = sc->bluetooth;
|
|
|
|
config.antenna = sc->antenna;
|
2006-12-20 19:30:20 +03:00
|
|
|
config.silence_threshold = 0x1e;
|
2005-01-11 21:24:24 +03:00
|
|
|
config.multicast_enabled = 1;
|
2005-07-31 01:15:51 +04:00
|
|
|
config.answer_pbreq = (ic->ic_opmode == IEEE80211_M_IBSS) ? 1 : 0;
|
|
|
|
config.disable_unicast_decryption = 1;
|
|
|
|
config.disable_multicast_decryption = 1;
|
2005-01-11 21:24:24 +03:00
|
|
|
DPRINTF(("Configuring adapter\n"));
|
|
|
|
error = iwi_cmd(sc, IWI_CMD_SET_CONFIGURATION, &config, sizeof config,
|
|
|
|
0);
|
|
|
|
if (error != 0)
|
|
|
|
return error;
|
|
|
|
|
|
|
|
data = htole32(IWI_POWER_MODE_CAM);
|
|
|
|
DPRINTF(("Setting power mode to %u\n", le32toh(data)));
|
|
|
|
error = iwi_cmd(sc, IWI_CMD_SET_POWER_MODE, &data, sizeof data, 0);
|
|
|
|
if (error != 0)
|
|
|
|
return error;
|
|
|
|
|
|
|
|
data = htole32(ic->ic_rtsthreshold);
|
|
|
|
DPRINTF(("Setting RTS threshold to %u\n", le32toh(data)));
|
|
|
|
error = iwi_cmd(sc, IWI_CMD_SET_RTS_THRESHOLD, &data, sizeof data, 0);
|
|
|
|
if (error != 0)
|
|
|
|
return error;
|
|
|
|
|
2005-07-31 01:15:51 +04:00
|
|
|
data = htole32(ic->ic_fragthreshold);
|
|
|
|
DPRINTF(("Setting fragmentation threshold to %u\n", le32toh(data)));
|
|
|
|
error = iwi_cmd(sc, IWI_CMD_SET_FRAG_THRESHOLD, &data, sizeof data, 0);
|
|
|
|
if (error != 0)
|
|
|
|
return error;
|
|
|
|
|
2006-12-20 19:30:20 +03:00
|
|
|
/*
|
|
|
|
* Set default Tx power for 802.11b/g and 802.11a channels.
|
|
|
|
*/
|
|
|
|
nchan = 0;
|
|
|
|
for (i = 0; i <= IEEE80211_CHAN_MAX; i++) {
|
|
|
|
if (!IEEE80211_IS_CHAN_2GHZ(&ic->ic_channels[i]))
|
|
|
|
continue;
|
|
|
|
power.chan[nchan].chan = i;
|
|
|
|
power.chan[nchan].power = IWI_TXPOWER_MAX;
|
|
|
|
nchan++;
|
|
|
|
}
|
|
|
|
power.nchan = nchan;
|
2005-01-11 21:24:24 +03:00
|
|
|
|
2006-12-20 19:30:20 +03:00
|
|
|
power.mode = IWI_MODE_11G;
|
|
|
|
DPRINTF(("Setting .11g channels tx power\n"));
|
|
|
|
error = iwi_cmd(sc, IWI_CMD_SET_TX_POWER, &power, sizeof power, 0);
|
|
|
|
if (error != 0)
|
|
|
|
return error;
|
|
|
|
|
|
|
|
power.mode = IWI_MODE_11B;
|
|
|
|
DPRINTF(("Setting .11b channels tx power\n"));
|
|
|
|
error = iwi_cmd(sc, IWI_CMD_SET_TX_POWER, &power, sizeof power, 0);
|
|
|
|
if (error != 0)
|
|
|
|
return error;
|
|
|
|
|
|
|
|
nchan = 0;
|
|
|
|
for (i = 0; i <= IEEE80211_CHAN_MAX; i++) {
|
|
|
|
if (!IEEE80211_IS_CHAN_5GHZ(&ic->ic_channels[i]))
|
|
|
|
continue;
|
|
|
|
power.chan[nchan].chan = i;
|
|
|
|
power.chan[nchan].power = IWI_TXPOWER_MAX;
|
|
|
|
nchan++;
|
|
|
|
}
|
|
|
|
power.nchan = nchan;
|
|
|
|
|
|
|
|
if (nchan > 0) { /* 2915ABG only */
|
|
|
|
power.mode = IWI_MODE_11A;
|
|
|
|
DPRINTF(("Setting .11a channels tx power\n"));
|
2005-01-11 21:24:24 +03:00
|
|
|
error = iwi_cmd(sc, IWI_CMD_SET_TX_POWER, &power, sizeof power,
|
|
|
|
0);
|
|
|
|
if (error != 0)
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
|
|
|
|
rs.mode = IWI_MODE_11G;
|
|
|
|
rs.type = IWI_RATESET_TYPE_SUPPORTED;
|
|
|
|
rs.nrates = ic->ic_sup_rates[IEEE80211_MODE_11G].rs_nrates;
|
|
|
|
memcpy(rs.rates, ic->ic_sup_rates[IEEE80211_MODE_11G].rs_rates,
|
|
|
|
rs.nrates);
|
|
|
|
DPRINTF(("Setting .11bg supported rates (%u)\n", rs.nrates));
|
|
|
|
error = iwi_cmd(sc, IWI_CMD_SET_RATES, &rs, sizeof rs, 0);
|
|
|
|
if (error != 0)
|
|
|
|
return error;
|
|
|
|
|
|
|
|
rs.mode = IWI_MODE_11A;
|
|
|
|
rs.type = IWI_RATESET_TYPE_SUPPORTED;
|
|
|
|
rs.nrates = ic->ic_sup_rates[IEEE80211_MODE_11A].rs_nrates;
|
|
|
|
memcpy(rs.rates, ic->ic_sup_rates[IEEE80211_MODE_11A].rs_rates,
|
|
|
|
rs.nrates);
|
|
|
|
DPRINTF(("Setting .11a supported rates (%u)\n", rs.nrates));
|
|
|
|
error = iwi_cmd(sc, IWI_CMD_SET_RATES, &rs, sizeof rs, 0);
|
|
|
|
if (error != 0)
|
|
|
|
return error;
|
|
|
|
|
2005-11-18 19:42:22 +03:00
|
|
|
/* if we have a desired ESSID, set it now */
|
|
|
|
if (ic->ic_des_esslen != 0) {
|
|
|
|
#ifdef IWI_DEBUG
|
|
|
|
if (iwi_debug > 0) {
|
|
|
|
printf("Setting desired ESSID to ");
|
|
|
|
ieee80211_print_essid(ic->ic_des_essid,
|
|
|
|
ic->ic_des_esslen);
|
|
|
|
printf("\n");
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
error = iwi_cmd(sc, IWI_CMD_SET_ESSID, ic->ic_des_essid,
|
|
|
|
ic->ic_des_esslen, 0);
|
|
|
|
if (error != 0)
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
|
2005-01-11 21:24:24 +03:00
|
|
|
data = htole32(arc4random());
|
|
|
|
DPRINTF(("Setting initialization vector to %u\n", le32toh(data)));
|
|
|
|
error = iwi_cmd(sc, IWI_CMD_SET_IV, &data, sizeof data, 0);
|
|
|
|
if (error != 0)
|
|
|
|
return error;
|
|
|
|
|
2006-12-20 19:30:20 +03:00
|
|
|
if (ic->ic_flags & IEEE80211_F_PRIVACY) {
|
|
|
|
/* XXX iwi_setwepkeys? */
|
|
|
|
for (i = 0; i < IEEE80211_WEP_NKID; i++) {
|
|
|
|
wk = &ic->ic_crypto.cs_nw_keys[i];
|
|
|
|
|
|
|
|
wepkey.cmd = IWI_WEP_KEY_CMD_SETKEY;
|
|
|
|
wepkey.idx = i;
|
|
|
|
wepkey.len = wk->wk_keylen;
|
|
|
|
memset(wepkey.key, 0, sizeof wepkey.key);
|
|
|
|
memcpy(wepkey.key, wk->wk_key, wk->wk_keylen);
|
|
|
|
DPRINTF(("Setting wep key index %u len %u\n",
|
|
|
|
wepkey.idx, wepkey.len));
|
|
|
|
error = iwi_cmd(sc, IWI_CMD_SET_WEP_KEY, &wepkey,
|
|
|
|
sizeof wepkey, 0);
|
|
|
|
if (error != 0)
|
|
|
|
return error;
|
|
|
|
}
|
2005-01-11 21:24:24 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Enable adapter */
|
|
|
|
DPRINTF(("Enabling adapter\n"));
|
|
|
|
return iwi_cmd(sc, IWI_CMD_ENABLE, NULL, 0, 0);
|
|
|
|
}
|
|
|
|
|
2005-06-20 13:03:44 +04:00
|
|
|
static int
|
|
|
|
iwi_set_chan(struct iwi_softc *sc, struct ieee80211_channel *chan)
|
|
|
|
{
|
|
|
|
struct ieee80211com *ic = &sc->sc_ic;
|
2005-10-08 10:19:46 +04:00
|
|
|
struct iwi_scan_v2 scan;
|
2005-06-20 13:03:44 +04:00
|
|
|
|
2005-07-31 01:15:51 +04:00
|
|
|
(void)memset(&scan, 0, sizeof scan);
|
2005-10-08 10:19:46 +04:00
|
|
|
|
|
|
|
scan.dwelltime[IWI_SCAN_TYPE_PASSIVE] = htole16(2000);
|
|
|
|
scan.channels[0] = 1 |
|
|
|
|
(IEEE80211_IS_CHAN_5GHZ(chan) ? IWI_CHAN_5GHZ : IWI_CHAN_2GHZ);
|
2005-06-20 13:03:44 +04:00
|
|
|
scan.channels[1] = ieee80211_chan2ieee(ic, chan);
|
2005-10-08 10:19:46 +04:00
|
|
|
iwi_scan_type_set(scan, 1, IWI_SCAN_TYPE_PASSIVE);
|
2005-06-20 13:03:44 +04:00
|
|
|
|
|
|
|
DPRINTF(("Setting channel to %u\n", ieee80211_chan2ieee(ic, chan)));
|
2005-10-08 10:19:46 +04:00
|
|
|
return iwi_cmd(sc, IWI_CMD_SCAN_V2, &scan, sizeof scan, 1);
|
2005-06-20 13:03:44 +04:00
|
|
|
}
|
|
|
|
|
2005-01-11 21:24:24 +03:00
|
|
|
static int
|
|
|
|
iwi_scan(struct iwi_softc *sc)
|
|
|
|
{
|
|
|
|
struct ieee80211com *ic = &sc->sc_ic;
|
2005-09-25 15:55:05 +04:00
|
|
|
struct iwi_scan_v2 scan;
|
|
|
|
uint32_t type;
|
2005-09-17 16:40:27 +04:00
|
|
|
uint8_t *p;
|
2005-09-25 15:55:05 +04:00
|
|
|
int i, count, idx;
|
2005-01-11 21:24:24 +03:00
|
|
|
|
2005-07-31 01:15:51 +04:00
|
|
|
(void)memset(&scan, 0, sizeof scan);
|
2005-09-25 15:55:05 +04:00
|
|
|
scan.dwelltime[IWI_SCAN_TYPE_ACTIVE_BROADCAST] =
|
|
|
|
htole16(sc->dwelltime);
|
|
|
|
scan.dwelltime[IWI_SCAN_TYPE_ACTIVE_BDIRECT] =
|
|
|
|
htole16(sc->dwelltime);
|
2005-01-11 21:24:24 +03:00
|
|
|
|
2005-09-25 15:55:05 +04:00
|
|
|
/* tell the firmware about the desired essid */
|
|
|
|
if (ic->ic_des_esslen) {
|
|
|
|
int error;
|
|
|
|
|
|
|
|
DPRINTF(("%s: Setting adapter desired ESSID to %s\n",
|
|
|
|
__func__, ic->ic_des_essid));
|
|
|
|
|
|
|
|
error = iwi_cmd(sc, IWI_CMD_SET_ESSID,
|
|
|
|
ic->ic_des_essid, ic->ic_des_esslen, 1);
|
|
|
|
if (error)
|
|
|
|
return error;
|
|
|
|
|
|
|
|
type = IWI_SCAN_TYPE_ACTIVE_BDIRECT;
|
|
|
|
} else {
|
|
|
|
type = IWI_SCAN_TYPE_ACTIVE_BROADCAST;
|
|
|
|
}
|
|
|
|
|
|
|
|
p = &scan.channels[0];
|
|
|
|
count = idx = 0;
|
2005-01-11 21:24:24 +03:00
|
|
|
for (i = 0; i <= IEEE80211_CHAN_MAX; i++) {
|
|
|
|
if (IEEE80211_IS_CHAN_5GHZ(&ic->ic_channels[i]) &&
|
|
|
|
isset(ic->ic_chan_active, i)) {
|
|
|
|
*++p = i;
|
|
|
|
count++;
|
2005-09-25 15:55:05 +04:00
|
|
|
idx++;
|
|
|
|
iwi_scan_type_set(scan, idx, type);
|
2005-01-11 21:24:24 +03:00
|
|
|
}
|
|
|
|
}
|
2005-09-25 15:55:05 +04:00
|
|
|
if (count) {
|
|
|
|
*(p - count) = IWI_CHAN_5GHZ | count;
|
|
|
|
p++;
|
|
|
|
}
|
2005-01-11 21:24:24 +03:00
|
|
|
|
|
|
|
count = 0;
|
|
|
|
for (i = 0; i <= IEEE80211_CHAN_MAX; i++) {
|
|
|
|
if (IEEE80211_IS_CHAN_2GHZ(&ic->ic_channels[i]) &&
|
|
|
|
isset(ic->ic_chan_active, i)) {
|
|
|
|
*++p = i;
|
|
|
|
count++;
|
2005-09-25 15:55:05 +04:00
|
|
|
idx++;
|
|
|
|
iwi_scan_type_set(scan, idx, type);
|
2005-01-11 21:24:24 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
*(p - count) = IWI_CHAN_2GHZ | count;
|
|
|
|
|
|
|
|
DPRINTF(("Start scanning\n"));
|
2005-09-25 15:55:05 +04:00
|
|
|
return iwi_cmd(sc, IWI_CMD_SCAN_V2, &scan, sizeof scan, 1);
|
2005-01-11 21:24:24 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
iwi_auth_and_assoc(struct iwi_softc *sc)
|
|
|
|
{
|
|
|
|
struct ieee80211com *ic = &sc->sc_ic;
|
|
|
|
struct ieee80211_node *ni = ic->ic_bss;
|
2005-07-31 01:15:51 +04:00
|
|
|
struct ifnet *ifp = &sc->sc_if;
|
2005-11-18 19:42:22 +03:00
|
|
|
struct ieee80211_wme_info wme;
|
2005-01-11 21:24:24 +03:00
|
|
|
struct iwi_configuration config;
|
|
|
|
struct iwi_associate assoc;
|
|
|
|
struct iwi_rateset rs;
|
2005-09-17 16:40:27 +04:00
|
|
|
uint16_t capinfo;
|
|
|
|
uint32_t data;
|
2005-01-11 21:24:24 +03:00
|
|
|
int error;
|
|
|
|
|
2006-12-20 19:30:20 +03:00
|
|
|
memset(&config, 0, sizeof config);
|
|
|
|
config.bluetooth_coexistence = sc->bluetooth;
|
|
|
|
config.antenna = sc->antenna;
|
|
|
|
config.multicast_enabled = 1;
|
|
|
|
config.silence_threshold = 0x1e;
|
|
|
|
if (ic->ic_curmode == IEEE80211_MODE_11G)
|
2005-07-31 01:15:51 +04:00
|
|
|
config.use_protection = 1;
|
2006-12-20 19:30:20 +03:00
|
|
|
config.answer_pbreq = (ic->ic_opmode == IEEE80211_M_IBSS) ? 1 : 0;
|
|
|
|
config.disable_unicast_decryption = 1;
|
|
|
|
config.disable_multicast_decryption = 1;
|
|
|
|
|
|
|
|
DPRINTF(("Configuring adapter\n"));
|
|
|
|
error = iwi_cmd(sc, IWI_CMD_SET_CONFIGURATION, &config,
|
|
|
|
sizeof config, 1);
|
|
|
|
if (error != 0)
|
|
|
|
return error;
|
2005-01-11 21:24:24 +03:00
|
|
|
|
|
|
|
#ifdef IWI_DEBUG
|
|
|
|
if (iwi_debug > 0) {
|
2007-10-22 19:28:48 +04:00
|
|
|
aprint_debug_dev(sc->sc_dev, "Setting ESSID to ");
|
2005-01-11 21:24:24 +03:00
|
|
|
ieee80211_print_essid(ni->ni_essid, ni->ni_esslen);
|
2007-10-22 19:28:48 +04:00
|
|
|
aprint_debug("\n");
|
2005-01-11 21:24:24 +03:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
error = iwi_cmd(sc, IWI_CMD_SET_ESSID, ni->ni_essid, ni->ni_esslen, 1);
|
|
|
|
if (error != 0)
|
|
|
|
return error;
|
|
|
|
|
2005-09-13 01:15:04 +04:00
|
|
|
/* the rate set has already been "negotiated" */
|
2005-01-11 21:24:24 +03:00
|
|
|
rs.mode = IEEE80211_IS_CHAN_5GHZ(ni->ni_chan) ? IWI_MODE_11A :
|
|
|
|
IWI_MODE_11G;
|
2005-09-13 01:15:04 +04:00
|
|
|
rs.type = IWI_RATESET_TYPE_NEGOTIATED;
|
2005-01-11 21:24:24 +03:00
|
|
|
rs.nrates = ni->ni_rates.rs_nrates;
|
2006-12-20 19:30:20 +03:00
|
|
|
|
|
|
|
if (rs.nrates > IWI_RATESET_SIZE) {
|
|
|
|
DPRINTF(("Truncating negotiated rate set from %u\n",
|
|
|
|
rs.nrates));
|
|
|
|
rs.nrates = IWI_RATESET_SIZE;
|
|
|
|
}
|
2005-01-11 21:24:24 +03:00
|
|
|
memcpy(rs.rates, ni->ni_rates.rs_rates, rs.nrates);
|
2005-09-13 01:15:04 +04:00
|
|
|
DPRINTF(("Setting negotiated rates (%u)\n", rs.nrates));
|
2005-01-11 21:24:24 +03:00
|
|
|
error = iwi_cmd(sc, IWI_CMD_SET_RATES, &rs, sizeof rs, 1);
|
|
|
|
if (error != 0)
|
|
|
|
return error;
|
|
|
|
|
2005-11-18 19:42:22 +03:00
|
|
|
if ((ic->ic_flags & IEEE80211_F_WME) && ni->ni_wme_ie != NULL) {
|
|
|
|
wme.wme_id = IEEE80211_ELEMID_VENDOR;
|
|
|
|
wme.wme_len = sizeof (struct ieee80211_wme_info) - 2;
|
|
|
|
wme.wme_oui[0] = 0x00;
|
|
|
|
wme.wme_oui[1] = 0x50;
|
|
|
|
wme.wme_oui[2] = 0xf2;
|
|
|
|
wme.wme_type = WME_OUI_TYPE;
|
|
|
|
wme.wme_subtype = WME_INFO_OUI_SUBTYPE;
|
|
|
|
wme.wme_version = WME_VERSION;
|
|
|
|
wme.wme_info = 0;
|
|
|
|
|
|
|
|
DPRINTF(("Setting WME IE (len=%u)\n", wme.wme_len));
|
|
|
|
error = iwi_cmd(sc, IWI_CMD_SET_WMEIE, &wme, sizeof wme, 1);
|
|
|
|
if (error != 0)
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
|
2005-07-31 01:15:51 +04:00
|
|
|
if (ic->ic_opt_ie != NULL) {
|
|
|
|
DPRINTF(("Setting optional IE (len=%u)\n", ic->ic_opt_ie_len));
|
|
|
|
error = iwi_cmd(sc, IWI_CMD_SET_OPTIE, ic->ic_opt_ie,
|
|
|
|
ic->ic_opt_ie_len, 1);
|
|
|
|
if (error != 0)
|
|
|
|
return error;
|
|
|
|
}
|
2005-01-11 21:24:24 +03:00
|
|
|
data = htole32(ni->ni_rssi);
|
|
|
|
DPRINTF(("Setting sensitivity to %d\n", (int8_t)ni->ni_rssi));
|
|
|
|
error = iwi_cmd(sc, IWI_CMD_SET_SENSITIVITY, &data, sizeof data, 1);
|
|
|
|
if (error != 0)
|
|
|
|
return error;
|
|
|
|
|
|
|
|
memset(&assoc, 0, sizeof assoc);
|
2006-12-20 19:30:20 +03:00
|
|
|
if (IEEE80211_IS_CHAN_A(ni->ni_chan))
|
|
|
|
assoc.mode = IWI_MODE_11A;
|
|
|
|
else if (IEEE80211_IS_CHAN_G(ni->ni_chan))
|
|
|
|
assoc.mode = IWI_MODE_11G;
|
|
|
|
else if (IEEE80211_IS_CHAN_B(ni->ni_chan))
|
|
|
|
assoc.mode = IWI_MODE_11B;
|
|
|
|
|
2005-01-11 21:24:24 +03:00
|
|
|
assoc.chan = ieee80211_chan2ieee(ic, ni->ni_chan);
|
2006-12-20 19:30:20 +03:00
|
|
|
|
2005-07-31 01:15:51 +04:00
|
|
|
if (ni->ni_authmode == IEEE80211_AUTH_SHARED)
|
|
|
|
assoc.auth = (ic->ic_crypto.cs_def_txkey << 4) | IWI_AUTH_SHARED;
|
2006-12-20 19:30:20 +03:00
|
|
|
|
|
|
|
if (ic->ic_flags & IEEE80211_F_SHPREAMBLE)
|
|
|
|
assoc.plen = IWI_ASSOC_SHPREAMBLE;
|
|
|
|
|
2005-11-18 19:42:22 +03:00
|
|
|
if ((ic->ic_flags & IEEE80211_F_WME) && ni->ni_wme_ie != NULL)
|
|
|
|
assoc.policy |= htole16(IWI_POLICY_WME);
|
2006-02-21 10:24:07 +03:00
|
|
|
if (ic->ic_flags & IEEE80211_F_WPA)
|
2005-11-18 19:42:22 +03:00
|
|
|
assoc.policy |= htole16(IWI_POLICY_WPA);
|
2006-12-20 19:30:20 +03:00
|
|
|
if (ic->ic_opmode == IEEE80211_M_IBSS && ni->ni_tstamp.tsf == 0)
|
|
|
|
assoc.type = IWI_HC_IBSS_START;
|
|
|
|
else
|
|
|
|
assoc.type = IWI_HC_ASSOC;
|
2005-06-22 10:14:51 +04:00
|
|
|
memcpy(assoc.tstamp, ni->ni_tstamp.data, 8);
|
2005-07-31 01:15:51 +04:00
|
|
|
|
|
|
|
if (ic->ic_opmode == IEEE80211_M_IBSS)
|
|
|
|
capinfo = IEEE80211_CAPINFO_IBSS;
|
|
|
|
else
|
|
|
|
capinfo = IEEE80211_CAPINFO_ESS;
|
|
|
|
if (ic->ic_flags & IEEE80211_F_PRIVACY)
|
|
|
|
capinfo |= IEEE80211_CAPINFO_PRIVACY;
|
|
|
|
if ((ic->ic_flags & IEEE80211_F_SHPREAMBLE) &&
|
|
|
|
IEEE80211_IS_CHAN_2GHZ(ni->ni_chan))
|
|
|
|
capinfo |= IEEE80211_CAPINFO_SHORT_PREAMBLE;
|
|
|
|
if (ic->ic_flags & IEEE80211_F_SHSLOT)
|
|
|
|
capinfo |= IEEE80211_CAPINFO_SHORT_SLOTTIME;
|
|
|
|
assoc.capinfo = htole16(capinfo);
|
|
|
|
|
2005-01-11 21:24:24 +03:00
|
|
|
assoc.lintval = htole16(ic->ic_lintval);
|
|
|
|
assoc.intval = htole16(ni->ni_intval);
|
|
|
|
IEEE80211_ADDR_COPY(assoc.bssid, ni->ni_bssid);
|
2005-07-31 01:15:51 +04:00
|
|
|
if (ic->ic_opmode == IEEE80211_M_IBSS)
|
|
|
|
IEEE80211_ADDR_COPY(assoc.dst, ifp->if_broadcastaddr);
|
|
|
|
else
|
|
|
|
IEEE80211_ADDR_COPY(assoc.dst, ni->ni_bssid);
|
2006-12-20 19:30:20 +03:00
|
|
|
|
|
|
|
DPRINTF(("%s bssid %s dst %s channel %u policy 0x%x "
|
|
|
|
"auth %u capinfo 0x%x lintval %u bintval %u\n",
|
|
|
|
assoc.type == IWI_HC_IBSS_START ? "Start" : "Join",
|
|
|
|
ether_sprintf(assoc.bssid), ether_sprintf(assoc.dst),
|
|
|
|
assoc.chan, le16toh(assoc.policy), assoc.auth,
|
|
|
|
le16toh(assoc.capinfo), le16toh(assoc.lintval),
|
|
|
|
le16toh(assoc.intval)));
|
|
|
|
|
2005-01-11 21:24:24 +03:00
|
|
|
return iwi_cmd(sc, IWI_CMD_ASSOCIATE, &assoc, sizeof assoc, 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
iwi_init(struct ifnet *ifp)
|
|
|
|
{
|
|
|
|
struct iwi_softc *sc = ifp->if_softc;
|
|
|
|
struct ieee80211com *ic = &sc->sc_ic;
|
|
|
|
struct iwi_firmware *fw = &sc->fw;
|
|
|
|
int i, error;
|
|
|
|
|
|
|
|
/* exit immediately if firmware has not been ioctl'd */
|
|
|
|
if (!(sc->flags & IWI_FLAG_FW_CACHED)) {
|
2006-08-09 15:35:59 +04:00
|
|
|
if ((error = iwi_cache_firmware(sc)) != 0) {
|
2007-10-22 19:28:48 +04:00
|
|
|
aprint_error_dev(sc->sc_dev,
|
|
|
|
"could not cache the firmware\n");
|
2006-08-09 15:35:59 +04:00
|
|
|
goto fail;
|
|
|
|
}
|
2005-01-11 21:24:24 +03:00
|
|
|
}
|
|
|
|
|
2005-07-31 01:15:51 +04:00
|
|
|
iwi_stop(ifp, 0);
|
|
|
|
|
2005-01-11 21:24:24 +03:00
|
|
|
if ((error = iwi_reset(sc)) != 0) {
|
2007-10-22 19:28:48 +04:00
|
|
|
aprint_error_dev(sc->sc_dev, "could not reset adapter\n");
|
2005-01-11 21:24:24 +03:00
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((error = iwi_load_firmware(sc, fw->boot, fw->boot_size)) != 0) {
|
2007-10-22 19:28:48 +04:00
|
|
|
aprint_error_dev(sc->sc_dev, "could not load boot firmware\n");
|
2005-01-11 21:24:24 +03:00
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((error = iwi_load_ucode(sc, fw->ucode, fw->ucode_size)) != 0) {
|
2007-10-22 19:28:48 +04:00
|
|
|
aprint_error_dev(sc->sc_dev, "could not load microcode\n");
|
2005-01-11 21:24:24 +03:00
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
|
|
|
iwi_stop_master(sc);
|
|
|
|
|
2005-08-19 12:50:06 +04:00
|
|
|
CSR_WRITE_4(sc, IWI_CSR_CMD_BASE, sc->cmdq.desc_map->dm_segs[0].ds_addr);
|
|
|
|
CSR_WRITE_4(sc, IWI_CSR_CMD_SIZE, sc->cmdq.count);
|
|
|
|
CSR_WRITE_4(sc, IWI_CSR_CMD_WIDX, sc->cmdq.cur);
|
|
|
|
|
2005-11-18 19:42:22 +03:00
|
|
|
CSR_WRITE_4(sc, IWI_CSR_TX1_BASE, sc->txq[0].desc_map->dm_segs[0].ds_addr);
|
|
|
|
CSR_WRITE_4(sc, IWI_CSR_TX1_SIZE, sc->txq[0].count);
|
|
|
|
CSR_WRITE_4(sc, IWI_CSR_TX1_WIDX, sc->txq[0].cur);
|
2005-08-19 12:50:06 +04:00
|
|
|
|
2005-11-18 19:42:22 +03:00
|
|
|
CSR_WRITE_4(sc, IWI_CSR_TX2_BASE, sc->txq[1].desc_map->dm_segs[0].ds_addr);
|
|
|
|
CSR_WRITE_4(sc, IWI_CSR_TX2_SIZE, sc->txq[1].count);
|
|
|
|
CSR_WRITE_4(sc, IWI_CSR_TX2_WIDX, sc->txq[1].cur);
|
2005-08-19 12:50:06 +04:00
|
|
|
|
2005-11-18 19:42:22 +03:00
|
|
|
CSR_WRITE_4(sc, IWI_CSR_TX3_BASE, sc->txq[2].desc_map->dm_segs[0].ds_addr);
|
|
|
|
CSR_WRITE_4(sc, IWI_CSR_TX3_SIZE, sc->txq[2].count);
|
|
|
|
CSR_WRITE_4(sc, IWI_CSR_TX3_WIDX, sc->txq[2].cur);
|
2005-08-19 12:50:06 +04:00
|
|
|
|
2005-11-18 19:42:22 +03:00
|
|
|
CSR_WRITE_4(sc, IWI_CSR_TX4_BASE, sc->txq[3].desc_map->dm_segs[0].ds_addr);
|
|
|
|
CSR_WRITE_4(sc, IWI_CSR_TX4_SIZE, sc->txq[3].count);
|
|
|
|
CSR_WRITE_4(sc, IWI_CSR_TX4_WIDX, sc->txq[3].cur);
|
2005-08-19 12:50:06 +04:00
|
|
|
|
|
|
|
for (i = 0; i < sc->rxq.count; i++)
|
2005-01-11 21:24:24 +03:00
|
|
|
CSR_WRITE_4(sc, IWI_CSR_RX_BASE + i * 4,
|
2005-08-19 12:50:06 +04:00
|
|
|
sc->rxq.data[i].map->dm_segs[0].ds_addr);
|
2005-01-11 21:24:24 +03:00
|
|
|
|
2005-08-19 12:50:06 +04:00
|
|
|
CSR_WRITE_4(sc, IWI_CSR_RX_WIDX, sc->rxq.count -1);
|
2005-01-11 21:24:24 +03:00
|
|
|
|
|
|
|
if ((error = iwi_load_firmware(sc, fw->main, fw->main_size)) != 0) {
|
2007-10-22 19:28:48 +04:00
|
|
|
aprint_error_dev(sc->sc_dev, "could not load main firmware\n");
|
2005-01-11 21:24:24 +03:00
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
|
|
|
sc->flags |= IWI_FLAG_FW_INITED;
|
|
|
|
|
|
|
|
if ((error = iwi_config(sc)) != 0) {
|
2007-10-22 19:28:48 +04:00
|
|
|
aprint_error_dev(sc->sc_dev, "device configuration failed\n");
|
2005-01-11 21:24:24 +03:00
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
2005-12-05 12:24:54 +03:00
|
|
|
ic->ic_state = IEEE80211_S_INIT;
|
|
|
|
|
|
|
|
ifp->if_flags &= ~IFF_OACTIVE;
|
|
|
|
ifp->if_flags |= IFF_RUNNING;
|
|
|
|
|
2005-11-18 19:42:22 +03:00
|
|
|
if (ic->ic_opmode != IEEE80211_M_MONITOR) {
|
|
|
|
if (ic->ic_roaming != IEEE80211_ROAMING_MANUAL)
|
|
|
|
ieee80211_new_state(ic, IEEE80211_S_SCAN, -1);
|
|
|
|
} else
|
2005-06-20 13:03:44 +04:00
|
|
|
ieee80211_new_state(ic, IEEE80211_S_RUN, -1);
|
2005-01-11 21:24:24 +03:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
2005-08-19 12:50:06 +04:00
|
|
|
fail: ifp->if_flags &= ~IFF_UP;
|
|
|
|
iwi_stop(ifp, 0);
|
2005-01-11 21:24:24 +03:00
|
|
|
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
|
2006-08-19 10:32:52 +04:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Return whether or not the radio is enabled in hardware
|
|
|
|
* (i.e. the rfkill switch is "off").
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
iwi_getrfkill(struct iwi_softc *sc)
|
|
|
|
{
|
|
|
|
return (CSR_READ_4(sc, IWI_CSR_IO) & IWI_IO_RADIO_ENABLED) == 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
iwi_sysctl_radio(SYSCTLFN_ARGS)
|
|
|
|
{
|
|
|
|
struct sysctlnode node;
|
|
|
|
struct iwi_softc *sc;
|
|
|
|
int val, error;
|
|
|
|
|
|
|
|
node = *rnode;
|
|
|
|
sc = (struct iwi_softc *)node.sysctl_data;
|
|
|
|
|
|
|
|
val = !iwi_getrfkill(sc);
|
|
|
|
|
|
|
|
node.sysctl_data = &val;
|
|
|
|
error = sysctl_lookup(SYSCTLFN_CALL(&node));
|
|
|
|
|
|
|
|
if (error || newp == NULL)
|
|
|
|
return error;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef IWI_DEBUG
|
|
|
|
SYSCTL_SETUP(sysctl_iwi, "sysctl iwi(4) subtree setup")
|
|
|
|
{
|
|
|
|
int rc;
|
|
|
|
const struct sysctlnode *rnode;
|
|
|
|
const struct sysctlnode *cnode;
|
|
|
|
|
|
|
|
if ((rc = sysctl_createv(clog, 0, NULL, &rnode,
|
|
|
|
CTLFLAG_PERMANENT, CTLTYPE_NODE, "hw", NULL,
|
|
|
|
NULL, 0, NULL, 0, CTL_HW, CTL_EOL)) != 0)
|
|
|
|
goto err;
|
|
|
|
|
|
|
|
if ((rc = sysctl_createv(clog, 0, &rnode, &rnode,
|
|
|
|
CTLFLAG_PERMANENT, CTLTYPE_NODE, "iwi",
|
|
|
|
SYSCTL_DESCR("iwi global controls"),
|
|
|
|
NULL, 0, NULL, 0, CTL_CREATE, CTL_EOL)) != 0)
|
|
|
|
goto err;
|
|
|
|
|
|
|
|
/* control debugging printfs */
|
|
|
|
if ((rc = sysctl_createv(clog, 0, &rnode, &cnode,
|
|
|
|
CTLFLAG_PERMANENT|CTLFLAG_READWRITE, CTLTYPE_INT,
|
|
|
|
"debug", SYSCTL_DESCR("Enable debugging output"),
|
|
|
|
NULL, 0, &iwi_debug, 0, CTL_CREATE, CTL_EOL)) != 0)
|
|
|
|
goto err;
|
|
|
|
|
|
|
|
return;
|
|
|
|
err:
|
|
|
|
aprint_error("%s: sysctl_createv failed (rc = %d)\n", __func__, rc);
|
|
|
|
}
|
|
|
|
|
|
|
|
#endif /* IWI_DEBUG */
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Add sysctl knobs.
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
iwi_sysctlattach(struct iwi_softc *sc)
|
|
|
|
{
|
|
|
|
int rc;
|
|
|
|
const struct sysctlnode *rnode;
|
|
|
|
const struct sysctlnode *cnode;
|
|
|
|
|
|
|
|
struct sysctllog **clog = &sc->sc_sysctllog;
|
|
|
|
|
|
|
|
if ((rc = sysctl_createv(clog, 0, NULL, &rnode,
|
|
|
|
CTLFLAG_PERMANENT, CTLTYPE_NODE, "hw", NULL,
|
|
|
|
NULL, 0, NULL, 0, CTL_HW, CTL_EOL)) != 0)
|
|
|
|
goto err;
|
|
|
|
|
|
|
|
if ((rc = sysctl_createv(clog, 0, &rnode, &rnode,
|
2007-10-22 19:28:48 +04:00
|
|
|
CTLFLAG_PERMANENT, CTLTYPE_NODE, device_xname(sc->sc_dev),
|
2006-08-19 10:32:52 +04:00
|
|
|
SYSCTL_DESCR("iwi controls and statistics"),
|
|
|
|
NULL, 0, NULL, 0, CTL_CREATE, CTL_EOL)) != 0)
|
|
|
|
goto err;
|
|
|
|
|
|
|
|
if ((rc = sysctl_createv(clog, 0, &rnode, &cnode,
|
|
|
|
CTLFLAG_PERMANENT, CTLTYPE_INT, "radio",
|
|
|
|
SYSCTL_DESCR("radio transmitter switch state (0=off, 1=on)"),
|
|
|
|
iwi_sysctl_radio, 0, sc, 0, CTL_CREATE, CTL_EOL)) != 0)
|
|
|
|
goto err;
|
|
|
|
|
|
|
|
sc->dwelltime = 100;
|
|
|
|
if ((rc = sysctl_createv(clog, 0, &rnode, &cnode,
|
|
|
|
CTLFLAG_PERMANENT|CTLFLAG_READWRITE, CTLTYPE_INT,
|
|
|
|
"dwell", SYSCTL_DESCR("channel dwell time (ms) for AP/station scanning"),
|
|
|
|
NULL, 0, &sc->dwelltime, 0, CTL_CREATE, CTL_EOL)) != 0)
|
|
|
|
goto err;
|
|
|
|
|
2006-12-20 19:30:20 +03:00
|
|
|
sc->bluetooth = 0;
|
2006-08-19 10:32:52 +04:00
|
|
|
if ((rc = sysctl_createv(clog, 0, &rnode, &cnode,
|
|
|
|
CTLFLAG_PERMANENT|CTLFLAG_READWRITE, CTLTYPE_INT,
|
|
|
|
"bluetooth", SYSCTL_DESCR("bluetooth coexistence"),
|
|
|
|
NULL, 0, &sc->bluetooth, 0, CTL_CREATE, CTL_EOL)) != 0)
|
|
|
|
goto err;
|
|
|
|
|
2006-12-20 19:30:20 +03:00
|
|
|
sc->antenna = IWI_ANTENNA_AUTO;
|
2006-08-19 10:32:52 +04:00
|
|
|
if ((rc = sysctl_createv(clog, 0, &rnode, &cnode,
|
|
|
|
CTLFLAG_PERMANENT|CTLFLAG_READWRITE, CTLTYPE_INT,
|
|
|
|
"antenna", SYSCTL_DESCR("antenna (0=auto)"),
|
|
|
|
NULL, 0, &sc->antenna, 0, CTL_CREATE, CTL_EOL)) != 0)
|
|
|
|
goto err;
|
|
|
|
|
|
|
|
return;
|
|
|
|
err:
|
|
|
|
aprint_error("%s: sysctl_createv failed (rc = %d)\n", __func__, rc);
|
|
|
|
}
|
|
|
|
|
2005-01-11 21:24:24 +03:00
|
|
|
static void
|
2006-11-16 04:32:37 +03:00
|
|
|
iwi_stop(struct ifnet *ifp, int disable)
|
2005-01-11 21:24:24 +03:00
|
|
|
{
|
|
|
|
struct iwi_softc *sc = ifp->if_softc;
|
|
|
|
struct ieee80211com *ic = &sc->sc_ic;
|
|
|
|
|
2005-11-29 16:57:00 +03:00
|
|
|
IWI_LED_OFF(sc);
|
|
|
|
|
2005-01-11 21:24:24 +03:00
|
|
|
iwi_stop_master(sc);
|
|
|
|
CSR_WRITE_4(sc, IWI_CSR_RST, IWI_RST_SW_RESET);
|
|
|
|
|
2005-08-19 12:50:06 +04:00
|
|
|
/* reset rings */
|
|
|
|
iwi_reset_cmd_ring(sc, &sc->cmdq);
|
2005-11-18 19:42:22 +03:00
|
|
|
iwi_reset_tx_ring(sc, &sc->txq[0]);
|
|
|
|
iwi_reset_tx_ring(sc, &sc->txq[1]);
|
|
|
|
iwi_reset_tx_ring(sc, &sc->txq[2]);
|
|
|
|
iwi_reset_tx_ring(sc, &sc->txq[3]);
|
2005-08-19 12:50:06 +04:00
|
|
|
iwi_reset_rx_ring(sc, &sc->rxq);
|
2005-01-11 21:24:24 +03:00
|
|
|
|
|
|
|
ifp->if_timer = 0;
|
|
|
|
ifp->if_flags &= ~(IFF_RUNNING | IFF_OACTIVE);
|
|
|
|
|
|
|
|
ieee80211_new_state(ic, IEEE80211_S_INIT, -1);
|
|
|
|
}
|
2005-11-14 14:58:52 +03:00
|
|
|
|
2005-11-29 16:57:00 +03:00
|
|
|
static void
|
|
|
|
iwi_led_set(struct iwi_softc *sc, uint32_t state, int toggle)
|
|
|
|
{
|
|
|
|
uint32_t val;
|
|
|
|
|
|
|
|
val = MEM_READ_4(sc, IWI_MEM_EVENT_CTL);
|
|
|
|
|
|
|
|
switch (sc->nictype) {
|
|
|
|
case 1:
|
|
|
|
/* special NIC type: reversed leds */
|
|
|
|
if (state == IWI_LED_ACTIVITY) {
|
|
|
|
state &= ~IWI_LED_ACTIVITY;
|
|
|
|
state |= IWI_LED_ASSOCIATED;
|
|
|
|
} else if (state == IWI_LED_ASSOCIATED) {
|
|
|
|
state &= ~IWI_LED_ASSOCIATED;
|
|
|
|
state |= IWI_LED_ACTIVITY;
|
|
|
|
}
|
|
|
|
/* and ignore toggle effect */
|
|
|
|
val |= state;
|
|
|
|
break;
|
|
|
|
case 0:
|
|
|
|
case 2:
|
|
|
|
case 3:
|
|
|
|
case 4:
|
|
|
|
val = (toggle && (val & state)) ? val & ~state : val | state;
|
|
|
|
break;
|
|
|
|
default:
|
2007-10-22 19:28:48 +04:00
|
|
|
aprint_normal_dev(sc->sc_dev, "unknown NIC type %d\n",
|
|
|
|
sc->nictype);
|
2005-11-29 16:57:00 +03:00
|
|
|
return;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
MEM_WRITE_4(sc, IWI_MEM_EVENT_CTL, val);
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
2008-10-30 03:27:31 +03:00
|
|
|
|
|
|
|
SYSCTL_SETUP(sysctl_hw_iwi_accept_eula_setup, "sysctl hw.iwi.accept_eula")
|
|
|
|
{
|
|
|
|
const struct sysctlnode *rnode;
|
|
|
|
const struct sysctlnode *cnode;
|
|
|
|
|
|
|
|
sysctl_createv(NULL, 0, NULL, &rnode,
|
|
|
|
CTLFLAG_PERMANENT,
|
|
|
|
CTLTYPE_NODE, "hw",
|
|
|
|
NULL,
|
|
|
|
NULL, 0,
|
|
|
|
NULL, 0,
|
|
|
|
CTL_HW, CTL_EOL);
|
|
|
|
|
|
|
|
sysctl_createv(NULL, 0, &rnode, &rnode,
|
|
|
|
CTLFLAG_PERMANENT,
|
|
|
|
CTLTYPE_NODE, "iwi",
|
|
|
|
NULL,
|
|
|
|
NULL, 0,
|
|
|
|
NULL, 0,
|
|
|
|
CTL_CREATE, CTL_EOL);
|
|
|
|
|
|
|
|
sysctl_createv(NULL, 0, &rnode, &cnode,
|
|
|
|
CTLFLAG_PERMANENT | CTLFLAG_READWRITE,
|
|
|
|
CTLTYPE_INT, "accept_eula",
|
|
|
|
SYSCTL_DESCR("Accept Intel EULA and permit use of iwi(4) firmware"),
|
|
|
|
NULL, 0,
|
|
|
|
&iwi_accept_eula, sizeof(iwi_accept_eula),
|
|
|
|
CTL_CREATE, CTL_EOL);
|
|
|
|
}
|