PERFORCE change 122988 for review
Andrew Thompson
thompsa at FreeBSD.org
Fri Jul 6 03:23:30 UTC 2007
http://perforce.freebsd.org/chv.cgi?CH=122988
Change 122988 by thompsa at thompsa_heff on 2007/07/06 03:23:28
Add in a large chunk of ipw changes by Sam, I am currently testing and
refining this.
Affected files ...
.. //depot/projects/wifi/sys/dev/ipw/if_ipw.c#19 edit
.. //depot/projects/wifi/sys/dev/ipw/if_ipwreg.h#3 edit
.. //depot/projects/wifi/sys/dev/ipw/if_ipwvar.h#5 edit
Differences ...
==== //depot/projects/wifi/sys/dev/ipw/if_ipw.c#19 (text+ko) ====
@@ -1,8 +1,7 @@
-/* $FreeBSD: src/sys/dev/ipw/if_ipw.c,v 1.28 2007/06/29 02:43:13 kevlo Exp $ */
-
/*-
* Copyright (c) 2004-2006
* Damien Bergamini <damien.bergamini at free.fr>. All rights reserved.
+ * Copyright (c) 2006 Sam Leffler, Errno Consulting
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -28,7 +27,7 @@
*/
#include <sys/cdefs.h>
-__FBSDID("$FreeBSD: src/sys/dev/ipw/if_ipw.c,v 1.28 2007/06/29 02:43:13 kevlo Exp $");
+__FBSDID("$FreeBSD: src/sys/dev/ipw/if_ipw.c,v 1.18 2006/03/13 20:05:32 damien Exp $");
/*-
* Intel(R) PRO/Wireless 2100 MiniPCI driver
@@ -78,10 +77,11 @@
#include <dev/ipw/if_ipwreg.h>
#include <dev/ipw/if_ipwvar.h>
+#define IPW_DEBUG
#ifdef IPW_DEBUG
#define DPRINTF(x) do { if (ipw_debug > 0) printf x; } while (0)
#define DPRINTFN(n, x) do { if (ipw_debug >= (n)) printf x; } while (0)
-int ipw_debug = 0;
+int ipw_debug = 4;
SYSCTL_INT(_debug, OID_AUTO, ipw, CTLFLAG_RW, &ipw_debug, 0, "ipw debug level");
#else
#define DPRINTF(x)
@@ -109,40 +109,66 @@
static int ipw_media_change(struct ifnet *);
static void ipw_media_status(struct ifnet *, struct ifmediareq *);
static int ipw_newstate(struct ieee80211com *, enum ieee80211_state, int);
-static uint16_t ipw_read_prom_word(struct ipw_softc *, uint8_t);
-static void ipw_command_intr(struct ipw_softc *, struct ipw_soft_buf *);
-static void ipw_newstate_intr(struct ipw_softc *, struct ipw_soft_buf *);
-static void ipw_data_intr(struct ipw_softc *, struct ipw_status *,
+static void ipw_rx_cmd_intr(struct ipw_softc *, struct ipw_soft_buf *);
+static void ipw_rx_newstate_intr(struct ipw_softc *, struct ipw_soft_buf *);
+static void ipw_rx_data_intr(struct ipw_softc *, struct ipw_status *,
struct ipw_soft_bd *, struct ipw_soft_buf *);
static void ipw_rx_intr(struct ipw_softc *);
static void ipw_release_sbd(struct ipw_softc *, struct ipw_soft_bd *);
static void ipw_tx_intr(struct ipw_softc *);
+static const char *ipw_cmdname(int);
static void ipw_intr(void *);
static void ipw_dma_map_addr(void *, bus_dma_segment_t *, int, int);
-static int ipw_cmd(struct ipw_softc *, uint32_t, void *, uint32_t);
+static int ipw_cmd(struct ipw_softc *, uint32_t, const void *, uint32_t);
static int ipw_tx_start(struct ifnet *, struct mbuf *,
struct ieee80211_node *);
+static void ipw_start_locked(struct ifnet *);
static void ipw_start(struct ifnet *);
-static void ipw_watchdog(struct ifnet *);
+static void ipw_watchdog(void *);
static int ipw_ioctl(struct ifnet *, u_long, caddr_t);
static void ipw_stop_master(struct ipw_softc *);
static int ipw_reset(struct ipw_softc *);
-static int ipw_load_ucode(struct ipw_softc *, const char *, int);
-static int ipw_load_firmware(struct ipw_softc *, const char *, int);
+static int ipw_enable(struct ipw_softc *);
+static int ipw_disable(struct ipw_softc *);
static int ipw_config(struct ipw_softc *);
-static void ipw_init_task(void *, int);
+static void ipw_restart(void *, int);
+static int ipw_scan(struct ipw_softc *);
+static void ipw_scanstart(void *, int);
+static void ipw_assoc_lost(void *, int);
+static void ipw_assoc(void *, int);
+static int ipw_auth_and_assoc(struct ipw_softc *);
+static int ipw_disassociate(struct ipw_softc *);
+static void ipw_down(void *, int);
static void ipw_init(void *);
+static void ipw_init_locked(struct ipw_softc *, int);
+static void ipw_stop_locked(struct ipw_softc *);
static void ipw_stop(void *);
static int ipw_sysctl_stats(SYSCTL_HANDLER_ARGS);
+static int ipw_getrfkill(struct ipw_softc *);
+static void ipw_radio_on(void *, int);
+static void ipw_radio_off(void *, int);
static int ipw_sysctl_radio(SYSCTL_HANDLER_ARGS);
+static int ipw_get_firmware(struct ipw_softc *);
+static void ipw_put_firmware(struct ipw_softc *);
+static int ipw_load_ucode(struct ipw_softc *, const struct ipw_fw *);
+static int ipw_load_firmware(struct ipw_softc *, const struct ipw_fw *);
static uint32_t ipw_read_table1(struct ipw_softc *, uint32_t);
static void ipw_write_table1(struct ipw_softc *, uint32_t, uint32_t);
+#if 0
static int ipw_read_table2(struct ipw_softc *, uint32_t, void *,
uint32_t *);
static void ipw_read_mem_1(struct ipw_softc *, bus_size_t, uint8_t *,
bus_size_t);
+#endif
static void ipw_write_mem_1(struct ipw_softc *, bus_size_t,
const uint8_t *, bus_size_t);
+static uint16_t ipw_read_prom_word(struct ipw_softc *, uint8_t);
+static void ipw_sysctlattach(struct ipw_softc *);
+static void ipw_scan_start(struct ieee80211com *);
+static void ipw_scan_end(struct ieee80211com *);
+static void ipw_set_channel(struct ieee80211com *);
+static void ipw_scan_curchan(struct ieee80211com *, unsigned long maxdwell);
+static void ipw_scan_mindwell(struct ieee80211com *);
static int ipw_probe(device_t);
static int ipw_attach(device_t);
@@ -172,6 +198,22 @@
static devclass_t ipw_devclass;
DRIVER_MODULE(ipw, pci, ipw_driver, ipw_devclass, 0, 0);
+DRIVER_MODULE(ipw, cardbus, ipw_driver, ipw_devclass, 0, 0);
+
+/*
+ * NB.: This models the only instance of async locking in ipw_init_locked
+ * and must be kept in sync.
+ */
+#define IPW_LOCK_DECL int __waslocked = 0
+#define IPW_LOCK(sc) do { \
+ if (!(__waslocked = mtx_owned(&(sc)->sc_mtx))) \
+ mtx_lock(&sc->sc_mtx); \
+} while (0)
+#define IPW_UNLOCK(sc) do { \
+ if (!__waslocked) \
+ mtx_unlock(&sc->sc_mtx); \
+} while (0)
+#define IPW_LOCK_ASSERT(sc) mtx_assert(&(sc)->sc_mtx, MA_OWNED)
static int
ipw_probe(device_t dev)
@@ -206,7 +248,25 @@
mtx_init(&sc->sc_mtx, device_get_nameunit(dev), MTX_NETWORK_LOCK,
MTX_DEF | MTX_RECURSE);
- TASK_INIT(&sc->sc_init_task, 0, ipw_init_task, sc);
+#if __FreeBSD_version >= 700000
+ sc->sc_tq = taskqueue_create("ipw_taskq", M_NOWAIT,
+ taskqueue_thread_enqueue, &sc->sc_tq);
+ taskqueue_start_threads(&sc->sc_tq, 1, PI_NET, "%s taskq",
+ device_get_nameunit(dev));
+#else
+ sc->sc_tq = taskqueue_create("ipw_taskq", M_NOWAIT,
+ taskqueue_thread_enqueue, &sc->sc_tq, &sc->sc_tqproc);
+ taskqueue_start_threads(&sc->sc_tq, 1, PI_NET, "%s taskq",
+ device_get_nameunit(dev));
+#endif
+ TASK_INIT(&sc->sc_radiontask, 0, ipw_radio_on, sc);
+ TASK_INIT(&sc->sc_radiofftask, 0, ipw_radio_off, sc);
+ TASK_INIT(&sc->sc_scanstarttask,0, ipw_scanstart, sc);
+ TASK_INIT(&sc->sc_assoclosttask,0, ipw_assoc_lost, sc);
+ TASK_INIT(&sc->sc_assoctask, 0, ipw_assoc, sc);
+ TASK_INIT(&sc->sc_downtask, 0, ipw_down, sc);
+ TASK_INIT(&sc->sc_restarttask, 0, ipw_restart, sc);
+ callout_init_mtx(&sc->sc_wdtimer, &sc->sc_mtx, 0);
if (pci_get_powerstate(dev) != PCI_POWERSTATE_D0) {
device_printf(dev, "chip is in D%d power mode "
@@ -260,7 +320,6 @@
ifp->if_init = ipw_init;
ifp->if_ioctl = ipw_ioctl;
ifp->if_start = ipw_start;
- ifp->if_watchdog = ipw_watchdog;
IFQ_SET_MAXLEN(&ifp->if_snd, IFQ_MAXLEN);
ifp->if_snd.ifq_drv_maxlen = IFQ_MAXLEN;
IFQ_SET_READY(&ifp->if_snd);
@@ -271,11 +330,12 @@
ic->ic_state = IEEE80211_S_INIT;
/* set device capabilities */
- 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 */
+ ic->ic_caps = IEEE80211_C_IBSS /* IBSS mode supported */
+ | IEEE80211_C_MONITOR /* monitor mode supported */
+ | IEEE80211_C_PMGT /* power save supported */
+ | IEEE80211_C_SHPREAMBLE /* short preamble supported */
+ | IEEE80211_C_WPA /* 802.11i supported */
+ ;
/* read MAC address from EEPROM */
val = ipw_read_prom_word(sc, IPW_EEPROM_MAC + 0);
@@ -303,7 +363,7 @@
/* check support for radio transmitter switch in EEPROM */
if (!(ipw_read_prom_word(sc, IPW_EEPROM_RADIO) & 8))
- sc->flags |= IPW_FLAG_HAS_RADIO_SWITCH;
+ sc->flags |= IPW_FLAG_HAS_RFSWITCH;
ieee80211_ifattach(ic);
/* override state transition machine */
@@ -311,8 +371,14 @@
ic->ic_newstate = ipw_newstate;
ieee80211_media_init(ic, ipw_media_change, ipw_media_status);
+ ic->ic_scan_start = ipw_scan_start;
+ ic->ic_scan_end = ipw_scan_end;
+ ic->ic_set_channel = ipw_set_channel;
+ ic->ic_scan_curchan = ipw_scan_curchan;
+ ic->ic_scan_mindwell = ipw_scan_mindwell;
+
bpfattach2(ifp, DLT_IEEE802_11_RADIO,
- sizeof (struct ieee80211_frame) + sizeof (sc->sc_txtap),
+ sizeof (struct ieee80211_frame) + sizeof (sc->sc_txtap),
&sc->sc_drvbpf);
sc->sc_rxtap_len = sizeof sc->sc_rxtap;
@@ -323,26 +389,8 @@
sc->sc_txtap.wt_ihdr.it_len = htole16(sc->sc_txtap_len);
sc->sc_txtap.wt_ihdr.it_present = htole32(IPW_TX_RADIOTAP_PRESENT);
- /*
- * Add a few sysctl knobs.
- */
- sc->dwelltime = 100;
-
- SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev),
- SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, "radio",
- CTLTYPE_INT | CTLFLAG_RD, sc, 0, ipw_sysctl_radio, "I",
- "radio transmitter switch state (0=off, 1=on)");
+ ipw_sysctlattach(sc);
- SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev),
- SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, "stats",
- CTLTYPE_OPAQUE | CTLFLAG_RD, sc, 0, ipw_sysctl_stats, "S",
- "statistics");
-
- SYSCTL_ADD_INT(device_get_sysctl_ctx(dev),
- SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, "dwell",
- CTLFLAG_RW, &sc->dwelltime, 0,
- "channel dwell time (ms) for AP/station scanning");
-
/*
* Hook our interrupt after all initialization is complete.
*/
@@ -370,6 +418,8 @@
struct ifnet *ifp = ic->ic_ifp;
ipw_stop(sc);
+ callout_drain(&sc->sc_wdtimer);
+ ipw_put_firmware(sc);
if (ifp != NULL) {
bpfdetach(ifp);
@@ -389,10 +439,7 @@
if (ifp != NULL)
if_free(ifp);
- if (sc->sc_firmware != NULL) {
- firmware_put(sc->sc_firmware, FIRMWARE_UNLOAD);
- sc->sc_firmware = NULL;
- }
+ taskqueue_free(sc->sc_tq);
mtx_destroy(&sc->sc_mtx);
@@ -692,6 +739,7 @@
struct ipw_softc *sc = device_get_softc(dev);
ipw_stop(sc);
+ ipw_put_firmware(sc); /* ??? XXX */
return 0;
}
@@ -711,18 +759,19 @@
{
struct ipw_softc *sc = device_get_softc(dev);
struct ifnet *ifp = sc->sc_ic.ic_ifp;
+ IPW_LOCK_DECL;
- mtx_lock(&sc->sc_mtx);
+ IPW_LOCK(sc);
pci_write_config(dev, 0x41, 0, 1);
if (ifp->if_flags & IFF_UP) {
- ifp->if_init(ifp->if_softc);
+ ipw_init_locked(sc, 0);
if (ifp->if_drv_flags & IFF_DRV_RUNNING)
- ifp->if_start(ifp);
+ ipw_start_locked(ifp);
}
- mtx_unlock(&sc->sc_mtx);
+ IPW_UNLOCK(sc);
return 0;
}
@@ -732,20 +781,30 @@
{
struct ipw_softc *sc = ifp->if_softc;
int error;
+ IPW_LOCK_DECL;
- mtx_lock(&sc->sc_mtx);
-
+ IPW_LOCK(sc);
error = ieee80211_media_change(ifp);
- if (error != ENETRESET) {
- mtx_unlock(&sc->sc_mtx);
- return error;
+ if (error == ENETRESET) {
+ if ((ifp->if_flags & IFF_UP) &&
+ (ifp->if_drv_flags & IFF_DRV_RUNNING))
+ ipw_init_locked(sc, 0);
+ error = 0;
}
+ IPW_UNLOCK(sc);
- if ((ifp->if_flags & IFF_UP) && (ifp->if_drv_flags & IFF_DRV_RUNNING))
- ipw_init(sc);
+ return error;
+}
- mtx_unlock(&sc->sc_mtx);
-
+static int
+ipw_cvtrate(int ipwrate)
+{
+ switch (ipwrate) {
+ case IPW_RATE_DS1: return 2;
+ case IPW_RATE_DS2: return 4;
+ case IPW_RATE_DS5: return 11;
+ case IPW_RATE_DS11: return 22;
+ }
return 0;
}
@@ -756,20 +815,9 @@
static void
ipw_media_status(struct ifnet *ifp, struct ifmediareq *imr)
{
-#define N(a) (sizeof (a) / sizeof (a[0]))
struct ipw_softc *sc = ifp->if_softc;
struct ieee80211com *ic = &sc->sc_ic;
- static const struct {
- uint32_t val;
- int rate;
- } rates[] = {
- { IPW_RATE_DS1, 2 },
- { IPW_RATE_DS2, 4 },
- { IPW_RATE_DS5, 11 },
- { IPW_RATE_DS11, 22 },
- };
- uint32_t val;
- int rate, i;
+ int rate;
imr->ifm_status = IFM_AVALID;
imr->ifm_active = IFM_IEEE80211;
@@ -777,33 +825,13 @@
imr->ifm_status |= IFM_ACTIVE;
/* read current transmission rate from adapter */
- val = ipw_read_table1(sc, IPW_INFO_CURRENT_TX_RATE) & 0xf;
-
- /* convert ipw rate to 802.11 rate */
- for (i = 0; i < N(rates) && rates[i].val != val; i++);
- rate = (i < N(rates)) ? rates[i].rate : 0;
-
- imr->ifm_active |= IFM_IEEE80211_11B;
+ rate = ipw_cvtrate(ipw_read_table1(sc, IPW_INFO_CURRENT_TX_RATE) & 0xf);
imr->ifm_active |= ieee80211_rate2media(ic, rate, IEEE80211_MODE_11B);
- switch (ic->ic_opmode) {
- case IEEE80211_M_STA:
- break;
- case IEEE80211_M_IBSS:
+ if (ic->ic_opmode == IEEE80211_M_IBSS)
imr->ifm_active |= IFM_IEEE80211_IBSS;
- break;
-
- case IEEE80211_M_MONITOR:
+ else if (ic->ic_opmode == IEEE80211_M_MONITOR)
imr->ifm_active |= IFM_IEEE80211_MONITOR;
- break;
-
- case IEEE80211_M_AHDEMO:
- case IEEE80211_M_HOSTAP:
- case IEEE80211_M_WDS:
- /* should not get there */
- break;
- }
-#undef N
}
static int
@@ -811,99 +839,55 @@
{
struct ifnet *ifp = ic->ic_ifp;
struct ipw_softc *sc = ifp->if_softc;
- uint8_t macaddr[IEEE80211_ADDR_LEN];
- uint32_t len;
+
+ DPRINTF(("%s: %s -> %s flags 0x%x\n", __func__,
+ ieee80211_state_name[ic->ic_state],
+ ieee80211_state_name[nstate], sc->flags));
switch (nstate) {
+ case IEEE80211_S_AUTH:
+ taskqueue_enqueue(sc->sc_tq, &sc->sc_assoctask);
+ break;
+
case IEEE80211_S_RUN:
- DELAY(200); /* firmware needs a short delay here */
+ if (ic->ic_opmode == IEEE80211_M_IBSS) {
+ /*
+ * XXX when joining an ibss network we are called
+ * with a SCAN -> RUN transition on scan complete.
+ * Use that to call ipw_auth_and_assoc. On completing
+ * the join we are then called again with an
+ * AUTH -> RUN transition and we want to do nothing.
+ * This is all totally bogus and needs to be redone.
+ */
+ if (ic->ic_state == IEEE80211_S_SCAN)
+ taskqueue_enqueue(sc->sc_tq, &sc->sc_assoctask);
+ }
+ /* XXX way wrong */
+ return sc->sc_newstate(ic, nstate,
+ IEEE80211_FC0_SUBTYPE_ASSOC_RESP);
+ break;
- len = IEEE80211_ADDR_LEN;
- ipw_read_table2(sc, IPW_INFO_CURRENT_BSSID, macaddr, &len);
-
-#if 0
- ni = ieee80211_find_node(&ic->ic_scan, macaddr);
- if (ni == NULL)
- break;
-
- ieee80211_ref_node(ni);
- ieee80211_sta_join(ic, ni);
- ieee80211_node_authorize(ni);
-
- if (ic->ic_opmode == IEEE80211_M_STA)
- ieee80211_notify_node_join(ic, ni, 1);
-#endif
+ case IEEE80211_S_ASSOC:
break;
case IEEE80211_S_INIT:
- case IEEE80211_S_SCAN:
- case IEEE80211_S_AUTH:
- case IEEE80211_S_ASSOC:
+ /*
+ * NB: don't try to do this if ipw_stop_master has
+ * shutdown the firmware and disabled interrupts.
+ */
+ if (ic->ic_state == IEEE80211_S_RUN &&
+ (sc->flags & IPW_FLAG_FW_INITED))
+ taskqueue_enqueue(sc->sc_tq, &sc->sc_downtask);
break;
- }
- ic->ic_state = nstate;
-
- return 0;
-}
-
-/*
- * Read 16 bits at address 'addr' from the serial EEPROM.
- */
-static uint16_t
-ipw_read_prom_word(struct ipw_softc *sc, uint8_t addr)
-{
- uint32_t tmp;
- uint16_t val;
- int n;
-
- /* clock C once before the first command */
- IPW_EEPROM_CTL(sc, 0);
- IPW_EEPROM_CTL(sc, IPW_EEPROM_S);
- IPW_EEPROM_CTL(sc, IPW_EEPROM_S | IPW_EEPROM_C);
- IPW_EEPROM_CTL(sc, IPW_EEPROM_S);
-
- /* write start bit (1) */
- IPW_EEPROM_CTL(sc, IPW_EEPROM_S | IPW_EEPROM_D);
- IPW_EEPROM_CTL(sc, IPW_EEPROM_S | IPW_EEPROM_D | IPW_EEPROM_C);
-
- /* write READ opcode (10) */
- IPW_EEPROM_CTL(sc, IPW_EEPROM_S | IPW_EEPROM_D);
- IPW_EEPROM_CTL(sc, IPW_EEPROM_S | IPW_EEPROM_D | IPW_EEPROM_C);
- IPW_EEPROM_CTL(sc, IPW_EEPROM_S);
- IPW_EEPROM_CTL(sc, IPW_EEPROM_S | IPW_EEPROM_C);
-
- /* write address A7-A0 */
- for (n = 7; n >= 0; n--) {
- IPW_EEPROM_CTL(sc, IPW_EEPROM_S |
- (((addr >> n) & 1) << IPW_EEPROM_SHIFT_D));
- IPW_EEPROM_CTL(sc, IPW_EEPROM_S |
- (((addr >> n) & 1) << IPW_EEPROM_SHIFT_D) | IPW_EEPROM_C);
+ default:
+ break;
}
-
- IPW_EEPROM_CTL(sc, IPW_EEPROM_S);
-
- /* read data Q15-Q0 */
- val = 0;
- for (n = 15; n >= 0; n--) {
- IPW_EEPROM_CTL(sc, IPW_EEPROM_S | IPW_EEPROM_C);
- IPW_EEPROM_CTL(sc, IPW_EEPROM_S);
- tmp = MEM_READ_4(sc, IPW_MEM_EEPROM_CTL);
- val |= ((tmp & IPW_EEPROM_Q) >> IPW_EEPROM_SHIFT_Q) << n;
- }
-
- IPW_EEPROM_CTL(sc, 0);
-
- /* clear Chip Select and clock C */
- IPW_EEPROM_CTL(sc, IPW_EEPROM_S);
- IPW_EEPROM_CTL(sc, 0);
- IPW_EEPROM_CTL(sc, IPW_EEPROM_C);
-
- return le16toh(val);
+ return sc->sc_newstate(ic, nstate, arg);
}
static void
-ipw_command_intr(struct ipw_softc *sc, struct ipw_soft_buf *sbuf)
+ipw_rx_cmd_intr(struct ipw_softc *sc, struct ipw_soft_buf *sbuf)
{
struct ipw_cmd *cmd;
@@ -911,16 +895,18 @@
cmd = mtod(sbuf->m, struct ipw_cmd *);
- DPRINTFN(2, ("cmd ack'ed (%u, %u, %u, %u, %u)\n", le32toh(cmd->type),
- le32toh(cmd->subtype), le32toh(cmd->seq), le32toh(cmd->len),
- le32toh(cmd->status)));
+ DPRINTFN(9, ("%s(%s subtype %u seq %u len %u status %u)\n",
+ __func__, ipw_cmdname(le32toh(cmd->type)), le32toh(cmd->subtype),
+ le32toh(cmd->seq), le32toh(cmd->len), le32toh(cmd->status)));
- wakeup(sc);
+ sc->flags &= ~IPW_FLAG_BUSY;
+ wakeup_one(&sc->cmd);
}
static void
-ipw_newstate_intr(struct ipw_softc *sc, struct ipw_soft_buf *sbuf)
+ipw_rx_newstate_intr(struct ipw_softc *sc, struct ipw_soft_buf *sbuf)
{
+#define IEEESTATE(ic) ieee80211_state_name[ic->ic_state]
struct ieee80211com *ic = &sc->sc_ic;
uint32_t state;
@@ -928,43 +914,106 @@
state = le32toh(*mtod(sbuf->m, uint32_t *));
- DPRINTFN(2, ("entering state %u\n", state));
+ switch (state) {
+ case IPW_STATE_INITIALIZED:
+ case IPW_STATE_DISABLED:
+ break;
- switch (state) {
case IPW_STATE_ASSOCIATED:
- ieee80211_new_state(ic, IEEE80211_S_RUN, -1);
+ DPRINTFN(2, ("Association succeeded (%s flags 0x%x)\n",
+ IEEESTATE(ic), sc->flags));
+ sc->flags |= IPW_FLAG_ASSOCIATED;
+ /* XXX suppress state change in case the fw auto-associates */
+ if (ic->ic_state != IEEE80211_S_ASSOC) {
+ DPRINTF(("Unexpected association (state %u)\n",
+ ic->ic_state));
+ } else
+ ieee80211_new_state(ic, IEEE80211_S_RUN, -1);
break;
case IPW_STATE_SCANNING:
- /* don't leave run state on background scan */
- if (ic->ic_state != IEEE80211_S_RUN)
- ieee80211_new_state(ic, IEEE80211_S_SCAN, -1);
-
- ic->ic_flags |= IEEE80211_F_SCAN;
+ DPRINTFN(3, ("Scanning (%s flags 0x%x)\n",
+ IEEESTATE(ic), sc->flags));
+ /*
+ * NB: Check driver state for association on assoc
+ * loss as the firmware will immediately start to
+ * scan and we would treat it as a beacon miss if
+ * we checked the 802.11 layer state.
+ */
+ if (sc->flags & IPW_FLAG_ASSOCIATED)
+ ieee80211_beacon_miss(ic);
break;
case IPW_STATE_SCAN_COMPLETE:
- ieee80211_notify_scan_done(ic);
- ic->ic_flags &= ~IEEE80211_F_SCAN;
+ DPRINTFN(3, ("Scan complete (%s flags 0x%x)\n",
+ IEEESTATE(ic), sc->flags));
+ /*
+ * XXX For some reason scan requests generate scan
+ * started + scan done events before any traffic is
+ * received (e.g. probe response frames). We work
+ * around this by marking the HACK flag and skipping
+ * the first scan complete event. This works ok
+ * because the adapter scans only 2.4G channels so
+ * doing an extra pass doesn't take long.
+ */
+ if (sc->flags & IPW_FLAG_HACK) {
+ sc->flags &= ~IPW_FLAG_HACK;
+ break;
+ }
+ sc->sc_scan_timer = 0;
+ sc->flags &= ~IPW_FLAG_SCANNING;
+ ieee80211_scan_done(ic);
break;
case IPW_STATE_ASSOCIATION_LOST:
- ieee80211_new_state(ic, IEEE80211_S_INIT, -1);
+ DPRINTFN(2, ("Association lost (%s flags 0x%x)\n",
+ IEEESTATE(ic), sc->flags));
+ sc->flags &= ~IPW_FLAG_ASSOCIATED;
+ taskqueue_enqueue(sc->sc_tq, &sc->sc_assoclosttask);
break;
- case IPW_STATE_RADIO_DISABLED:
- ic->ic_ifp->if_flags &= ~IFF_UP;
- ipw_stop(sc);
+ case IPW_STATE_RADIO_OFF:
+ DPRINTFN(2, ("Radio off (%s flags 0x%x)\n",
+ IEEESTATE(ic), sc->flags));
+ taskqueue_enqueue(sc->sc_tq, &sc->sc_radiofftask);
+ break;
+ default:
+ DPRINTFN(2, ("%s: state %u %s flags 0x%x\n",
+ __func__, state, IEEESTATE(ic), sc->flags));
break;
}
+#undef IEEESTATE
}
/*
+ * Set driver state for current channel.
+ */
+static void
+ipw_setcurchan(struct ipw_softc *sc, int chan)
+{
+ struct ieee80211com *ic = &sc->sc_ic;
+
+ if (chan == sc->curchan)
+ return;
+
+ ic->ic_curchan = ieee80211_find_channel(ic,
+ ieee80211_ieee2mhz(chan, 0),
+ IEEE80211_CHAN_B);
+ sc->curchan = chan;
+
+ sc->sc_rxtap.wr_chan_freq = sc->sc_txtap.wt_chan_freq =
+ htole16(ic->ic_curchan->ic_freq);
+ sc->sc_rxtap.wr_chan_flags = sc->sc_txtap.wt_chan_flags =
+ htole16(ic->ic_curchan->ic_flags);
+}
+
+/*
* XXX: Hack to set the current channel to the value advertised in beacons or
* probe responses. Only used during AP detection.
+ * XXX value comes from DSPARMS ie which is wrong for off-channel rx's
*/
static void
-ipw_fix_channel(struct ieee80211com *ic, struct mbuf *m)
+ipw_fix_channel(struct ipw_softc *sc, struct mbuf *m)
{
struct ieee80211_frame *wh;
uint8_t subtype;
@@ -990,16 +1039,14 @@
#if IEEE80211_CHAN_MAX < 255
if (frm[2] <= IEEE80211_CHAN_MAX)
#endif
- ic->ic_bsschan = ieee80211_find_channel(ic,
- ieee80211_ieee2mhz(frm[2], 0),
- IEEE80211_CHAN_B);
+ ipw_setcurchan(sc, frm[2]);
frm += frm[1] + 2;
}
}
static void
-ipw_data_intr(struct ipw_softc *sc, struct ipw_status *status,
+ipw_rx_data_intr(struct ipw_softc *sc, struct ipw_status *status,
struct ipw_soft_bd *sbd, struct ipw_soft_buf *sbuf)
{
struct ieee80211com *ic = &sc->sc_ic;
@@ -1008,15 +1055,25 @@
struct ieee80211_frame *wh;
struct ieee80211_node *ni;
bus_addr_t physaddr;
- int error;
+ int error, framelen;
+ IPW_LOCK_DECL;
+
+ framelen = le32toh(status->len);
+ if (framelen < IEEE80211_MIN_LEN || framelen > MCLBYTES) {
+ /*
+ * XXX >MCLBYTES is bogus as it means the h/w dma'd
+ * out of bounds; need to figure out how to limit
+ * frame size in the firmware
+ */
+ /* XXX stat */
+ DPRINTF(("drop rx frame len=%u rssi=%u\n",
+ framelen, status->rssi));
+ return;
+ }
DPRINTFN(5, ("received frame len=%u, rssi=%u\n", le32toh(status->len),
status->rssi));
- if (le32toh(status->len) < sizeof (struct ieee80211_frame_min) ||
- le32toh(status->len) > MCLBYTES)
- return;
-
/*
* Try to allocate a new mbuf for this ring element and load it before
* processing the current mbuf. If the ring element cannot be loaded,
@@ -1062,22 +1119,21 @@
m->m_pkthdr.rcvif = ifp;
m->m_pkthdr.len = m->m_len = le32toh(status->len);
- if (bpf_peers_present(sc->sc_drvbpf)) {
+ /* XXX */
+ if (sc->flags & IPW_FLAG_SCANNING)
+ ipw_fix_channel(sc, m);
+
+ if (sc->sc_drvbpf != NULL) {
struct ipw_rx_radiotap_header *tap = &sc->sc_rxtap;
tap->wr_flags = 0;
- tap->wr_antsignal = status->rssi;
- tap->wr_chan_freq = htole16(ic->ic_curchan->ic_freq);
- tap->wr_chan_flags = htole16(ic->ic_curchan->ic_flags);
+ tap->wr_antsignal = status->rssi + IPW_RSSI_TO_DBM;
bpf_mtap2(sc->sc_drvbpf, tap, sc->sc_rxtap_len, m);
}
- if (ic->ic_state == IEEE80211_S_SCAN)
- ipw_fix_channel(ic, m);
-
wh = mtod(m, struct ieee80211_frame *);
- mtx_unlock(&sc->sc_mtx);
+ IPW_UNLOCK(sc);
ni = ieee80211_find_rxnode(ic, (struct ieee80211_frame_min *)wh);
/* send the frame to the 802.11 layer */
@@ -1085,7 +1141,7 @@
/* node is no longer needed */
ieee80211_free_node(ni);
- mtx_lock(&sc->sc_mtx);
+ IPW_LOCK(sc);
bus_dmamap_sync(sc->rbd_dmat, sc->rbd_map, BUS_DMASYNC_PREWRITE);
}
@@ -1093,6 +1149,7 @@
static void
ipw_rx_intr(struct ipw_softc *sc)
{
+ struct ieee80211com *ic = &sc->sc_ic;
struct ipw_status *status;
struct ipw_soft_bd *sbd;
struct ipw_soft_buf *sbuf;
@@ -1112,31 +1169,33 @@
switch (le16toh(status->code) & 0xf) {
case IPW_STATUS_CODE_COMMAND:
- ipw_command_intr(sc, sbuf);
+ ipw_rx_cmd_intr(sc, sbuf);
break;
case IPW_STATUS_CODE_NEWSTATE:
- ipw_newstate_intr(sc, sbuf);
+ ipw_rx_newstate_intr(sc, sbuf);
break;
case IPW_STATUS_CODE_DATA_802_3:
case IPW_STATUS_CODE_DATA_802_11:
- ipw_data_intr(sc, status, sbd, sbuf);
+ ipw_rx_data_intr(sc, status, sbd, sbuf);
break;
case IPW_STATUS_CODE_NOTIFICATION:
- DPRINTFN(2, ("received notification\n"));
+ DPRINTFN(2, ("notification status, len %u flags 0x%x\n",
+ le32toh(status->len), status->flags));
+ if (ic->ic_state == IEEE80211_S_AUTH) {
+ /* XXX assume auth notification */
+ ieee80211_node_authorize(ic->ic_bss);
+ ieee80211_new_state(ic, IEEE80211_S_ASSOC, -1);
+ }
break;
default:
- device_printf(sc->sc_dev, "unknown status code %u\n",
+ device_printf(sc->sc_dev, "unexpected status code %u\n",
le16toh(status->code));
+ break;
}
-
- /* firmware was killed, stop processing received frames */
- if (!(sc->flags & IPW_FLAG_FW_INITED))
- return;
-
sbd->bd->flags = 0;
}
@@ -1174,12 +1233,8 @@
bus_dmamap_unload(sc->txbuf_dmat, sbuf->map);
SLIST_INSERT_HEAD(&sc->free_sbuf, sbuf, next);
- if (sbuf->m->m_flags & M_TXCB)
- ieee80211_process_callback(sbuf->ni, sbuf->m, 0/*XXX*/);
m_freem(sbuf->m);
ieee80211_free_node(sbuf->ni);
-
- sc->sc_tx_timer = 0;
break;
}
@@ -1211,8 +1266,10 @@
/* remember what the firmware has processed */
sc->txold = (r == 0) ? IPW_NTBD - 1 : r - 1;
+ sc->sc_tx_timer = 0;
ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
- ipw_start(ifp);
+
+ ipw_start_locked(ifp);
}
static void
@@ -1222,36 +1279,39 @@
uint32_t r;
mtx_lock(&sc->sc_mtx);
+ r = CSR_READ_4(sc, IPW_CSR_INTR);
+ if (r != 0 && r != 0xffffffff) {
+ /* acknowledge all interrupts */
+ CSR_WRITE_4(sc, IPW_CSR_INTR, r);
- if ((r = CSR_READ_4(sc, IPW_CSR_INTR)) == 0 || r == 0xffffffff) {
- mtx_unlock(&sc->sc_mtx);
- return;
- }
+ DPRINTFN(9, ("%s: 0x%x\n", __func__, r));
- /* disable interrupts */
- CSR_WRITE_4(sc, IPW_CSR_INTR_MASK, 0);
-
- /* acknowledge all interrupts */
- CSR_WRITE_4(sc, IPW_CSR_INTR, r);
-
- if (r & (IPW_INTR_FATAL_ERROR | IPW_INTR_PARITY_ERROR)) {
- device_printf(sc->sc_dev, "firmware error\n");
- taskqueue_enqueue_fast(taskqueue_fast, &sc->sc_init_task);
- r = 0; /* don't process more interrupts */
+ if (r & IPW_INTR_FATAL_ERROR) {
+ device_printf(sc->sc_dev, "firmware error\n");
+ taskqueue_enqueue(sc->sc_tq, &sc->sc_restarttask);
+ r = 0; /* NB: don't do anything else */
+ }
+ if (r & IPW_INTR_FW_INIT_DONE) {
+ wakeup_one(sc);
+ r &= ~IPW_INTR_FW_INIT_DONE;
+ }
+ if (r & IPW_INTR_RX_TRANSFER) {
+ ipw_rx_intr(sc);
+ r &= ~IPW_INTR_RX_TRANSFER;
+ }
+ if (r & IPW_INTR_TX_TRANSFER) {
+ ipw_tx_intr(sc);
+ r &= ~IPW_INTR_TX_TRANSFER;
+ }
+ if (r & IPW_INTR_PARITY_ERROR) {
+ /* XXX rate limit */
+ device_printf(sc->sc_dev, "parity error\n");
+ r &= ~IPW_INTR_PARITY_ERROR;
+ }
+ if (r != 0)
+ device_printf(sc->sc_dev,
+ "%s: 0x%x unserviced\n", __func__, r);
}
-
- if (r & IPW_INTR_FW_INIT_DONE)
- wakeup(sc);
-
- if (r & IPW_INTR_RX_TRANSFER)
- ipw_rx_intr(sc);
-
- if (r & IPW_INTR_TX_TRANSFER)
- ipw_tx_intr(sc);
-
- /* re-enable interrupts */
- CSR_WRITE_4(sc, IPW_CSR_INTR_MASK, IPW_INTR_MASK);
-
mtx_unlock(&sc->sc_mtx);
}
@@ -1266,26 +1326,87 @@
*(bus_addr_t *)arg = segs[0].ds_addr;
}
+static const char *
+ipw_cmdname(int cmd)
+{
+#define N(a) (sizeof(a) / sizeof(a[0]))
+ static const struct {
+ int cmd;
+ const char *name;
+ } cmds[] = {
+ { IPW_CMD_ADD_MULTICAST, "ADD_MULTICAST" },
+ { IPW_CMD_BROADCAST_SCAN, "BROADCAST_SCAN" },
+ { IPW_CMD_DISABLE, "DISABLE" },
+ { IPW_CMD_DISABLE_PHY, "DISABLE_PHY" },
+ { IPW_CMD_DISASSOCIATE, "DISASSOCIATE" },
+ { IPW_CMD_ENABLE, "ENABLE" },
+ { IPW_CMD_PREPARE_POWER_DOWN, "PREPARE_POWER_DOWN" },
+ { IPW_CMD_SET_BASIC_TX_RATES, "SET_BASIC_TX_RATES" },
+ { IPW_CMD_SET_BEACON_INTERVAL, "SET_BEACON_INTERVAL" },
+ { IPW_CMD_SET_CHANNEL, "SET_CHANNEL" },
+ { IPW_CMD_SET_CONFIGURATION, "SET_CONFIGURATION" },
+ { IPW_CMD_SET_DESIRED_BSSID, "SET_DESIRED_BSSID" },
+ { IPW_CMD_SET_ESSID, "SET_ESSID" },
+ { IPW_CMD_SET_FRAG_THRESHOLD, "SET_FRAG_THRESHOLD" },
+ { IPW_CMD_SET_LONG_RETRY, "SET_LONG_RETRY" },
+ { IPW_CMD_SET_MAC_ADDRESS, "SET_MAC_ADDRESS" },
+ { IPW_CMD_SET_MANDATORY_BSSID, "SET_MANDATORY_BSSID" },
+ { IPW_CMD_SET_MODE, "SET_MODE" },
+ { IPW_CMD_SET_MSDU_TX_RATES, "SET_MSDU_TX_RATES" },
+ { IPW_CMD_SET_POWER_MODE, "SET_POWER_MODE" },
+ { IPW_CMD_SET_RTS_THRESHOLD, "SET_RTS_THRESHOLD" },
+ { IPW_CMD_SET_SCAN_DWELL_TIME, "SET_SCAN_DWELL_TIME" },
+ { IPW_CMD_SET_SCAN_OPTIONS, "SET_SCAN_OPTIONS" },
+ { IPW_CMD_SET_SECURITY_INFO, "SET_SECURITY_INFO" },
+ { IPW_CMD_SET_SHORT_RETRY, "SET_SHORT_RETRY" },
+ { IPW_CMD_SET_TX_POWER_INDEX, "SET_TX_POWER_INDEX" },
+ { IPW_CMD_SET_TX_RATES, "SET_TX_RATES" },
+ { IPW_CMD_SET_WEP_FLAGS, "SET_WEP_FLAGS" },
+ { IPW_CMD_SET_WEP_KEY, "SET_WEP_KEY" },
+ { IPW_CMD_SET_WEP_KEY_INDEX, "SET_WEP_KEY_INDEX" },
+ { IPW_CMD_SET_WPA_IE, "SET_WPA_IE" },
+
+ };
+ static char buf[12];
+ int i;
+
+ for (i = 0; i < N(cmds); i++)
+ if (cmds[i].cmd == cmd)
+ return cmds[i].name;
+ snprintf(buf, sizeof(buf), "%u", cmd);
+ return buf;
+#undef N
+}
+
/*
* Send a command to the firmware and wait for the acknowledgement.
*/
static int
-ipw_cmd(struct ipw_softc *sc, uint32_t type, void *data, uint32_t len)
+ipw_cmd(struct ipw_softc *sc, uint32_t cmd, const void *data, uint32_t len)
{
struct ipw_soft_bd *sbd;
bus_addr_t physaddr;
int error;
+ if (sc->flags & IPW_FLAG_BUSY) {
+ device_printf(sc->sc_dev, "%s: %s not sent, busy\n",
+ __func__, ipw_cmdname(cmd));
+ return EAGAIN;
+ }
+ sc->flags |= IPW_FLAG_BUSY;
+
sbd = &sc->stbd_list[sc->txcur];
error = bus_dmamap_load(sc->cmd_dmat, sc->cmd_map, &sc->cmd,
sizeof (struct ipw_cmd), ipw_dma_map_addr, &physaddr, 0);
if (error != 0) {
- device_printf(sc->sc_dev, "could not map command DMA memory\n");
+ device_printf(sc->sc_dev,
>>> TRUNCATED FOR MAIL (1000 lines) <<<
More information about the p4-projects
mailing list