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