Skip site navigation (1)Skip section navigation (2)
Date:      Fri, 28 Sep 2007 21:46:08 GMT
From:      Andrew Thompson <thompsa@FreeBSD.org>
To:        Perforce Change Reviews <perforce@freebsd.org>
Subject:   PERFORCE change 126945 for review
Message-ID:  <200709282146.l8SLk8e1057313@repoman.freebsd.org>

next in thread | raw e-mail | index | archive | help
http://perforce.freebsd.org/chv.cgi?CH=126945

Change 126945 by thompsa@thompsa_heff on 2007/09/28 21:45:44

	Drop in a reduced set of changes to HEAD which seem to work quite well.

Affected files ...

.. //depot/projects/wifi/sys/dev/ipw/if_ipw.c#26 edit
.. //depot/projects/wifi/sys/dev/ipw/if_ipwreg.h#6 edit
.. //depot/projects/wifi/sys/dev/ipw/if_ipwvar.h#8 edit

Differences ...

==== //depot/projects/wifi/sys/dev/ipw/if_ipw.c#26 (text+ko) ====

@@ -1,3 +1,5 @@
+/*	$FreeBSD: src/sys/dev/ipw/if_ipw.c,v 1.30 2007/09/05 21:31:31 sam Exp $	*/
+
 /*-
  * Copyright (c) 2004-2006
  *      Damien Bergamini <damien.bergamini@free.fr>. All rights reserved.
@@ -81,7 +83,7 @@
 #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 = 40;
+int ipw_debug = 3;
 SYSCTL_INT(_debug, OID_AUTO, ipw, CTLFLAG_RW, &ipw_debug, 0, "ipw debug level");
 #else
 #define DPRINTF(x)
@@ -109,49 +111,37 @@
 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 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 *,
+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 *,
 		    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, const void *, uint32_t);
+static const char * ipw_cmdname(int);
+static int	ipw_cmd(struct ipw_softc *, uint32_t, 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(void *);
+static void	ipw_watchdog(struct ifnet *);
 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_enable(struct ipw_softc *);
 static int	ipw_disable(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 void	ipw_assoc_task(void *, int);
+static int	ipw_auth_and_assoc(struct ipw_softc *);
 static int	ipw_config(struct ipw_softc *);
-static void	ipw_restart(void *, int);
-static int	ipw_scan(struct ipw_softc *);
-static void	ipw_assoc_lost(void *, int);
-static void	ipw_assoc(struct ieee80211com *);
-static void	ipw_disassoc(struct ieee80211com *);
-static int	ipw_auth_and_assoc(struct ipw_softc *);
-static int	ipw_disassociate(struct ipw_softc *);
-static void	ipw_ops(void *, int);
+static void	ipw_init_task(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
@@ -162,8 +152,8 @@
 #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_task(void *, int);
+static int	ipw_scan(struct ipw_softc *);
 static void	ipw_scan_start(struct ieee80211com *);
 static void	ipw_scan_end(struct ieee80211com *);
 static void	ipw_set_channel(struct ieee80211com *);
@@ -232,25 +222,10 @@
 
 	mtx_init(&sc->sc_mtx, device_get_nameunit(dev), MTX_NETWORK_LOCK,
 	    MTX_DEF | MTX_RECURSE);
-	IPW_CMD_LOCK_INIT(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_assoclosttask,0, ipw_assoc_lost, sc);
-	TASK_INIT(&sc->sc_restarttask,	0, ipw_restart, sc);
-	TASK_INIT(&sc->sc_opstask,	0, ipw_ops, sc);
-	callout_init_mtx(&sc->sc_wdtimer, &sc->sc_mtx, 0);
+	TASK_INIT(&sc->sc_init_task, 0, ipw_init_task, sc);
+	TASK_INIT(&sc->sc_scan_task, 0, ipw_scan_task, sc);
+	TASK_INIT(&sc->sc_assoc_task, 0, ipw_assoc_task, sc);
 
 	if (pci_get_powerstate(dev) != PCI_POWERSTATE_D0) {
 		device_printf(dev, "chip is in D%d power mode "
@@ -304,6 +279,7 @@
 	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);
@@ -314,12 +290,11 @@
 	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_PMGT		/* power save supported */
-		| IEEE80211_C_SHPREAMBLE	/* short preamble supported */
-		| IEEE80211_C_WPA		/* 802.11i supported */
-		;
+	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 */
 
 	/* read MAC address from EEPROM */
 	val = ipw_read_prom_word(sc, IPW_EEPROM_MAC + 0);
@@ -335,7 +310,6 @@
 	/* set supported .11b channels (read from EEPROM) */
 	if ((val = ipw_read_prom_word(sc, IPW_EEPROM_CHANNEL_LIST)) == 0)
 		val = 0x7ff; /* default to channels 1-11 */
-	sc->chanmask = val;
 	val <<= 1;
 	for (i = 1; i < 16; i++) {
 		if (val & (1 << i)) {
@@ -345,11 +319,10 @@
 			c->ic_ieee = i;
 		}
 	}
-	DPRINTF(("%s: added %d channels\n", __func__, ic->ic_nchans));
 
 	/* check support for radio transmitter switch in EEPROM */
 	if (!(ipw_read_prom_word(sc, IPW_EEPROM_RADIO) & 8))
-		sc->flags |= IPW_FLAG_HAS_RFSWITCH;
+		sc->flags |= IPW_FLAG_HAS_RADIO_SWITCH;
 
 	ieee80211_ifattach(ic);
 	/* override state transition machine */
@@ -364,7 +337,7 @@
 	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;
@@ -375,7 +348,25 @@
 	sc->sc_txtap.wt_ihdr.it_len = htole16(sc->sc_txtap_len);
 	sc->sc_txtap.wt_ihdr.it_present = htole32(IPW_TX_RADIOTAP_PRESENT);
 
-	ipw_sysctlattach(sc);
+	/*
+	 * 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)");
+
+	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.
@@ -404,8 +395,6 @@
 	struct ifnet *ifp = ic->ic_ifp;
 
 	ipw_stop(sc);
-	callout_drain(&sc->sc_wdtimer);
-	ipw_put_firmware(sc);
 
 	if (ifp != NULL) {
 		bpfdetach(ifp);
@@ -425,10 +414,12 @@
 	if (ifp != NULL)
 		if_free(ifp);
 
-	taskqueue_free(sc->sc_tq);
+	if (sc->sc_firmware != NULL) {
+		firmware_put(sc->sc_firmware, FIRMWARE_UNLOAD);
+		sc->sc_firmware = NULL;
+	}
 
 	mtx_destroy(&sc->sc_mtx);
-	IPW_CMD_LOCK_DESTROY(sc);
 
 	return 0;
 }
@@ -726,7 +717,6 @@
 	struct ipw_softc *sc = device_get_softc(dev);
 
 	ipw_stop(sc);
-	ipw_put_firmware(sc);		/* ??? XXX */
 
 	return 0;
 }
@@ -746,19 +736,18 @@
 {
 	struct ipw_softc *sc = device_get_softc(dev);
 	struct ifnet *ifp = sc->sc_ic.ic_ifp;
-	IPW_LOCK_DECL;
 
-	IPW_LOCK(sc);
+	mtx_lock(&sc->sc_mtx);
 
 	pci_write_config(dev, 0x41, 0, 1);
 
 	if (ifp->if_flags & IFF_UP) {
-		ipw_init_locked(sc, 0);
+		ifp->if_init(ifp->if_softc);
 		if (ifp->if_drv_flags & IFF_DRV_RUNNING)
-			ipw_start_locked(ifp);
+			ifp->if_start(ifp);
 	}
 
-	IPW_UNLOCK(sc);
+	mtx_unlock(&sc->sc_mtx);
 
 	return 0;
 }
@@ -768,30 +757,20 @@
 {
 	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) {
-		if ((ifp->if_flags & IFF_UP) &&
-		    (ifp->if_drv_flags & IFF_DRV_RUNNING))
-			ipw_init_locked(sc, 0);
-		error = 0;
+	if (error != ENETRESET) {
+		mtx_unlock(&sc->sc_mtx);
+		return error;
 	}
-	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;
 }
 
@@ -802,9 +781,20 @@
 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;
-	int rate;
+	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;
 
 	imr->ifm_status = IFM_AVALID;
 	imr->ifm_active = IFM_IEEE80211;
@@ -812,13 +802,33 @@
 		imr->ifm_status |= IFM_ACTIVE;
 
 	/* read current transmission rate from adapter */
-	rate = ipw_cvtrate(ipw_read_table1(sc, IPW_INFO_CURRENT_TX_RATE) & 0xf);
+	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;
 	imr->ifm_active |= ieee80211_rate2media(ic, rate, IEEE80211_MODE_11B);
+	switch (ic->ic_opmode) {
+	case IEEE80211_M_STA:
+		break;
 
-	if (ic->ic_opmode == IEEE80211_M_IBSS)
+	case IEEE80211_M_IBSS:
 		imr->ifm_active |= IFM_IEEE80211_IBSS;
-	else if (ic->ic_opmode == IEEE80211_M_MONITOR)
+		break;
+
+	case 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
@@ -826,56 +836,129 @@
 {
 	struct ifnet *ifp = ic->ic_ifp;
 	struct ipw_softc *sc = ifp->if_softc;
+#if 0
+	struct ieee80211_node *ni;
+	uint32_t len, error;
+#endif
 
 	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:
-		ipw_assoc(ic);
-		break;
+	case IEEE80211_S_RUN:
+#if 0
+		ni = ic->ic_bss;
+		DELAY(200); /* firmware needs a short delay here */
+
+		len = IEEE80211_ADDR_LEN;
+		ipw_read_table2(sc, IPW_INFO_CURRENT_BSSID, ni->ni_bssid, &len);
+		IEEE80211_ADDR_COPY(ni->ni_macaddr, ni->ni_bssid);
+
+		if (ic->ic_opmode != IEEE80211_M_HOSTAP) {
+			len = IEEE80211_NWID_LEN;
+			error = ipw_read_table2(sc, IPW_INFO_CURRENT_SSID,
+			    ni->ni_essid, &len);
 
-	case IEEE80211_S_RUN:
-		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)
-				ipw_assoc(ic);
+			if (error == 0) {
+				ni->ni_esslen = len;
+#ifdef IPW_DEBUG
+				if (ipw_debug > 0) {
+					printf("Associated to ");
+					ieee80211_print_essid(ni->ni_essid,
+					    ni->ni_esslen);
+					printf("\n");
+				}
+#endif
+			}
 		}
-		/* XXX way wrong */
-		return sc->sc_newstate(ic, nstate,
-		    IEEE80211_FC0_SUBTYPE_ASSOC_RESP);
+#endif
+		return (*sc->sc_newstate)(ic, nstate, arg);
+
+	case IEEE80211_S_INIT:
+	case IEEE80211_S_SCAN:
+		return sc->sc_newstate(ic, nstate, arg);
+
+	case IEEE80211_S_AUTH:
+		taskqueue_enqueue_fast(taskqueue_fast, &sc->sc_assoc_task);
 		break;
 
 	case IEEE80211_S_ASSOC:
-		break;
-
-	case IEEE80211_S_INIT:
 		/*
-		 * NB: don't try to do this if ipw_stop_master has
-		 *     shutdown the firmware and disabled interrupts.
+		 * If we are not transitioning from AUTH the resend the
+		 * association request.
 		 */
-		if (ic->ic_state == IEEE80211_S_RUN &&
-		    (sc->flags & IPW_FLAG_FW_INITED))
-			ipw_disassoc(ic);
-	default:
+		if (ic->ic_state != IEEE80211_S_AUTH)
+			taskqueue_enqueue_fast(taskqueue_fast,
+			    &sc->sc_assoc_task);
 		break;
 
 	default:
 		break;
 	}
-	return sc->sc_newstate(ic, nstate, arg);
+
+	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);
+	}
+
+	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);
 }
 
 static void
-ipw_rx_cmd_intr(struct ipw_softc *sc, struct ipw_soft_buf *sbuf)
+ipw_command_intr(struct ipw_softc *sc, struct ipw_soft_buf *sbuf)
 {
 	struct ipw_cmd *cmd;
 
@@ -883,16 +966,17 @@
 
 	cmd = mtod(sbuf->m, struct ipw_cmd *);
 
-	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)));
+	DPRINTFN(4, ("cmd ack'ed %s(%u, %u, %u, %u, %u)\n",
+	    ipw_cmdname(le32toh(cmd->type)), le32toh(cmd->type),
+	    le32toh(cmd->subtype), le32toh(cmd->seq), le32toh(cmd->len),
+	    le32toh(cmd->status)));
 
 	sc->flags &= ~IPW_FLAG_BUSY;
-	wakeup_one(&sc->cmd);
+	wakeup(sc);
 }
 
 static void
-ipw_rx_newstate_intr(struct ipw_softc *sc, struct ipw_soft_buf *sbuf)
+ipw_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;
@@ -903,26 +987,20 @@
 	state = le32toh(*mtod(sbuf->m, uint32_t *));
 
 	switch (state) {
-	case IPW_STATE_INITIALIZED:
-	case IPW_STATE_DISABLED:
-		break;
-
 	case IPW_STATE_ASSOCIATED:
 		DPRINTFN(2, ("Association succeeded (%s flags 0x%x)\n",
 			IEEESTATE(ic), sc->flags));
+		if (sc->flags & IPW_FLAG_SCANNING) {
+			ieee80211_cancel_scan(ic);
+			sc->flags &= ~IPW_FLAG_SCANNING;
+		}
 		sc->flags |= IPW_FLAG_ASSOCIATED;
-		IPW_STATE_END(sc, IPW_FW_ASSOCIATING);
-		/* 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);
+		ieee80211_new_state(ic, IEEE80211_S_RUN, -1);
 		break;
 
 	case IPW_STATE_SCANNING:
 		DPRINTFN(3, ("Scanning (%s flags 0x%x)\n",
-			IEEESTATE(ic), sc->flags));
+			    IEEESTATE(ic), sc->flags));
 		/*
 		 * NB: Check driver state for association on assoc
 		 * loss as the firmware will immediately start to
@@ -931,32 +1009,14 @@
 		 */
 		if (sc->flags & IPW_FLAG_ASSOCIATED)
 			ieee80211_beacon_miss(ic);
-
-		sc->sc_state_timer = 3;		/* fw is still alive */
 		break;
 
 	case IPW_STATE_SCAN_COMPLETE:
 		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;
-		}
-		*/
-
-		/* Only update the scan module if we were actaully scanning */
-		if (sc->fw_state == IPW_FW_SCANNING) {
+			    IEEESTATE(ic), sc->flags));
+		if (sc->flags & IPW_FLAG_SCANNING) {
+			ieee80211_scan_done(ic);
 			sc->flags &= ~IPW_FLAG_SCANNING;
-			IPW_STATE_END(sc, IPW_FW_SCANNING);
-			ieee80211_scan_done(ic);
 		}
 		break;
 
@@ -964,16 +1024,24 @@
 		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);
+		if (ic->ic_state == IEEE80211_S_RUN)
+			ieee80211_new_state(ic, IEEE80211_S_SCAN, -1);
+		break;
+
+	case IPW_STATE_DISABLED:
+		DPRINTFN(2, ("Firmware disabled (%s flags 0x%x)\n",
+			IEEESTATE(ic), sc->flags));
 		break;
 
-	case IPW_STATE_RADIO_OFF:
+	case IPW_STATE_RADIO_DISABLED:
 		DPRINTFN(2, ("Radio off (%s flags 0x%x)\n",
 			IEEESTATE(ic), sc->flags));
-		taskqueue_enqueue(sc->sc_tq, &sc->sc_radiofftask);
+		ic->ic_ifp->if_flags &= ~IFF_UP;
+		ipw_stop(sc);
 		break;
+
 	default:
-		DPRINTFN(2, ("%s: state %u %s flags 0x%x\n",
+		DPRINTFN(2, ("%s: unhandled state %u %s flags 0x%x\n",
 			__func__, state, IEEESTATE(ic), sc->flags));
 		break;
 	}
@@ -998,7 +1066,6 @@
 /*
  * 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 ipw_softc *sc, struct mbuf *m)
@@ -1025,11 +1092,12 @@
 
 	frm += 12;	/* skip tstamp, bintval and capinfo fields */
 	while (frm < efrm) {
-		if (*frm == IEEE80211_ELEMID_DSPARMS
+		if (*frm == IEEE80211_ELEMID_DSPARMS)
 #if IEEE80211_CHAN_MAX < 255
-		    && frm[2] <= IEEE80211_CHAN_MAX
+		if (frm[2] <= IEEE80211_CHAN_MAX)
 #endif
-		    ) {
+		{
+			DPRINTF(("Fixing channel to %d\n", frm[2]));
 			c = ieee80211_find_channel(ic,
 				ieee80211_ieee2mhz(frm[2], 0),
 				IEEE80211_CHAN_B);
@@ -1043,7 +1111,7 @@
 }
 
 static void
-ipw_rx_data_intr(struct ipw_softc *sc, struct ipw_status *status,
+ipw_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;
@@ -1052,25 +1120,15 @@
 	struct ieee80211_frame *wh;
 	struct ieee80211_node *ni;
 	bus_addr_t physaddr;
-	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;
-	}
+	int error;
 
 	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,
@@ -1116,21 +1174,22 @@
 	m->m_pkthdr.rcvif = ifp;
 	m->m_pkthdr.len = m->m_len = le32toh(status->len);
 
-	/* XXX */
-	if (sc->flags & IPW_FLAG_SCANNING)
-		ipw_fix_channel(sc, m);
-
-	if (sc->sc_drvbpf != NULL) {
+	if (bpf_peers_present(sc->sc_drvbpf)) {
 		struct ipw_rx_radiotap_header *tap = &sc->sc_rxtap;
 
 		tap->wr_flags = 0;
-		tap->wr_antsignal = status->rssi + IPW_RSSI_TO_DBM;
+		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);
 
 		bpf_mtap2(sc->sc_drvbpf, tap, sc->sc_rxtap_len, m);
 	}
 
+	if (ic->ic_state == IEEE80211_S_SCAN)
+		ipw_fix_channel(sc, m);
+
 	wh = mtod(m, struct ieee80211_frame *);
-	IPW_UNLOCK(sc);
+	mtx_unlock(&sc->sc_mtx);
 	ni = ieee80211_find_rxnode(ic, (struct ieee80211_frame_min *)wh);
 
 	/* send the frame to the 802.11 layer */
@@ -1138,7 +1197,7 @@
 
 	/* node is no longer needed */
 	ieee80211_free_node(ni);
-	IPW_LOCK(sc);
+	mtx_lock(&sc->sc_mtx);
 
 	bus_dmamap_sync(sc->rbd_dmat, sc->rbd_map, BUS_DMASYNC_PREWRITE);
 }
@@ -1166,16 +1225,16 @@
 
 		switch (le16toh(status->code) & 0xf) {
 		case IPW_STATUS_CODE_COMMAND:
-			ipw_rx_cmd_intr(sc, sbuf);
+			ipw_command_intr(sc, sbuf);
 			break;
 
 		case IPW_STATUS_CODE_NEWSTATE:
-			ipw_rx_newstate_intr(sc, sbuf);
+			ipw_newstate_intr(sc, sbuf);
 			break;
 
 		case IPW_STATUS_CODE_DATA_802_3:
 		case IPW_STATUS_CODE_DATA_802_11:
-			ipw_rx_data_intr(sc, status, sbd, sbuf);
+			ipw_data_intr(sc, status, sbd, sbuf);
 			break;
 
 		case IPW_STATUS_CODE_NOTIFICATION:
@@ -1191,8 +1250,12 @@
 		default:
 			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;
 	}
 
@@ -1230,8 +1293,12 @@
 		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;
 	}
 
@@ -1263,10 +1330,8 @@
 	/* 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_locked(ifp);
+	ipw_start(ifp);
 }
 
 static void
@@ -1276,39 +1341,36 @@
 	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;
+	}
+
+	/* disable interrupts */
+	CSR_WRITE_4(sc, IPW_CSR_INTR_MASK, 0);
 
-		DPRINTFN(9, ("%s: 0x%x\n", __func__, r));
+	/* acknowledge all interrupts */
+	CSR_WRITE_4(sc, IPW_CSR_INTR, r);
 
-		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_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_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);
 }
 
@@ -1335,7 +1397,6 @@
 		{ 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" },
@@ -1345,17 +1406,13 @@
 		{ 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_SECURITY_INFORMATION,	"SET_SECURITY_INFO" },
 		{ 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" },
@@ -1379,15 +1436,18 @@
  * Send a command to the firmware and wait for the acknowledgement.
  */
 static int
-ipw_cmd(struct ipw_softc *sc, uint32_t cmd, const void *data, uint32_t len)
+ipw_cmd(struct ipw_softc *sc, uint32_t type, void *data, uint32_t len)
 {
 	struct ipw_soft_bd *sbd;
 	bus_addr_t physaddr;
 	int error;
 
+	if (mtx_recursed(&sc->sc_mtx))
+		device_printf(sc->sc_dev, "OMG, msleeping on a recursed mtx\n");
+
 	if (sc->flags & IPW_FLAG_BUSY) {
 		device_printf(sc->sc_dev, "%s: %s not sent, busy\n",
-			__func__, ipw_cmdname(cmd));
+			__func__, ipw_cmdname(type));
 		return EAGAIN;
 	}
 	sc->flags |= IPW_FLAG_BUSY;
@@ -1397,13 +1457,12 @@
 	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,
-		    "%s: %s not sent, could not map memory (error %u)\n",
-		    __func__, ipw_cmdname(cmd), error);
+		device_printf(sc->sc_dev, "could not map command DMA memory\n");
+		sc->flags &= ~IPW_FLAG_BUSY;
 		return error;
 	}
 
-	sc->cmd.type = htole32(cmd);
+	sc->cmd.type = htole32(type);
 	sc->cmd.subtype = 0;
 	sc->cmd.len = htole32(len);
 	sc->cmd.seq = 0;
@@ -1420,7 +1479,7 @@
 	bus_dmamap_sync(sc->tbd_dmat, sc->tbd_map, BUS_DMASYNC_PREWRITE);
 
 	DPRINTFN(4, ("sending %s(%u, %u, %u, %u)\n",
-		ipw_cmdname(cmd), cmd, 0, 0, len));
+		ipw_cmdname(type), type, 0, 0, len));
 
 	/* kick firmware */
 	sc->txfree--;
@@ -1428,13 +1487,14 @@
 	CSR_WRITE_4(sc, IPW_CSR_TX_WRITE, sc->txcur);
 
 	/* wait at most one second for command to complete */
-	error = msleep(&sc->cmd, &sc->sc_mtx, 0, "ipwcmd", hz);
+	error = msleep(sc, &sc->sc_mtx, 0, "ipwcmd", hz);
 	if (error != 0) {
 		device_printf(sc->sc_dev, "%s: %s failed, timeout (error %u)\n",
-		    __func__, ipw_cmdname(cmd), error);
-		return error;
+		    __func__, ipw_cmdname(type), error);
+		sc->flags &= ~IPW_FLAG_BUSY;
+		return (error);
 	}
-	return 0;
+	return (0);
 }
 
 static int
@@ -1450,15 +1510,11 @@
 	struct mbuf *mnew;
 	bus_dma_segment_t segs[IPW_MAX_NSEG];
 	bus_addr_t physaddr;
-	int nsegs, error, i, iswep, hdrlen, ismcast;
+	int nsegs, error, i;
 
 	wh = mtod(m0, struct ieee80211_frame *);
-	/* NB: only data frames use this path */
-	iswep = wh->i_fc[1] & IEEE80211_FC1_WEP;
-	hdrlen = ieee80211_hdrsize(wh);
-	ismcast = IEEE80211_IS_MULTICAST(wh->i_addr1);
 
-	if (iswep) {
+	if (wh->i_fc[1] & IEEE80211_FC1_WEP) {
 		k = ieee80211_crypto_encap(ic, ni, m0);
 		if (k == NULL) {
 			m_freem(m0);
@@ -1469,10 +1525,12 @@
 		wh = mtod(m0, struct ieee80211_frame *);
 	}
 
-	if (sc->sc_drvbpf != NULL) {
+	if (bpf_peers_present(sc->sc_drvbpf)) {
 		struct ipw_tx_radiotap_header *tap = &sc->sc_txtap;
 
 		tap->wt_flags = 0;
+		tap->wt_chan_freq = htole16(ic->ic_curchan->ic_freq);

>>> TRUNCATED FOR MAIL (1000 lines) <<<



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?200709282146.l8SLk8e1057313>