summaryrefslogtreecommitdiffstats
path: root/sys/dev/iwi
diff options
context:
space:
mode:
authordamien <damien@FreeBSD.org>2005-11-19 16:54:55 +0000
committerdamien <damien@FreeBSD.org>2005-11-19 16:54:55 +0000
commita9203048910de63fd07e54a38ed908abfcff9b07 (patch)
tree2fb218547b7e52333250a742f32ea070f9506b19 /sys/dev/iwi
parent736e6b710d329cffb76d1bc5d4830af5150cd560 (diff)
downloadFreeBSD-src-a9203048910de63fd07e54a38ed908abfcff9b07.zip
FreeBSD-src-a9203048910de63fd07e54a38ed908abfcff9b07.tar.gz
Load firmware images directly from the filesystem (looks into /etc/firmware
directory by default) without requiring the user to load them by hand using e.g iwicontrol. Get rid of the old ioctl crud. Updated iwi-firmware port coming soon. Obtained from: OpenBSD
Diffstat (limited to 'sys/dev/iwi')
-rw-r--r--sys/dev/iwi/if_iwi.c293
-rw-r--r--sys/dev/iwi/if_iwireg.h6
-rw-r--r--sys/dev/iwi/if_iwivar.h19
3 files changed, 165 insertions, 153 deletions
diff --git a/sys/dev/iwi/if_iwi.c b/sys/dev/iwi/if_iwi.c
index 7dfb20a..c7b8660 100644
--- a/sys/dev/iwi/if_iwi.c
+++ b/sys/dev/iwi/if_iwi.c
@@ -46,6 +46,10 @@ __FBSDID("$FreeBSD$");
#include <sys/module.h>
#include <sys/bus.h>
#include <sys/endian.h>
+#include <sys/proc.h>
+#include <sys/mount.h>
+#include <sys/namei.h>
+#include <sys/vnode.h>
#include <machine/bus.h>
#include <machine/resource.h>
@@ -139,16 +143,15 @@ static void iwi_watchdog(struct ifnet *);
static int iwi_ioctl(struct ifnet *, u_long, caddr_t);
static void iwi_stop_master(struct iwi_softc *);
static int iwi_reset(struct iwi_softc *);
-static int iwi_load_ucode(struct iwi_softc *, void *, int);
-static int iwi_load_firmware(struct iwi_softc *, void *, int);
-static int iwi_cache_firmware(struct iwi_softc *, void *);
-static void iwi_free_firmware(struct iwi_softc *);
+static int iwi_load_ucode(struct iwi_softc *, const char *);
+static int iwi_load_firmware(struct iwi_softc *, const char *);
static int iwi_config(struct iwi_softc *);
static int iwi_set_chan(struct iwi_softc *, struct ieee80211_channel *);
static int iwi_scan(struct iwi_softc *);
static int iwi_auth_and_assoc(struct iwi_softc *);
static void iwi_init(void *);
static void iwi_stop(void *);
+static int iwi_read_firmware(const char *, caddr_t *, size_t *);
static int iwi_sysctl_stats(SYSCTL_HANDLER_ARGS);
static int iwi_sysctl_radio(SYSCTL_HANDLER_ARGS);
@@ -468,8 +471,6 @@ iwi_detach(device_t dev)
iwi_stop(sc);
- iwi_free_firmware(sc);
-
if (ifp != NULL) {
bpfdetach(ifp);
ieee80211_ifdetach(ic);
@@ -1764,7 +1765,6 @@ iwi_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
{
struct iwi_softc *sc = ifp->if_softc;
struct ieee80211com *ic = &sc->sc_ic;
- struct ifreq *ifr;
int error = 0;
IWI_LOCK(sc);
@@ -1780,25 +1780,6 @@ iwi_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
}
break;
- case SIOCSLOADFW:
- /* only super-user can do that! */
- if ((error = suser(curthread)) != 0)
- break;
-
- ifr = (struct ifreq *)data;
- error = iwi_cache_firmware(sc, ifr->ifr_data);
- break;
-
- case SIOCSKILLFW:
- /* only super-user can do that! */
- if ((error = suser(curthread)) != 0)
- break;
-
- ifp->if_flags &= ~IFF_UP;
- iwi_stop(sc);
- iwi_free_firmware(sc);
- break;
-
default:
error = ieee80211_ioctl(ic, cmd, data);
}
@@ -1882,11 +1863,28 @@ iwi_reset(struct iwi_softc *sc)
}
static int
-iwi_load_ucode(struct iwi_softc *sc, void *uc, int size)
+iwi_load_ucode(struct iwi_softc *sc, const char *name)
{
uint32_t tmp;
uint16_t *w;
- int ntries, i;
+ char *uc;
+ int i, size, ntries, error;
+
+ IWI_UNLOCK(sc);
+ error = iwi_read_firmware(name, &uc, &size);
+ IWI_LOCK(sc);
+ if (error != 0) {
+ device_printf(sc->sc_dev, "could not read ucode image\n");
+ goto fail1;
+ }
+
+ if (size < sizeof (struct iwi_firmware_hdr)) {
+ error = EINVAL;
+ goto fail2;
+ }
+
+ uc += sizeof (struct iwi_firmware_hdr);
+ size -= sizeof (struct iwi_firmware_hdr);
CSR_WRITE_4(sc, IWI_CSR_RST, CSR_READ_4(sc, IWI_CSR_RST) |
IWI_RST_STOP_MASTER);
@@ -1897,7 +1895,8 @@ iwi_load_ucode(struct iwi_softc *sc, void *uc, int size)
}
if (ntries == 5) {
device_printf(sc->sc_dev, "timeout waiting for master\n");
- return EIO;
+ error = EIO;
+ goto fail2;
}
MEM_WRITE_4(sc, 0x3000e0, 0x80000000);
@@ -1919,7 +1918,7 @@ iwi_load_ucode(struct iwi_softc *sc, void *uc, int size)
DELAY(1000);
/* write microcode into adapter memory */
- for (w = uc; size > 0; w++, size -= 2)
+ for (w = (uint16_t *)uc; size > 0; w++, size -= 2)
MEM_WRITE_2(sc, 0x200010, htole16(*w));
MEM_WRITE_1(sc, 0x200000, 0x00);
@@ -1934,7 +1933,8 @@ iwi_load_ucode(struct iwi_softc *sc, void *uc, int size)
if (ntries == 100) {
device_printf(sc->sc_dev,
"timeout waiting for ucode to initialize\n");
- return EIO;
+ error = EIO;
+ goto fail2;
}
/* read the answer or the firmware will not initialize properly */
@@ -1943,22 +1943,42 @@ iwi_load_ucode(struct iwi_softc *sc, void *uc, int size)
MEM_WRITE_1(sc, 0x200000, 0x00);
- return 0;
+fail2: free(uc, M_DEVBUF);
+fail1:
+ return error;
}
/* macro to handle unaligned little endian data in firmware image */
#define GETLE32(p) ((p)[0] | (p)[1] << 8 | (p)[2] << 16 | (p)[3] << 24)
static int
-iwi_load_firmware(struct iwi_softc *sc, void *fw, int size)
+iwi_load_firmware(struct iwi_softc *sc, const char *name)
{
bus_dma_tag_t dmat;
bus_dmamap_t map;
bus_addr_t physaddr;
void *virtaddr;
+ char *fw;
u_char *p, *end;
uint32_t sentinel, ctl, src, dst, sum, len, mlen, tmp;
- int ntries, error = 0;
+ int size, ntries, error;
+
+ /* read firmware image from filesystem */
+ IWI_UNLOCK(sc);
+ error = iwi_read_firmware(name, &fw, &size);
+ IWI_LOCK(sc);
+ if (error != 0) {
+ device_printf(sc->sc_dev, "could not read firmware image\n");
+ goto fail1;
+ }
+
+ if (size < sizeof (struct iwi_firmware_hdr)) {
+ error = EINVAL;
+ goto fail2;
+ }
+
+ fw += sizeof (struct iwi_firmware_hdr);
+ size -= sizeof (struct iwi_firmware_hdr);
/* allocate DMA memory for mapping firmware image */
error = bus_dma_tag_create(NULL, 4, 0, BUS_SPACE_MAXADDR_32BIT,
@@ -1966,21 +1986,21 @@ iwi_load_firmware(struct iwi_softc *sc, void *fw, int size)
if (error != 0) {
device_printf(sc->sc_dev,
"could not create firmware DMA tag\n");
- goto fail1;
+ goto fail2;
}
error = bus_dmamem_alloc(dmat, &virtaddr, BUS_DMA_NOWAIT, &map);
if (error != 0) {
device_printf(sc->sc_dev,
"could not allocate firmware DMA memory\n");
- goto fail2;
+ goto fail3;
}
error = bus_dmamap_load(dmat, map, virtaddr, size, iwi_dma_map_addr,
&physaddr, 0);
if (error != 0) {
device_printf(sc->sc_dev, "could not load firmware DMA map\n");
- goto fail3;
+ goto fail4;
}
/* copy firmware image to DMA memory */
@@ -2036,7 +2056,7 @@ iwi_load_firmware(struct iwi_softc *sc, void *fw, int size)
/* tell the adapter to start processing command blocks */
MEM_WRITE_4(sc, 0x3000a4, 0x540100);
- /* wait until the adapter reach the sentinel */
+ /* wait until the adapter reaches the sentinel */
for (ntries = 0; ntries < 400; ntries++) {
if (MEM_READ_4(sc, 0x3000d0) >= sentinel)
break;
@@ -2046,13 +2066,13 @@ iwi_load_firmware(struct iwi_softc *sc, void *fw, int size)
device_printf(sc->sc_dev,
"timeout processing command blocks\n");
error = EIO;
- goto fail4;
+ goto fail5;
}
/* we're done with command blocks processing */
MEM_WRITE_4(sc, 0x3000a4, 0x540c00);
- /* allow interrupts so we know when the firmware is inited */
+ /* allow interrupts so we know when the firmware is ready */
CSR_WRITE_4(sc, IWI_CSR_INTR_MASK, IWI_INTR_MASK);
/* tell the adapter to initialize the firmware */
@@ -2065,96 +2085,17 @@ iwi_load_firmware(struct iwi_softc *sc, void *fw, int size)
if ((error = msleep(sc, &sc->sc_mtx, 0, "iwiinit", hz)) != 0) {
device_printf(sc->sc_dev, "timeout waiting for firmware "
"initialization to complete\n");
- goto fail4;
}
-fail4: bus_dmamap_sync(dmat, map, BUS_DMASYNC_POSTWRITE);
+fail5: bus_dmamap_sync(dmat, map, BUS_DMASYNC_POSTWRITE);
bus_dmamap_unload(dmat, map);
-fail3: bus_dmamem_free(dmat, virtaddr, map);
-fail2: bus_dma_tag_destroy(dmat);
+fail4: bus_dmamem_free(dmat, virtaddr, map);
+fail3: bus_dma_tag_destroy(dmat);
+fail2: free(fw, M_DEVBUF);
fail1:
return error;
}
-/*
- * Store firmware into kernel memory so we can download it when we need to,
- * e.g when the adapter wakes up from suspend mode.
- */
-static int
-iwi_cache_firmware(struct iwi_softc *sc, void *data)
-{
- struct iwi_firmware *kfw = &sc->fw;
- struct iwi_firmware ufw;
- int error;
-
- iwi_free_firmware(sc);
-
- IWI_UNLOCK(sc);
-
- if ((error = copyin(data, &ufw, sizeof ufw)) != 0)
- goto fail1;
-
- kfw->boot_size = ufw.boot_size;
- kfw->ucode_size = ufw.ucode_size;
- kfw->main_size = ufw.main_size;
-
- kfw->boot = malloc(kfw->boot_size, M_DEVBUF, M_NOWAIT);
- if (kfw->boot == NULL) {
- error = ENOMEM;
- goto fail1;
- }
-
- kfw->ucode = malloc(kfw->ucode_size, M_DEVBUF, M_NOWAIT);
- if (kfw->ucode == NULL) {
- error = ENOMEM;
- goto fail2;
- }
-
- kfw->main = malloc(kfw->main_size, M_DEVBUF, M_NOWAIT);
- if (kfw->main == NULL) {
- error = ENOMEM;
- goto fail3;
- }
-
- if ((error = copyin(ufw.boot, kfw->boot, kfw->boot_size)) != 0)
- goto fail4;
-
- if ((error = copyin(ufw.ucode, kfw->ucode, kfw->ucode_size)) != 0)
- goto fail4;
-
- if ((error = copyin(ufw.main, kfw->main, kfw->main_size)) != 0)
- goto fail4;
-
- DPRINTF(("Firmware cached: boot %u, ucode %u, main %u\n",
- kfw->boot_size, kfw->ucode_size, kfw->main_size));
-
- IWI_LOCK(sc);
-
- sc->flags |= IWI_FLAG_FW_CACHED;
-
- return 0;
-
-fail4: free(kfw->boot, M_DEVBUF);
-fail3: free(kfw->ucode, M_DEVBUF);
-fail2: free(kfw->main, M_DEVBUF);
-fail1: IWI_LOCK(sc);
-
- return error;
-}
-
-static void
-iwi_free_firmware(struct iwi_softc *sc)
-{
- if (!(sc->flags & IWI_FLAG_FW_CACHED))
- return;
-
- free(sc->fw.boot, M_DEVBUF);
- free(sc->fw.ucode, M_DEVBUF);
- free(sc->fw.main, M_DEVBUF);
-
- sc->flags &= ~IWI_FLAG_FW_CACHED;
-}
-
static int
iwi_config(struct iwi_softc *sc)
{
@@ -2477,19 +2418,10 @@ iwi_init(void *priv)
struct iwi_softc *sc = priv;
struct ieee80211com *ic = &sc->sc_ic;
struct ifnet *ifp = ic->ic_ifp;
- struct iwi_firmware *fw = &sc->fw;
struct iwi_rx_data *data;
+ const char *uc, *fw;
int i;
- /* exit immediately if firmware has not been ioctl'd */
- if (!(sc->flags & IWI_FLAG_FW_CACHED)) {
- if (!(sc->flags & IWI_FLAG_FW_WARNED))
- device_printf(sc->sc_dev, "Please load firmware\n");
- sc->flags |= IWI_FLAG_FW_WARNED;
- ifp->if_flags &= ~IFF_UP;
- return;
- }
-
iwi_stop(sc);
if (iwi_reset(sc) != 0) {
@@ -2497,12 +2429,33 @@ iwi_init(void *priv)
goto fail;
}
- if (iwi_load_firmware(sc, fw->boot, fw->boot_size) != 0) {
+ switch (ic->ic_opmode) {
+ case IEEE80211_M_STA:
+ uc = "iwi-ucode-bss";
+ fw = "iwi-bss";
+ break;
+
+ case IEEE80211_M_IBSS:
+ uc = "iwi-ucode-ibss";
+ fw = "iwi-ibss";
+ break;
+
+ case IEEE80211_M_MONITOR:
+ uc = "iwi-ucode-monitor";
+ fw = "iwi-monitor";
+ break;
+
+ default:
+ uc = fw = NULL; /* should not get there */
+ break;
+ }
+
+ if (iwi_load_firmware(sc, "iwi-boot") != 0) {
device_printf(sc->sc_dev, "could not load boot firmware\n");
goto fail;
}
- if (iwi_load_ucode(sc, fw->ucode, fw->ucode_size) != 0) {
+ if (iwi_load_ucode(sc, uc) != 0) {
device_printf(sc->sc_dev, "could not load microcode\n");
goto fail;
}
@@ -2536,7 +2489,7 @@ iwi_init(void *priv)
CSR_WRITE_4(sc, IWI_CSR_RX_WIDX, sc->rxq.count - 1);
- if (iwi_load_firmware(sc, fw->main, fw->main_size) != 0) {
+ if (iwi_load_firmware(sc, fw) != 0) {
device_printf(sc->sc_dev, "could not load main firmware\n");
goto fail;
}
@@ -2589,6 +2542,74 @@ iwi_stop(void *priv)
ieee80211_new_state(ic, IEEE80211_S_INIT, -1);
}
+/*
+ * Read firmware image from the filesystem and load it into kernel memory.
+ * Must be called from a process context.
+ */
+static int
+iwi_read_firmware(const char *name, caddr_t *bufp, size_t *len)
+{
+ char path[64];
+ struct thread *td = curthread;
+ struct nameidata nd;
+ struct vattr va;
+ struct iovec iov;
+ struct uio uio;
+ caddr_t buf;
+ int vfslocked, error;
+
+ snprintf(path, sizeof path, "/etc/firmware/%s.fw", name);
+
+ NDINIT(&nd, LOOKUP, NOFOLLOW | LOCKLEAF | MPSAFE, UIO_SYSSPACE, path,
+ td);
+ if ((error = namei(&nd)) != 0)
+ goto fail1;
+
+ vfslocked = NDHASGIANT(&nd);
+
+ if ((error = VOP_GETATTR(nd.ni_vp, &va, td->td_ucred, td)) != 0)
+ goto fail2;
+
+ /* limit firmware size to 256KB */
+ if (va.va_size > 256 * 1024) {
+ error = E2BIG;
+ goto fail2;
+ }
+
+ if ((buf = malloc(va.va_size, M_DEVBUF, M_NOWAIT)) == NULL) {
+ error = ENOMEM;
+ goto fail2;
+ }
+
+ /* read the whole image at once */
+ uio.uio_iov = &iov;
+ uio.uio_iovcnt = 1;
+ iov.iov_base = buf;
+ iov.iov_len = va.va_size;
+ uio.uio_offset = 0;
+ uio.uio_resid = va.va_size;
+ uio.uio_segflg = UIO_SYSSPACE;
+ uio.uio_rw = UIO_READ;
+ uio.uio_td = td;
+
+ if ((error = VOP_READ(nd.ni_vp, &uio, 0, NOCRED)) != 0)
+ goto fail3;
+
+ *bufp = buf;
+ *len = va.va_size;
+
+ vput(nd.ni_vp);
+ VFS_UNLOCK_GIANT(vfslocked);
+
+ return 0;
+
+fail3: free(buf, M_DEVBUF);
+fail2: vput(nd.ni_vp);
+ VFS_UNLOCK_GIANT(vfslocked);
+fail1:
+ return error;
+}
+
static int
iwi_sysctl_stats(SYSCTL_HANDLER_ARGS)
{
diff --git a/sys/dev/iwi/if_iwireg.h b/sys/dev/iwi/if_iwireg.h
index ae14f2c..8fde2fb 100644
--- a/sys/dev/iwi/if_iwireg.h
+++ b/sys/dev/iwi/if_iwireg.h
@@ -127,6 +127,12 @@
#define IWI_RATE_OFDM48 1
#define IWI_RATE_OFDM54 3
+/* firmware binary image header */
+struct iwi_firmware_hdr {
+ uint32_t version;
+ uint32_t mode;
+} __packed;
+
struct iwi_hdr {
uint8_t type;
#define IWI_HDR_TYPE_DATA 0
diff --git a/sys/dev/iwi/if_iwivar.h b/sys/dev/iwi/if_iwivar.h
index 82d5e93..604aa64 100644
--- a/sys/dev/iwi/if_iwivar.h
+++ b/sys/dev/iwi/if_iwivar.h
@@ -27,15 +27,6 @@
* SUCH DAMAGE.
*/
-struct iwi_firmware {
- void *boot;
- int boot_size;
- void *ucode;
- int ucode_size;
- void *main;
- int main_size;
-};
-
struct iwi_rx_radiotap_header {
struct ieee80211_radiotap_header wr_ihdr;
uint8_t wr_flags;
@@ -127,12 +118,9 @@ struct iwi_softc {
struct mtx sc_mtx;
struct unrhdr *sc_unr;
- struct iwi_firmware fw;
uint32_t flags;
-#define IWI_FLAG_FW_CACHED (1 << 0)
-#define IWI_FLAG_FW_INITED (1 << 1)
-#define IWI_FLAG_FW_WARNED (1 << 2)
-#define IWI_FLAG_SCANNING (1 << 3)
+#define IWI_FLAG_FW_INITED (1 << 0)
+#define IWI_FLAG_SCANNING (1 << 1)
struct iwi_cmd_ring cmdq;
struct iwi_tx_ring txq[WME_NUM_AC];
@@ -169,8 +157,5 @@ struct iwi_softc {
int sc_txtap_len;
};
-#define SIOCSLOADFW _IOW('i', 137, struct ifreq)
-#define SIOCSKILLFW _IOW('i', 138, struct ifreq)
-
#define IWI_LOCK(sc) mtx_lock(&(sc)->sc_mtx)
#define IWI_UNLOCK(sc) mtx_unlock(&(sc)->sc_mtx)
OpenPOWER on IntegriCloud