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 <damien.bergam...@free.fr> @@ -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)