summaryrefslogtreecommitdiff
path: root/sys/dev/iwi
diff options
context:
space:
mode:
authorLuigi Rizzo <luigi@FreeBSD.org>2007-02-20 15:45:59 +0000
committerLuigi Rizzo <luigi@FreeBSD.org>2007-02-20 15:45:59 +0000
commit484f6530e9ee16ccc7d090cf9bdb7c026d445b99 (patch)
treeb601c864043d3e3f8d1da7d5d17322466a2c5d69 /sys/dev/iwi
parentbd146f1302e08d5a68d08e86966fc81bcbf9df27 (diff)
Notes
Diffstat (limited to 'sys/dev/iwi')
-rw-r--r--sys/dev/iwi/if_iwi.c205
-rw-r--r--sys/dev/iwi/if_iwivar.h20
2 files changed, 147 insertions, 78 deletions
diff --git a/sys/dev/iwi/if_iwi.c b/sys/dev/iwi/if_iwi.c
index 1f7f573181b3..9ebc86e7cba8 100644
--- a/sys/dev/iwi/if_iwi.c
+++ b/sys/dev/iwi/if_iwi.c
@@ -157,6 +157,7 @@ static void iwi_stop_master(struct iwi_softc *);
static int iwi_reset(struct iwi_softc *);
static int iwi_load_ucode(struct iwi_softc *, const struct iwi_fw *);
static int iwi_load_firmware(struct iwi_softc *, const struct iwi_fw *);
+static void iwi_release_fw_dma(struct iwi_softc *sc);
static int iwi_config(struct iwi_softc *);
static int iwi_get_firmware(struct iwi_softc *);
static void iwi_put_firmware(struct iwi_softc *);
@@ -331,32 +332,15 @@ iwi_attach(device_t dev)
goto fail;
}
- error = iwi_alloc_tx_ring(sc, &sc->txq[0], IWI_TX_RING_COUNT,
- IWI_CSR_TX1_RIDX, IWI_CSR_TX1_WIDX);
- if (error != 0) {
- device_printf(dev, "could not allocate Tx ring 1\n");
- 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) {
- device_printf(dev, "could not allocate Tx ring 2\n");
- 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) {
- device_printf(dev, "could not allocate Tx ring 3\n");
- 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) {
- device_printf(dev, "could not allocate Tx ring 4\n");
- goto fail;
+ for (i = 0; i < 4; i++) {
+ error = iwi_alloc_tx_ring(sc, &sc->txq[i], IWI_TX_RING_COUNT,
+ IWI_CSR_TX1_RIDX + i * 4,
+ IWI_CSR_TX1_WIDX + i * 4);
+ if (error != 0) {
+ device_printf(dev, "could not allocate Tx ring %d\n",
+ i+i);
+ goto fail;
+ }
}
if (iwi_alloc_rx_ring(sc, &sc->rxq, IWI_RX_RING_COUNT) != 0) {
@@ -496,6 +480,7 @@ iwi_detach(device_t dev)
ieee80211_ifdetach(ic);
}
iwi_put_firmware(sc);
+ iwi_release_fw_dma(sc);
iwi_free_cmd_ring(sc, &sc->cmdq);
iwi_free_tx_ring(sc, &sc->txq[0]);
@@ -970,6 +955,7 @@ iwi_newstate(struct ieee80211com *ic, enum ieee80211_state nstate, int arg)
struct ifnet *ifp = ic->ic_ifp;
struct iwi_softc *sc = ifp->if_softc;
+ IWI_LOCK_ASSERT(sc);
DPRINTF(("%s: %s -> %s flags 0x%x\n", __func__,
ieee80211_state_name[ic->ic_state],
ieee80211_state_name[nstate], sc->flags));
@@ -1216,6 +1202,7 @@ iwi_setcurchan(struct iwi_softc *sc, int chan)
{
struct ieee80211com *ic = &sc->sc_ic;
+ IWI_LOCK_ASSERT(sc);
ic->ic_curchan = &ic->ic_channels[chan];
sc->curchan = chan;
@@ -1709,6 +1696,8 @@ iwi_cmd(struct iwi_softc *sc, uint8_t type, void *data, uint8_t len)
{
struct iwi_cmd_desc *desc;
+ IWI_LOCK_ASSERT(sc);
+
if (sc->flags & IWI_FLAG_BUSY) {
device_printf(sc->sc_dev, "%s: cmd %d not sent, busy\n",
__func__, type);
@@ -1771,6 +1760,7 @@ iwi_tx_start(struct ifnet *ifp, struct mbuf *m0, struct ieee80211_node *ni,
int error, nsegs, hdrlen, i;
int ismcast, flags, xflags, staid;
+ IWI_LOCK_ASSERT(sc);
wh = mtod(m0, const struct ieee80211_frame *);
/* NB: only data frames use this path */
hdrlen = ieee80211_hdrsize(wh);
@@ -2080,11 +2070,20 @@ iwi_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
*/
sc->sc_rfkill_timer = 0;
}
- iwi_put_firmware(sc);
}
break;
default:
+ /*
+ * XXX the driver has a tendency to freeze the machine
+ * when initializing the interface. This seems due to
+ * a race condition, whose origin is still unclear.
+ * Adding a printf in this particular condition seems to cure
+ * the symptom, so we do it until we find a proper fix.
+ */
+ if (sc->flags & IWI_FLAG_BUSY)
+ device_printf(sc->sc_dev, "%s: flags %x cmd 0x%lx\n",
+ __func__, sc->flags, cmd);
error = ieee80211_ioctl(ic, cmd, data);
}
@@ -2107,6 +2106,8 @@ iwi_stop_master(struct iwi_softc *sc)
uint32_t tmp;
int ntries;
+ IWI_LOCK_ASSERT(sc);
+
/* disable interrupts */
CSR_WRITE_4(sc, IWI_CSR_INTR_MASK, 0);
@@ -2223,6 +2224,7 @@ iwi_getfw(struct iwi_fw *fw, const char *fwname,
* This is necessary because we re-init the device sometimes
* from a context where we cannot read from the filesystem
* (e.g. from the taskqueue thread when rfkill is re-enabled).
+ * XXX return 0 on success, 1 on error.
*
* NB: the order of get'ing and put'ing images here is
* intentional to support handling firmware images bundled
@@ -2306,33 +2308,38 @@ iwi_get_firmware(struct iwi_softc *sc)
/*
* Check and setup combined image.
*/
- if (fp->datasize < sizeof(hdr)) {
+ if (fp->datasize < sizeof(struct iwi_firmware_hdr)) {
device_printf(sc->sc_dev, "image '%s' too small\n",
fp->name);
goto bad;
}
hdr = (const struct iwi_firmware_hdr *)fp->data;
- if (fp->datasize < sizeof(*hdr) + hdr->bsize + hdr->usize + hdr->fsize) {
+ if (fp->datasize < sizeof(*hdr) + le32toh(hdr->bsize) + le32toh(hdr->usize)
+ + le32toh(hdr->fsize)) {
device_printf(sc->sc_dev, "image '%s' too small (2)\n",
fp->name);
goto bad;
}
sc->fw_boot.data = ((const char *) fp->data) + sizeof(*hdr);
- sc->fw_boot.size = hdr->bsize;
+ sc->fw_boot.size = le32toh(hdr->bsize);
sc->fw_boot.name = fp->name;
sc->fw_uc.data = sc->fw_boot.data + sc->fw_boot.size;
- sc->fw_uc.size = hdr->usize;
+ sc->fw_uc.size = le32toh(hdr->usize);
sc->fw_uc.name = fp->name;
sc->fw_fw.data = sc->fw_uc.data + sc->fw_uc.size;
- sc->fw_fw.size = hdr->fsize;
+ sc->fw_fw.size = le32toh(hdr->fsize);
sc->fw_fw.name = fp->name;
}
+#if 0
+ device_printf(sc->sc_dev, "boot %d ucode %d fw %d bytes\n",
+ sc->fw_boot.size, sc->fw_uc.size, sc->fw_fw.size);
+#endif
sc->fw_mode = ic->ic_opmode;
- return 1;
+ return 0;
bad:
iwi_put_firmware(sc);
- return 0;
+ return 1;
}
static void
@@ -2367,6 +2374,7 @@ iwi_load_ucode(struct iwi_softc *sc, const struct iwi_fw *fw)
size_t size = fw->size;
int i, ntries, error;
+ IWI_LOCK_ASSERT(sc);
error = 0;
CSR_WRITE_4(sc, IWI_CSR_RST, CSR_READ_4(sc, IWI_CSR_RST) |
IWI_RST_STOP_MASTER);
@@ -2439,6 +2447,7 @@ iwi_load_firmware(struct iwi_softc *sc, const struct iwi_fw *fw)
uint32_t sentinel, ctl, src, dst, sum, len, mlen, tmp;
int ntries, error;
+ IWI_LOCK_ASSERT(sc);
/* copy firmware image to DMA memory */
memcpy(sc->fw_virtaddr, fw->data, fw->size);
@@ -2498,12 +2507,13 @@ iwi_load_firmware(struct iwi_softc *sc, const struct iwi_fw *fw)
break;
DELAY(100);
}
+ /* sync dma, just in case */
+ bus_dmamap_sync(sc->fw_dmat, sc->fw_map, BUS_DMASYNC_POSTWRITE);
if (ntries == 400) {
device_printf(sc->sc_dev,
"timeout processing command blocks for %s firmware\n",
fw->name);
- error = EIO;
- goto fail5;
+ return EIO;
}
/* we're done with command blocks processing */
@@ -2524,7 +2534,6 @@ iwi_load_firmware(struct iwi_softc *sc, const struct iwi_fw *fw)
"initialization to complete\n", fw->name);
}
-fail5:
return error;
}
@@ -2580,6 +2589,7 @@ iwi_config(struct iwi_softc *sc)
struct iwi_txpower power;
uint32_t data;
int error, i;
+ IWI_LOCK_ASSERT(sc);
IEEE80211_ADDR_COPY(ic->ic_myaddr, IF_LLADDR(ifp));
DPRINTF(("Setting MAC address to %6D\n", ic->ic_myaddr, ":"));
@@ -2707,6 +2717,8 @@ iwi_scan(struct iwi_softc *sc)
struct iwi_scan_ext scan;
int i, ix, start, scan_type, error;
+ IWI_LOCK_ASSERT(sc);
+
memset(&scan, 0, sizeof scan);
/* XXX different dwell times for different scan types */
@@ -2908,7 +2920,8 @@ iwi_auth_and_assoc(struct iwi_softc *sc)
struct iwi_rateset rs;
uint16_t capinfo;
int error;
-
+
+ IWI_LOCK_ASSERT(sc);
if (IEEE80211_IS_CHAN_2GHZ(ni->ni_chan)) {
memset(&config, 0, sizeof config);
config.bluetooth_coexistence = sc->bluetooth;
@@ -3077,6 +3090,66 @@ iwi_init(void *priv)
IWI_UNLOCK(sc);
}
+/*
+ * release dma resources for the firmware
+ */
+static void
+iwi_release_fw_dma(struct iwi_softc *sc)
+{
+ if (sc->fw_flags & IWI_FW_HAVE_PHY)
+ bus_dmamap_unload(sc->fw_dmat, sc->fw_map);
+ if (sc->fw_flags & IWI_FW_HAVE_MAP)
+ bus_dmamem_free(sc->fw_dmat, sc->fw_virtaddr, sc->fw_map);
+ if (sc->fw_flags & IWI_FW_HAVE_DMAT)
+ bus_dma_tag_destroy(sc->fw_dmat);
+
+ sc->fw_flags = 0;
+ sc->fw_dma_size = 0;
+ sc->fw_dmat = NULL;
+ sc->fw_map = NULL;
+ sc->fw_physaddr = 0;
+ sc->fw_virtaddr = NULL;
+}
+
+/*
+ * allocate the dma descriptor for the firmware.
+ * Return 0 on success, 1 on error.
+ * Must be called unlocked, protected by IWI_FLAG_FW_LOADING.
+ */
+static int
+iwi_init_fw_dma(struct iwi_softc *sc, int size)
+{
+ if (sc->fw_dma_size > size)
+ return 0;
+ if (bus_dma_tag_create(bus_get_dma_tag(sc->sc_dev), 4, 0,
+ BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL,
+ size, 1, size, 0, NULL, NULL, &sc->fw_dmat) != 0) {
+ device_printf(sc->sc_dev,
+ "could not create firmware DMA tag\n");
+ goto error;
+ }
+ sc->fw_flags |= IWI_FW_HAVE_DMAT;
+ if (bus_dmamem_alloc(sc->fw_dmat, &sc->fw_virtaddr, 0,
+ &sc->fw_map) != 0) {
+ device_printf(sc->sc_dev,
+ "could not allocate firmware DMA memory\n");
+ goto error;
+ }
+ sc->fw_flags |= IWI_FW_HAVE_MAP;
+ if (bus_dmamap_load(sc->fw_dmat, sc->fw_map, sc->fw_virtaddr,
+ size, iwi_dma_map_addr, &sc->fw_physaddr, 0) != 0) {
+ device_printf(sc->sc_dev, "could not load firmware DMA map\n");
+ goto error;
+ }
+ sc->fw_flags |= IWI_FW_HAVE_PHY;
+ sc->fw_dma_size = size;
+ return 0;
+
+error:
+ iwi_release_fw_dma(sc);
+ return 1;
+}
+
static void
iwi_init_locked(void *priv, int force)
{
@@ -3087,8 +3160,11 @@ iwi_init_locked(void *priv, int force)
int i;
IWI_LOCK_DECL;
- if (sc->flags & IWI_FLAG_FW_LOADING)
+ IWI_LOCK_ASSERT(sc);
+ if (sc->flags & IWI_FLAG_FW_LOADING) {
+ device_printf(sc->sc_dev, "%s: already loading\n", __func__);
return; /* XXX: condvar? */
+ }
iwi_stop(sc);
@@ -3100,53 +3176,34 @@ iwi_init_locked(void *priv, int force)
sc->flags |= IWI_FLAG_FW_LOADING;
IWI_UNLOCK(sc);
- if (!iwi_get_firmware(sc)) {
+ if (iwi_get_firmware(sc)) {
IWI_LOCK(sc);
goto fail;
}
/* allocate DMA memory for mapping firmware image */
- if (sc->fw_boot.size > sc->fw_dma_size)
- sc->fw_dma_size = sc->fw_boot.size;
- if (sc->fw_fw.size > sc->fw_dma_size)
- sc->fw_dma_size = sc->fw_fw.size;
- if (sc->fw_uc.size > sc->fw_dma_size)
- sc->fw_dma_size = sc->fw_uc.size;
-
- if (bus_dma_tag_create(bus_get_dma_tag(sc->sc_dev), 4, 0,
- BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL,
- sc->fw_dma_size, 1, sc->fw_dma_size, 0, NULL, NULL,
- &sc->fw_dmat) != 0) {
- device_printf(sc->sc_dev,
- "could not create firmware DMA tag\n");
+ i = sc->fw_fw.size;
+ if (sc->fw_boot.size > i)
+ i = sc->fw_boot.size;
+ /* XXX do we dma the ucode as well ? */
+ if (sc->fw_uc.size > i)
+ i = sc->fw_uc.size;
+ if (iwi_init_fw_dma(sc, i)) {
IWI_LOCK(sc);
goto fail;
}
- if (bus_dmamem_alloc(sc->fw_dmat, &sc->fw_virtaddr, 0,
- &sc->fw_map) != 0) {
- device_printf(sc->sc_dev,
- "could not allocate firmware DMA memory\n");
- IWI_LOCK(sc);
- goto fail2;
- }
- if (bus_dmamap_load(sc->fw_dmat, sc->fw_map, sc->fw_virtaddr,
- sc->fw_dma_size, iwi_dma_map_addr, &sc->fw_physaddr, 0) != 0) {
- device_printf(sc->sc_dev, "could not load firmware DMA map\n");
- IWI_LOCK(sc);
- goto fail3;
- }
IWI_LOCK(sc);
if (iwi_load_firmware(sc, &sc->fw_boot) != 0) {
device_printf(sc->sc_dev,
"could not load boot firmware %s\n", sc->fw_boot.name);
- goto fail4;
+ goto fail;
}
if (iwi_load_ucode(sc, &sc->fw_uc) != 0) {
device_printf(sc->sc_dev,
"could not load microcode %s\n", sc->fw_uc.name);
- goto fail4;
+ goto fail;
}
iwi_stop_master(sc);
@@ -3181,15 +3238,10 @@ iwi_init_locked(void *priv, int force)
if (iwi_load_firmware(sc, &sc->fw_fw) != 0) {
device_printf(sc->sc_dev,
"could not load main firmware %s\n", sc->fw_fw.name);
- goto fail4;
+ goto fail;
}
sc->flags |= IWI_FLAG_FW_INITED;
- bus_dmamap_sync(sc->fw_dmat, sc->fw_map, BUS_DMASYNC_POSTWRITE);
- bus_dmamap_unload(sc->fw_dmat, sc->fw_map);
- bus_dmamem_free(sc->fw_dmat, sc->fw_virtaddr, sc->fw_map);
- bus_dma_tag_destroy(sc->fw_dmat);
-
if (iwi_config(sc) != 0) {
device_printf(sc->sc_dev, "device configuration failed\n");
goto fail;
@@ -3213,10 +3265,6 @@ iwi_init_locked(void *priv, int force)
sc->flags &= ~IWI_FLAG_FW_LOADING;
return;
-fail4: bus_dmamap_sync(sc->fw_dmat, sc->fw_map, BUS_DMASYNC_POSTWRITE);
- bus_dmamap_unload(sc->fw_dmat, sc->fw_map);
-fail3: bus_dmamem_free(sc->fw_dmat, sc->fw_virtaddr, sc->fw_map);
-fail2: bus_dma_tag_destroy(sc->fw_dmat);
fail: ifp->if_flags &= ~IFF_UP;
sc->flags &= ~IWI_FLAG_FW_LOADING;
iwi_stop(sc);
@@ -3230,6 +3278,7 @@ iwi_stop(void *priv)
struct ieee80211com *ic = &sc->sc_ic;
struct ifnet *ifp = ic->ic_ifp;
+ IWI_LOCK_ASSERT(sc); /* XXX: pretty sure this triggers */
if (sc->sc_softled) {
callout_stop(&sc->sc_ledtimer);
sc->sc_blinking = 0;
diff --git a/sys/dev/iwi/if_iwivar.h b/sys/dev/iwi/if_iwivar.h
index 6b5571e17df3..ce292a9f3855 100644
--- a/sys/dev/iwi/if_iwivar.h
+++ b/sys/dev/iwi/if_iwivar.h
@@ -149,7 +149,22 @@ struct iwi_softc {
int mem_rid;
int irq_rid;
+ /*
+ * The card needs external firmware images to work, which is made of a
+ * bootloader, microcode and firmware proper. In version 3.00 and
+ * above, all pieces are contained in a single image, preceded by a
+ * struct iwi_firmware_hdr indicating the size of the 3 pieces.
+ * Old firmware < 3.0 has separate boot and ucode, so we need to
+ * load all of them explicitly.
+ * To avoid issues related to fragmentation, we keep the block of
+ * dma-ble memory around until detach time, and reallocate it when
+ * it becomes too small. fw_dma_size is the size currently allocated.
+ */
int fw_dma_size;
+ uint32_t fw_flags; /* allocation status */
+#define IWI_FW_HAVE_DMAT 0x01
+#define IWI_FW_HAVE_MAP 0x02
+#define IWI_FW_HAVE_PHY 0x04
bus_dma_tag_t fw_dmat;
bus_dmamap_t fw_map;
bus_addr_t fw_physaddr;
@@ -216,6 +231,11 @@ struct iwi_softc {
* and must be kept in sync.
*/
#define IWI_LOCK_DECL int __waslocked = 0
+//#define IWI_LOCK_ASSERT(sc) mtx_assert(&(sc)->sc_mtx, MA_OWNED)
+#define IWI_LOCK_ASSERT(sc) do { \
+ if (!mtx_owned(&(sc)->sc_mtx)) \
+ printf("%s iwi_lock not held\n", __func__); \
+} while (0)
#define IWI_LOCK(sc) do { \
if (!(__waslocked = mtx_owned(&(sc)->sc_mtx))) \
mtx_lock(&(sc)->sc_mtx); \