Module Name: src
Committed By: christos
Date: Fri Jun 18 21:10:23 UTC 2010
Modified Files:
src/sys/dev/pci: if_iwn.c if_iwnreg.h if_iwnvar.h
Log Message:
Patch from Sverre Froyen to avoid panic when an ioctl happens while the
driver is initializing.
To generate a diff of this commit:
cvs rdiff -u -r1.45 -r1.46 src/sys/dev/pci/if_iwn.c
cvs rdiff -u -r1.7 -r1.8 src/sys/dev/pci/if_iwnreg.h
cvs rdiff -u -r1.10 -r1.11 src/sys/dev/pci/if_iwnvar.h
Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.
Modified files:
Index: src/sys/dev/pci/if_iwn.c
diff -u src/sys/dev/pci/if_iwn.c:1.45 src/sys/dev/pci/if_iwn.c:1.46
--- src/sys/dev/pci/if_iwn.c:1.45 Wed May 12 08:26:16 2010
+++ src/sys/dev/pci/if_iwn.c Fri Jun 18 17:10:23 2010
@@ -1,5 +1,5 @@
-/* $NetBSD: if_iwn.c,v 1.45 2010/05/12 12:26:16 christos Exp $ */
-/* $OpenBSD: if_iwn.c,v 1.88 2010/04/10 08:37:36 damien Exp $ */
+/* $NetBSD: if_iwn.c,v 1.46 2010/06/18 21:10:23 christos Exp $ */
+/* $OpenBSD: if_iwn.c,v 1.96 2010/05/13 09:25:03 damien Exp $ */
/*-
* Copyright (c) 2007-2010 Damien Bergamini <[email protected]>
@@ -22,7 +22,7 @@
* adapters.
*/
#include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: if_iwn.c,v 1.45 2010/05/12 12:26:16 christos Exp $");
+__KERNEL_RCSID(0, "$NetBSD: if_iwn.c,v 1.46 2010/06/18 21:10:23 christos Exp $");
#define IWN_USE_RBUF /* Use local storage for RX */
#undef IWN_HWCRYPTO /* XXX does not even compile yet */
@@ -32,7 +32,7 @@
#include <sys/param.h>
#include <sys/sockio.h>
-#include <sys/sysctl.h>
+#include <sys/proc.h>
#include <sys/mbuf.h>
#include <sys/kernel.h>
#include <sys/socket.h>
@@ -96,6 +96,19 @@
PCI_PRODUCT_INTEL_WIFI_LINK_6050_2X2_2,
PCI_PRODUCT_INTEL_WIFI_LINK_6005_2X2_1,
PCI_PRODUCT_INTEL_WIFI_LINK_6005_2X2_2,
+#ifdef notyet
+ /*
+ * XXX NetBSD: the 6005A replaces the two 6005, above
+ * (see OpenBSD rev 1.96).
+ */
+ PCI_PRODUCT_INTEL_WIFI_LINK_6005A_2X2_1,
+ PCI_PRODUCT_INTEL_WIFI_LINK_6005A_2X2_2,
+ PCI_PRODUCT_INTEL_WIFI_LINK_6005B_1X1_1,
+ PCI_PRODUCT_INTEL_WIFI_LINK_6005B_1X1_2,
+ PCI_PRODUCT_INTEL_WIFI_LINK_6005B_2X2_1,
+ PCI_PRODUCT_INTEL_WIFI_LINK_6005B_2X2_2,
+ PCI_PRODUCT_INTEL_WIFI_LINK_6005B_2X2_3
+#endif
};
/*
@@ -263,6 +276,10 @@
static int iwn4965_load_firmware(struct iwn_softc *);
static int iwn5000_load_firmware_section(struct iwn_softc *, uint32_t,
const uint8_t *, int);
+static int iwn_read_firmware_leg(struct iwn_softc *,
+ struct iwn_fw_info *);
+static int iwn_read_firmware_tlv(struct iwn_softc *,
+ struct iwn_fw_info *, uint16_t);
static int iwn5000_load_firmware(struct iwn_softc *);
static int iwn_read_firmware(struct iwn_softc *);
static int iwn_clock_wait(struct iwn_softc *);
@@ -3144,6 +3161,22 @@
int s, error = 0;
s = splnet();
+ /*
+ * Prevent processes from entering this function while another
+ * process is tsleep'ing in it.
+ */
+ if (sc->sc_flags & IWN_FLAG_BUSY) {
+ switch (cmd) {
+ case SIOCSIFADDR:
+ /* FALLTHROUGH */
+ case SIOCSIFFLAGS:
+ splx(s);
+ aprint_normal_dev(sc->sc_dev,
+ "ioctl while busy cmd = 0x%lx\n", cmd);
+ return EBUSY;
+ }
+ }
+ sc->sc_flags |= IWN_FLAG_BUSY;
switch (cmd) {
case SIOCSIFADDR:
@@ -3190,6 +3223,8 @@
error = iwn_init(ifp);
}
}
+
+ sc->sc_flags &= ~IWN_FLAG_BUSY;
splx(s);
return error;
}
@@ -4296,7 +4331,7 @@
tx->lifetime = htole32(IWN_LIFETIME_INFINITE);
if (flags & IEEE80211_CHAN_5GHZ) {
- hdr->crc_threshold = htole16(1);
+ hdr->crc_threshold = 0xffff;
/* Send probe requests at 6Mbps. */
tx->plcp = iwn_rates[IWN_RIDX_OFDM6].plcp;
rs = &ic->ic_sup_rates[IEEE80211_MODE_11A];
@@ -4334,6 +4369,10 @@
frm = (uint8_t *)(wh + 1);
frm = ieee80211_add_ssid(frm, NULL, 0);
frm = ieee80211_add_rates(frm, rs);
+#ifndef IEEE80211_NO_HT
+ if (ic->ic_flags & IEEE80211_F_HTON)
+ frm = ieee80211_add_htcaps(frm, ic);
+#endif
if (rs->rs_nrates > IEEE80211_RATE_SIZE)
frm = ieee80211_add_xrates(frm, rs);
@@ -5242,20 +5281,155 @@
return 0;
}
+/*
+ * Extract text and data sections from a legacy firmware image.
+ */
+static int
+iwn_read_firmware_leg(struct iwn_softc *sc, struct iwn_fw_info *fw)
+{
+ const uint32_t *ptr;
+ size_t hdrlen = 24;
+ uint32_t rev;
+
+ ptr = (const uint32_t *)fw->data;
+ rev = le32toh(*ptr++);
+
+ /* Check firmware API version. */
+ if (IWN_FW_API(rev) <= 1) {
+ aprint_error_dev(sc->sc_dev,
+ "bad firmware, need API version >=2\n");
+ return EINVAL;
+ }
+ if (IWN_FW_API(rev) >= 3) {
+ /* Skip build number (version 2 header). */
+ hdrlen += 4;
+ ptr++;
+ }
+ if (fw->size < hdrlen) {
+ aprint_error_dev(sc->sc_dev,
+ "firmware too short: %zd bytes\n", fw->size);
+ return EINVAL;
+ }
+ fw->main.textsz = le32toh(*ptr++);
+ fw->main.datasz = le32toh(*ptr++);
+ fw->init.textsz = le32toh(*ptr++);
+ fw->init.datasz = le32toh(*ptr++);
+ fw->boot.textsz = le32toh(*ptr++);
+
+ /* Check that all firmware sections fit. */
+ if (fw->size < hdrlen + fw->main.textsz + fw->main.datasz +
+ fw->init.textsz + fw->init.datasz + fw->boot.textsz) {
+ aprint_error_dev(sc->sc_dev,
+ "firmware too short: %zd bytes\n", fw->size);
+ return EINVAL;
+ }
+
+ /* Get pointers to firmware sections. */
+ fw->main.text = (const uint8_t *)ptr;
+ fw->main.data = fw->main.text + fw->main.textsz;
+ fw->init.text = fw->main.data + fw->main.datasz;
+ fw->init.data = fw->init.text + fw->init.textsz;
+ fw->boot.text = fw->init.data + fw->init.datasz;
+ return 0;
+}
+
+/*
+ * Extract text and data sections from a TLV firmware image.
+ */
+static int
+iwn_read_firmware_tlv(struct iwn_softc *sc, struct iwn_fw_info *fw,
+ uint16_t alt)
+{
+ const struct iwn_fw_tlv_hdr *hdr;
+ const struct iwn_fw_tlv *tlv;
+ const uint8_t *ptr, *end;
+ uint64_t altmask;
+ uint32_t len;
+
+ if (fw->size < sizeof (*hdr)) {
+ aprint_error_dev(sc->sc_dev,
+ "firmware too short: %zd bytes\n", fw->size);
+ return EINVAL;
+ }
+ hdr = (const struct iwn_fw_tlv_hdr *)fw->data;
+ if (hdr->signature != htole32(IWN_FW_SIGNATURE)) {
+ aprint_error_dev(sc->sc_dev,
+ "bad firmware signature 0x%08x\n", le32toh(hdr->signature));
+ return EINVAL;
+ }
+ DPRINTF(("FW: \"%.64s\", build 0x%x\n", hdr->descr,
+ le32toh(hdr->build)));
+
+ /*
+ * Select the closest supported alternative that is less than
+ * or equal to the specified one.
+ */
+ altmask = le64toh(hdr->altmask);
+ while (alt > 0 && !(altmask & (1ULL << alt)))
+ alt--; /* Downgrade. */
+ DPRINTF(("using alternative %d\n", alt));
+
+ ptr = (const uint8_t *)(hdr + 1);
+ end = (const uint8_t *)(fw->data + fw->size);
+
+ /* Parse type-length-value fields. */
+ while (ptr + sizeof (*tlv) <= end) {
+ tlv = (const struct iwn_fw_tlv *)ptr;
+ len = le32toh(tlv->len);
+
+ ptr += sizeof (*tlv);
+ if (ptr + len > end) {
+ aprint_error_dev(sc->sc_dev,
+ "firmware too short: %zd bytes\n", fw->size);
+ return EINVAL;
+ }
+ /* Skip other alternatives. */
+ if (tlv->alt != 0 && tlv->alt != htole16(alt))
+ goto next;
+
+ switch (le16toh(tlv->type)) {
+ case IWN_FW_TLV_MAIN_TEXT:
+ fw->main.text = ptr;
+ fw->main.textsz = len;
+ break;
+ case IWN_FW_TLV_MAIN_DATA:
+ fw->main.data = ptr;
+ fw->main.datasz = len;
+ break;
+ case IWN_FW_TLV_INIT_TEXT:
+ fw->init.text = ptr;
+ fw->init.textsz = len;
+ break;
+ case IWN_FW_TLV_INIT_DATA:
+ fw->init.data = ptr;
+ fw->init.datasz = len;
+ break;
+ case IWN_FW_TLV_BOOT_TEXT:
+ fw->boot.text = ptr;
+ fw->boot.textsz = len;
+ break;
+ default:
+ DPRINTF(("TLV type %d not handled\n",
+ le16toh(tlv->type)));
+ break;
+ }
+ next: /* TLV fields are 32-bit aligned. */
+ ptr += (len + 3) & ~3;
+ }
+ return 0;
+}
+
static int
iwn_read_firmware(struct iwn_softc *sc)
{
const struct iwn_hal *hal = sc->sc_hal;
struct iwn_fw_info *fw = &sc->fw;
firmware_handle_t fwh;
- const uint32_t *ptr;
- uint32_t rev;
- size_t size;
int error;
/* Initialize for error returns */
fw->data = NULL;
- fw->datasz = 0;
+ fw->size = 0;
/* Open firmware image. */
if ((error = firmware_open("if_iwn", sc->fwname, &fwh)) != 0) {
@@ -5263,53 +5437,42 @@
"could not get firmware handle %s\n", sc->fwname);
return error;
}
- size = firmware_get_size(fwh);
- if (size < 28) {
+ fw->size = firmware_get_size(fwh);
+ if (fw->size < sizeof (uint32_t)) {
aprint_error_dev(sc->sc_dev,
- "truncated firmware header: %zu bytes\n", size);
+ "firmware too short: %zd bytes\n", fw->size);
firmware_close(fwh);
return EINVAL;
}
/* Read the firmware. */
- fw->data = firmware_malloc(size);
+ fw->data = firmware_malloc(fw->size);
if (fw->data == NULL) {
aprint_error_dev(sc->sc_dev,
"not enough memory to stock firmware %s\n", sc->fwname);
firmware_close(fwh);
return ENOMEM;
}
- error = firmware_read(fwh, 0, fw->data, size);
+ error = firmware_read(fwh, 0, fw->data, fw->size);
firmware_close(fwh);
- fw->datasz = size;
if (error != 0) {
aprint_error_dev(sc->sc_dev,
"could not read firmware %s\n", sc->fwname);
goto out;
}
- /* Process firmware header. */
- ptr = (const uint32_t *)fw->data;
- rev = le32toh(*ptr++);
- /* Check firmware API version. */
- if (IWN_FW_API(rev) <= 1) {
+ /* Retrieve text and data sections. */
+ if (*(const uint32_t *)fw->data != 0) /* Legacy image. */
+ error = iwn_read_firmware_leg(sc, fw);
+ else
+ error = iwn_read_firmware_tlv(sc, fw, 1);
+ if (error != 0) {
aprint_error_dev(sc->sc_dev,
- "bad firmware, need API version >=2\n");
+ "could not read firmware sections\n");
goto out;
}
- if (IWN_FW_API(rev) >= 3) {
- /* Skip build number (version 2 header). */
- size -= 4;
- ptr++;
- }
- fw->main.textsz = le32toh(*ptr++);
- fw->main.datasz = le32toh(*ptr++);
- fw->init.textsz = le32toh(*ptr++);
- fw->init.datasz = le32toh(*ptr++);
- fw->boot.textsz = le32toh(*ptr++);
- size -= 24;
- /* Sanity-check firmware header. */
+ /* Make sure text and data sections fit in hardware memory. */
if (fw->main.textsz > hal->fw_text_maxsz ||
fw->main.datasz > hal->fw_data_maxsz ||
fw->init.textsz > hal->fw_text_maxsz ||
@@ -5317,30 +5480,16 @@
fw->boot.textsz > IWN_FW_BOOT_TEXT_MAXSZ ||
(fw->boot.textsz & 3) != 0) {
aprint_error_dev(sc->sc_dev,
- "invalid firmware header\n");
- goto out;
- }
-
- /* Check that all firmware sections fit. */
- if (fw->main.textsz + fw->main.datasz + fw->init.textsz +
- fw->init.datasz + fw->boot.textsz > size) {
- aprint_error_dev(sc->sc_dev,
- "firmware file too short: %zu bytes\n", size);
+ "firmware sections too large\n");
goto out;
}
- /* Get pointers to firmware sections. */
- fw->main.text = (const uint8_t *)ptr;
- fw->main.data = fw->main.text + fw->main.textsz;
- fw->init.text = fw->main.data + fw->main.datasz;
- fw->init.data = fw->init.text + fw->init.textsz;
- fw->boot.text = fw->init.data + fw->init.datasz;
-
+ /* We can proceed with loading the firmware. */
return 0;
out:
- firmware_free(fw->data, fw->datasz);
+ firmware_free(fw->data, fw->size);
fw->data = NULL;
- fw->datasz = 0;
+ fw->size = 0;
return error ? error : EINVAL;
}
@@ -5733,11 +5882,11 @@
sc->sc_flags &= ~IWN_FLAG_USE_ICT;
/* Initialize hardware and upload firmware. */
- KASSERT(sc->fw.data != NULL && sc->fw.datasz > 0);
+ KASSERT(sc->fw.data != NULL && sc->fw.size > 0);
error = iwn_hw_init(sc);
- firmware_free(sc->fw.data, sc->fw.datasz);
+ firmware_free(sc->fw.data, sc->fw.size);
sc->fw.data = NULL;
- sc->fw.datasz = 0;
+ sc->fw.size = 0;
if (error != 0) {
aprint_error_dev(sc->sc_dev,
"could not initialize hardware\n");
Index: src/sys/dev/pci/if_iwnreg.h
diff -u src/sys/dev/pci/if_iwnreg.h:1.7 src/sys/dev/pci/if_iwnreg.h:1.8
--- src/sys/dev/pci/if_iwnreg.h:1.7 Thu Apr 15 21:40:41 2010
+++ src/sys/dev/pci/if_iwnreg.h Fri Jun 18 17:10:23 2010
@@ -1,5 +1,5 @@
-/* $NetBSD: if_iwnreg.h,v 1.7 2010/04/16 01:40:41 christos Exp $ */
-/* $OpenBSD: if_iwnreg.h,v 1.38 2010/04/10 08:37:36 damien Exp $ */
+/* $NetBSD: if_iwnreg.h,v 1.8 2010/06/18 21:10:23 christos Exp $ */
+/* $OpenBSD: if_iwnreg.h,v 1.40 2010/05/05 19:41:57 damien Exp $ */
/*-
* Copyright (c) 2007, 2008
@@ -1253,6 +1253,34 @@
uint32_t time[2];
} __packed;
+/* TLV firmware header. */
+struct iwn_fw_tlv_hdr {
+ uint32_t zero; /* Always 0, to differentiate from legacy. */
+ uint32_t signature;
+#define IWN_FW_SIGNATURE 0x0a4c5749 /* "IWL\n" */
+
+ uint8_t descr[64];
+ uint32_t rev;
+#define IWN_FW_API(x) (((x) >> 8) & 0xff)
+
+ uint32_t build;
+ uint64_t altmask;
+} __packed;
+
+/* TLV header. */
+struct iwn_fw_tlv {
+ uint16_t type;
+#define IWN_FW_TLV_MAIN_TEXT 1
+#define IWN_FW_TLV_MAIN_DATA 2
+#define IWN_FW_TLV_INIT_TEXT 3
+#define IWN_FW_TLV_INIT_DATA 4
+#define IWN_FW_TLV_BOOT_TEXT 5
+#define IWN_FW_TLV_PBREQ_MAXLEN 6
+
+ uint16_t alt;
+ uint32_t len;
+} __packed;
+
#define IWN4965_FW_TEXT_MAXSZ ( 96 * 1024)
#define IWN4965_FW_DATA_MAXSZ ( 40 * 1024)
#define IWN5000_FW_TEXT_MAXSZ (256 * 1024)
@@ -1261,8 +1289,6 @@
#define IWN4965_FWSZ (IWN4965_FW_TEXT_MAXSZ + IWN4965_FW_DATA_MAXSZ)
#define IWN5000_FWSZ IWN5000_FW_TEXT_MAXSZ
-#define IWN_FW_API(x) (((x) >> 8) & 0xff)
-
/*
* Offsets into EEPROM.
*/
Index: src/sys/dev/pci/if_iwnvar.h
diff -u src/sys/dev/pci/if_iwnvar.h:1.10 src/sys/dev/pci/if_iwnvar.h:1.11
--- src/sys/dev/pci/if_iwnvar.h:1.10 Fri Apr 23 16:56:20 2010
+++ src/sys/dev/pci/if_iwnvar.h Fri Jun 18 17:10:23 2010
@@ -1,5 +1,5 @@
-/* $NetBSD: if_iwnvar.h,v 1.10 2010/04/23 20:56:20 christos Exp $ */
-/* $OpenBSD: if_iwnvar.h,v 1.17 2010/02/17 18:23:00 damien Exp $ */
+/* $NetBSD: if_iwnvar.h,v 1.11 2010/06/18 21:10:23 christos Exp $ */
+/* $OpenBSD: if_iwnvar.h,v 1.19 2010/05/05 19:47:43 damien Exp $ */
/*-
* Copyright (c) 2007, 2008
@@ -168,7 +168,7 @@
struct iwn_fw_info {
u_char *data;
- uint32_t datasz;
+ size_t size;
struct iwn_fw_part init;
struct iwn_fw_part main;
struct iwn_fw_part boot;
@@ -226,6 +226,7 @@
#define IWN_FLAG_CALIB_DONE (1 << 2)
#define IWN_FLAG_USE_ICT (1 << 3)
#define IWN_FLAG_INTERNAL_PA (1 << 4)
+#define IWN_FLAG_BUSY (1 << 5)
/* Added for NetBSD */
#define IWN_FLAG_SCANNING (1 << 8)