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
This commit is contained in:
parent
d75b2048db
commit
408146ed57
Notes:
svn2git
2020-12-20 02:59:44 +00:00
svn path=/head/; revision=152611
@ -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)
|
||||
{
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -3,6 +3,6 @@
|
||||
.PATH: ${.CURDIR}/../../dev/iwi
|
||||
|
||||
KMOD = if_iwi
|
||||
SRCS = if_iwi.c device_if.h bus_if.h pci_if.h
|
||||
SRCS = if_iwi.c device_if.h bus_if.h pci_if.h vnode_if.h
|
||||
|
||||
.include <bsd.kmod.mk>
|
||||
|
Loading…
Reference in New Issue
Block a user