summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--sys/dev/pci/if_iwn.c61
-rw-r--r--sys/dev/pci/if_iwnreg.h11
2 files changed, 49 insertions, 23 deletions
diff --git a/sys/dev/pci/if_iwn.c b/sys/dev/pci/if_iwn.c
index 5a23ee0dfaf..d539fdae379 100644
--- a/sys/dev/pci/if_iwn.c
+++ b/sys/dev/pci/if_iwn.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: if_iwn.c,v 1.92 2010/04/30 16:31:47 damien Exp $ */
+/* $OpenBSD: if_iwn.c,v 1.93 2010/05/05 19:41:57 damien Exp $ */
/*-
* Copyright (c) 2007-2010 Damien Bergamini <damien.bergamini@free.fr>
@@ -240,7 +240,7 @@ int iwn5000_load_firmware(struct iwn_softc *);
int iwn_read_firmware_leg(struct iwn_softc *,
struct iwn_fw_info *);
int iwn_read_firmware_tlv(struct iwn_softc *,
- struct iwn_fw_info *);
+ struct iwn_fw_info *, uint16_t);
int iwn_read_firmware(struct iwn_softc *);
int iwn_clock_wait(struct iwn_softc *);
int iwn_apm_init(struct iwn_softc *);
@@ -5219,11 +5219,14 @@ iwn_read_firmware_leg(struct iwn_softc *sc, struct iwn_fw_info *fw)
* Extract text and data sections from a TLV firmware image.
*/
int
-iwn_read_firmware_tlv(struct iwn_softc *sc, struct iwn_fw_info *fw)
+iwn_read_firmware_tlv(struct iwn_softc *sc, struct iwn_fw_info *fw,
+ uint16_t alt)
{
const struct iwn_fw_tlv_hdr *hdr;
- const uint32_t *ptr, *end;
- uint32_t type, len;
+ const struct iwn_fw_tlv *tlv;
+ const uint8_t *ptr, *end;
+ uint64_t altmask;
+ uint32_t len;
if (fw->size < sizeof (*hdr)) {
printf("%s: firmware too short: %d bytes\n",
@@ -5239,45 +5242,61 @@ iwn_read_firmware_tlv(struct iwn_softc *sc, struct iwn_fw_info *fw)
DPRINTF(("FW: \"%.64s\", build 0x%x\n", hdr->descr,
letoh32(hdr->build)));
- ptr = (const uint32_t *)(hdr + 1);
- end = (const uint32_t *)(fw->data + fw->size);
+ /*
+ * Select the closest supported alternative that is less than
+ * or equal to the specified one.
+ */
+ altmask = letoh64(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 + 2 <= end) {
- type = letoh32(*ptr++);
- len = letoh32(*ptr++);
- if (ptr + (len + 3) / 4 > end) {
+ while (ptr + sizeof (*tlv) <= end) {
+ tlv = (const struct iwn_fw_tlv *)ptr;
+ len = letoh32(tlv->len);
+
+ ptr += sizeof (*tlv);
+ if (ptr + len > end) {
printf("%s: firmware too short: %d bytes\n",
sc->sc_dev.dv_xname, fw->size);
return EINVAL;
}
- switch (type) {
+ /* Skip other alternatives. */
+ if (tlv->alt != 0 && tlv->alt != htole16(alt))
+ goto next;
+
+ switch (letoh16(tlv->type)) {
case IWN_FW_TLV_MAIN_TEXT:
- fw->main.text = (const uint8_t *)ptr;
+ fw->main.text = ptr;
fw->main.textsz = len;
break;
case IWN_FW_TLV_MAIN_DATA:
- fw->main.data = (const uint8_t *)ptr;
+ fw->main.data = ptr;
fw->main.datasz = len;
break;
case IWN_FW_TLV_INIT_TEXT:
- fw->init.text = (const uint8_t *)ptr;
+ fw->init.text = ptr;
fw->init.textsz = len;
break;
case IWN_FW_TLV_INIT_DATA:
- fw->init.data = (const uint8_t *)ptr;
+ fw->init.data = ptr;
fw->init.datasz = len;
break;
case IWN_FW_TLV_BOOT_TEXT:
- fw->boot.text = (const uint8_t *)ptr;
+ fw->boot.text = ptr;
fw->boot.textsz = len;
break;
default:
- DPRINTF(("TLV type %d not handled\n", type));
+ DPRINTF(("TLV type %d not handled\n",
+ letoh16(tlv->type)));
break;
}
- /* TLV fields are 32-bit aligned. */
- ptr += (len + 3) / 4;
+ next: /* TLV fields are 32-bit aligned. */
+ ptr += (len + 3) & ~3;
}
return 0;
}
@@ -5308,7 +5327,7 @@ iwn_read_firmware(struct iwn_softc *sc)
if (*(const uint32_t *)fw->data != 0) /* Legacy image. */
error = iwn_read_firmware_leg(sc, fw);
else
- error = iwn_read_firmware_tlv(sc, fw);
+ error = iwn_read_firmware_tlv(sc, fw, 1);
if (error != 0) {
printf("%s: could not read firmware sections\n",
sc->sc_dev.dv_xname);
diff --git a/sys/dev/pci/if_iwnreg.h b/sys/dev/pci/if_iwnreg.h
index a2b01109d0d..ca9e1aaf131 100644
--- a/sys/dev/pci/if_iwnreg.h
+++ b/sys/dev/pci/if_iwnreg.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: if_iwnreg.h,v 1.39 2010/04/30 16:06:46 damien Exp $ */
+/* $OpenBSD: if_iwnreg.h,v 1.40 2010/05/05 19:41:57 damien Exp $ */
/*-
* Copyright (c) 2007, 2008
@@ -1259,9 +1259,12 @@ struct iwn_fw_tlv_hdr {
#define IWN_FW_API(x) (((x) >> 8) & 0xff)
uint32_t build;
+ uint64_t altmask;
} __packed;
-/* Firmware TLV fields types. */
+/* 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
@@ -1269,6 +1272,10 @@ struct iwn_fw_tlv_hdr {
#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)